Ejemplo n.º 1
0
    def test_sequence_concurency(self):
        """Computing the same name in concurent transactions is not allowed."""
        with self.env.registry.cursor() as cr0,\
                self.env.registry.cursor() as cr1,\
                self.env.registry.cursor() as cr2:
            env0 = api.Environment(cr0, SUPERUSER_ID, {})
            env1 = api.Environment(cr1, SUPERUSER_ID, {})
            env2 = api.Environment(cr2, SUPERUSER_ID, {})

            journal = env0['account.journal'].create({
                'name': 'concurency_test',
                'code': 'CT',
                'type': 'general',
            })
            account = env0['account.account'].create({
                'code':
                'CT',
                'name':
                'CT',
                'user_type_id':
                env0.ref('account.data_account_type_fixed_assets').id,
            })
            moves = env0['account.move'].create([{
                'journal_id':
                journal.id,
                'date':
                fields.Date.from_string('2016-01-01'),
                'line_ids': [(0, 0, {
                    'name': 'name',
                    'account_id': account.id
                })]
            }] * 3)
            moves.name = '/'
            moves[0].action_post()
            self.assertEqual(moves.mapped('name'),
                             ['CT/2016/01/0001', '/', '/'])
            env0.cr.commit()

            # start the transactions here on cr2 to simulate concurency with cr1
            env2.cr.execute('SELECT 1')

            move = env1['account.move'].browse(moves[1].id)
            move.action_post()
            env1.cr.commit()

            move = env2['account.move'].browse(moves[2].id)
            with self.assertRaises(
                    psycopg2.OperationalError), env2.cr.savepoint(
                    ), mute_logger('flectra.sql_db'):
                move.action_post()

            self.assertEqual(moves.mapped('name'),
                             ['CT/2016/01/0001', 'CT/2016/01/0002', '/'])
            moves.button_draft()
            moves.posted_before = False
            moves.unlink()
            journal.unlink()
            account.unlink()
            env0.cr.commit()
Ejemplo n.º 2
0
    def poll(self, dbname, channels, last, options=None, timeout=TIMEOUT):
        if options is None:
            options = {}
        # Dont hang ctrl-c for a poll request, we need to bypass private
        # attribute access because we dont know before starting the thread that
        # it will handle a longpolling request
        if not flectra.evented:
            current = threading.current_thread()
            current._daemonic = True
            # rename the thread to avoid tests waiting for a longpolling
            current.setName("openerp.longpolling.request.%s" % current.ident)

        registry = flectra.registry(dbname)

        # immediatly returns if past notifications exist
        with registry.cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, {})
            notifications = env['bus.bus'].poll(channels, last, options)

        # immediatly returns in peek mode
        if options.get('peek'):
            return dict(notifications=notifications, channels=channels)

        # or wait for future ones
        if not notifications:
            if not self.started:
                # Lazy start of events listener
                self.start()

            event = self.Event()
            for channel in channels:
                self.channels.setdefault(hashable(channel), set()).add(event)
            try:
                event.wait(timeout=timeout)
                with registry.cursor() as cr:
                    env = api.Environment(cr, SUPERUSER_ID, {})
                    notifications = env['bus.bus'].poll(
                        channels, last, options)
            except Exception:
                # timeout
                pass
            finally:
                # gc pointers to event
                for channel in channels:
                    channel_events = self.channels.get(hashable(channel))
                    if channel_events and event in channel_events:
                        channel_events.remove(event)
        return notifications
Ejemplo n.º 3
0
    def test_02_demo_address_and_company(self):
        ''' This test ensure that the company_id of the address (partner) is
            correctly set and also, is not wrongly changed.
            eg: new shipping should use the company of the website and not the
                one from the admin, and editing a billing should not change its
                company.
        '''
        self._setUp_multicompany_env()
        so = self._create_so(self.demo_partner.id)

        env = api.Environment(self.env.cr, self.demo_user.id, {})
        # change also website env for `sale_get_order` to not change order partner_id
        with MockRequest(env,
                         website=self.website.with_env(env),
                         sale_order_id=so.id):
            # 1. Logged in user, new shipping
            self.WebsiteSaleController.address(**self.default_address_values)
            new_shipping = self._get_last_address(self.demo_partner)
            self.assertTrue(
                new_shipping.company_id != self.env.user.company_id,
                "Logged in user new shipping should not get the company of the sudo() neither the one from it's partner.."
            )
            self.assertEqual(new_shipping.company_id, self.website.company_id,
                             ".. but the one from the website.")

            # 2. Logged in user, edit billing
            self.default_address_values['partner_id'] = self.demo_partner.id
            self.WebsiteSaleController.address(**self.default_address_values)
            self.assertEqual(
                self.demo_partner.company_id, self.company_c,
                "Logged in user edited billing (the partner itself) should not get its company modified."
            )
Ejemplo n.º 4
0
    def _handle_exception(cls, exception):
        is_frontend_request = bool(getattr(request, 'is_frontend', False))
        if not is_frontend_request:
            # Don't touch non frontend requests exception handling
            return super(IrHttp, cls)._handle_exception(exception)
        try:
            response = super(IrHttp, cls)._handle_exception(exception)

            if isinstance(response, Exception):
                exception = response
            else:
                # if parent excplicitely returns a plain response, then we don't touch it
                return response
        except Exception as e:
            if 'werkzeug' in config['dev_mode']:
                raise e
            exception = e

        code, values = cls._get_exception_code_values(exception)

        if code is None:
            # Hand-crafted HTTPException likely coming from abort(),
            # usually for a redirect response -> return it directly
            return exception

        if not request.uid:
            cls._auth_method_public()

        # We rollback the current transaction before initializing a new
        # cursor to avoid potential deadlocks.

        # If the current (failed) transaction was holding a lock, the new
        # cursor might have to wait for this lock to be released further
        # down the line. However, this will only happen after the
        # request is done (and in fact it won't happen). As a result, the
        # current thread/worker is frozen until its timeout is reached.

        # So rolling back the transaction will release any potential lock
        # and, since we are in a case where an exception was raised, the
        # transaction shouldn't be committed in the first place.
        request.env.cr.rollback()

        with registry(request.env.cr.dbname).cursor() as cr:
            env = api.Environment(cr, request.uid, request.env.context)
            if code == 500:
                _logger.error("500 Internal Server Error:\n\n%s",
                              values['traceback'])
                values = cls._get_values_500_error(env, values, exception)
            elif code == 403:
                _logger.warning("403 Forbidden:\n\n%s", values['traceback'])
            elif code == 400:
                _logger.warning("400 Bad Request:\n\n%s", values['traceback'])
            try:
                code, html = cls._get_error_html(env, code, values)
            except Exception:
                code, html = 418, env['ir.ui.view']._render_template(
                    'http_routing.http_error', values)

        return werkzeug.wrappers.Response(
            html, status=code, content_type='text/html;charset=utf-8')
Ejemplo n.º 5
0
    def _serve_attachment(cls):
        env = api.Environment(request.cr, SUPERUSER_ID, request.context)
        domain = [('type', '=', 'binary'), ('url', '=', request.httprequest.path)]
        fields = ['__last_update', 'datas', 'name', 'mimetype', 'checksum']
        attach = env['ir.attachment'].search_read(domain, fields)
        if attach:
            wdate = attach[0]['__last_update']
            datas = attach[0]['datas'] or b''
            name = attach[0]['name']
            checksum = attach[0]['checksum'] or hashlib.sha1(datas).hexdigest()

            if (not datas and name != request.httprequest.path and
                    name.startswith(('http://', 'https://', '/'))):
                return werkzeug.utils.redirect(name, 301)

            response = werkzeug.wrappers.Response()
            server_format = tools.DEFAULT_SERVER_DATETIME_FORMAT
            try:
                response.last_modified = datetime.datetime.strptime(wdate, server_format + '.%f')
            except ValueError:
                # just in case we have a timestamp without microseconds
                response.last_modified = datetime.datetime.strptime(wdate, server_format)

            response.set_etag(checksum)
            response.make_conditional(request.httprequest)

            if response.status_code == 304:
                return response

            response.mimetype = attach[0]['mimetype'] or 'application/octet-stream'
            response.data = base64.b64decode(datas)
            return response
Ejemplo n.º 6
0
def _auto_install_enterprise_dependencies(cr, registry):
    env = api.Environment(cr, SUPERUSER_ID, {})
    module_list = ['account_accountant']
    module_ids = env['ir.module.module'].search([('name', 'in', module_list),
                                                 ('state', '=', 'uninstalled')
                                                 ])
    module_ids.sudo().button_install()
Ejemplo n.º 7
0
    def _button_immediate_function(self, function):
        try:
            # This is done because the installation/uninstallation/upgrade can modify a currently
            # running cron job and prevent it from finishing, and since the ir_cron table is locked
            # during execution, the lock won't be released until timeout.
            self._cr.execute("SELECT * FROM ir_cron FOR UPDATE NOWAIT")
        except psycopg2.OperationalError:
            raise UserError(
                _("The server is busy right now, module operations are not possible at"
                  " this time, please try again later."))
        function(self)

        self._cr.commit()
        api.Environment.reset()
        modules.registry.Registry.new(self._cr.dbname, update_module=True)

        self._cr.commit()
        env = api.Environment(self._cr, self._uid, self._context)
        # pylint: disable=next-method-called
        config = env['ir.module.module'].next() or {}
        if config.get('type') not in ('ir.actions.act_window_close', ):
            return config

        # reload the client; open the first available root menu
        menu = env['ir.ui.menu'].search([('parent_id', '=', False)])[:1]
        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
            'params': {
                'menu_id': menu.id
            },
        }
Ejemplo n.º 8
0
 def setUp(self):
     super(TestAPI, self).setUp()
     self.db_name = get_db_name()
     self.phantom_env = api.Environment(self.registry.test_cr, self.uid, {})
     self.demo_user = self.phantom_env.ref(USER_DEMO)
     self.admin_user = self.phantom_env.ref(USER_ADMIN)
     self.model_name = "res.partner"
Ejemplo n.º 9
0
    def test_03_public_user_address_and_company(self):
        ''' Same as test_02 but with public user '''
        self._setUp_multicompany_env()
        so = self._create_so(self.website.user_id.partner_id.id)

        env = api.Environment(self.env.cr, self.website.user_id.id, {})
        # change also website env for `sale_get_order` to not change order partner_id
        with MockRequest(env,
                         website=self.website.with_env(env),
                         sale_order_id=so.id):
            # 1. Public user, new billing
            self.default_address_values['partner_id'] = -1
            self.WebsiteSaleController.address(**self.default_address_values)
            new_partner = so.partner_id
            self.assertNotEqual(
                new_partner, self.website.user_id.partner_id,
                "New billing should have created a new partner and assign it on the SO"
            )
            self.assertEqual(
                new_partner.company_id, self.website.company_id,
                "The new partner should get the company of the website")

            # 2. Public user, edit billing
            self.default_address_values['partner_id'] = new_partner.id
            self.WebsiteSaleController.address(**self.default_address_values)
            self.assertEqual(
                new_partner.company_id, self.website.company_id,
                "Public user edited billing (the partner itself) should not get its company modified."
            )
Ejemplo n.º 10
0
    def log_xml(self, xml_string, func):
        self.ensure_one()

        if self.debug_logging:
            self.flush()
            db_name = self._cr.dbname

            # Use a new cursor to avoid rollback that could be caused by an upper method
            try:
                db_registry = registry(db_name)
                with db_registry.cursor() as cr:
                    env = api.Environment(cr, SUPERUSER_ID, {})
                    IrLogging = env['ir.logging']
                    IrLogging.sudo().create({
                        'name': 'delivery.carrier',
                        'type': 'server',
                        'dbname': db_name,
                        'level': 'DEBUG',
                        'message': xml_string,
                        'path': self.delivery_type,
                        'func': func,
                        'line': 1
                    })
            except psycopg2.Error:
                pass
Ejemplo n.º 11
0
    def oea(self, **kw):
        """login user via Flectra Account provider"""
        dbname = kw.pop('db', None)
        if not dbname:
            dbname = db_monodb()
        if not dbname:
            return BadRequest()
        if not http.db_filter([dbname]):
            return BadRequest()

        registry = registry_get(dbname)
        with registry.cursor() as cr:
            try:
                env = api.Environment(cr, SUPERUSER_ID, {})
                provider = env.ref('auth_oauth.provider_openerp')
            except ValueError:
                return set_cookie_and_redirect('/web?db=%s' % dbname)
            assert provider._name == 'auth.oauth.provider'

        state = {
            'd': dbname,
            'p': provider.id,
            'c': {
                'no_user_creation': True
            },
        }

        kw['state'] = json.dumps(state)
        return self.signin(**kw)
Ejemplo n.º 12
0
    def _serve_attachment(cls):
        env = api.Environment(request.cr, SUPERUSER_ID, request.context)
        attach = env['ir.attachment'].get_serve_attachment(
            request.httprequest.path, extra_fields=['name', 'checksum'])
        if attach:
            wdate = attach[0]['__last_update']
            datas = attach[0]['datas'] or b''
            name = attach[0]['name']
            checksum = attach[0]['checksum'] or hashlib.sha512(
                datas).hexdigest()[:64]  # sha512/256

            if (not datas and name != request.httprequest.path
                    and name.startswith(('http://', 'https://', '/'))):
                return werkzeug.utils.redirect(name, 301)

            response = werkzeug.wrappers.Response()
            response.last_modified = wdate

            response.set_etag(checksum)
            response.make_conditional(request.httprequest)

            if response.status_code == 304:
                return response

            response.mimetype = attach[0][
                'mimetype'] or 'application/octet-stream'
            response.data = base64.b64decode(datas)
            return response
Ejemplo n.º 13
0
    def check_tables_exist(self, cr):
        """
        Verify that all tables are present and try to initialize
        those that are missing.
        """
        # This monkey patch is meant to avoid that the _logger writes
        # warning and error messages, while running an update all,
        # in case the model is a bi-view-generated model.

        env = api.Environment(cr, SUPERUSER_ID, {})
        table2model = {
            model._table: name
            for name, model in env.items()
            if not model._abstract and not _bi_view(name)  # here is the patch
        }
        missing_tables = set(table2model).difference(
            existing_tables(cr, table2model))

        if missing_tables:
            missing = {table2model[table] for table in missing_tables}
            _logger.warning("Models have no table: %s.", ", ".join(missing))
            # recreate missing tables following model dependencies
            deps = {name: model._depends for name, model in env.items()}
            for name in topological_sort(deps):
                if name in missing:
                    _logger.info("Recreate table of model %s.", name)
                    env[name].init()
            # check again, and log errors if tables are still missing
            missing_tables = set(table2model).difference(
                existing_tables(cr, table2model))
            for table in missing_tables:
                _logger.error("Model %s has no table.", table2model[table])
Ejemplo n.º 14
0
def _set_default_identification_type(cr, registry):
    env = api.Environment(cr, SUPERUSER_ID, {})
    env.cr.execute(
        """
            UPDATE res_partner
               SET l10n_latam_identification_type_id = %s
        """, [env.ref('l10n_latam_base.it_vat').id])
Ejemplo n.º 15
0
 def authenticate(cls, db, login, password, user_agent_env):
     """Verifies and returns the user ID corresponding to the given
       ``login`` and ``password`` combination, or False if there was
       no matching user.
        :param str db: the database on which user is trying to authenticate
        :param str login: username
        :param str password: user password
        :param dict user_agent_env: environment dictionary describing any
            relevant environment attributes
     """
     uid = cls._login(db, login, password)
     if uid == SUPERUSER_ID:
         # Successfully logged in as admin!
         # Attempt to guess the web base url...
         if user_agent_env and user_agent_env.get('base_location'):
             try:
                 with cls.pool.cursor() as cr:
                     base = user_agent_env['base_location']
                     ICP = api.Environment(cr, uid,
                                           {})['ir.config_parameter']
                     if not ICP.get_param('web.base.url.freeze'):
                         ICP.set_param('web.base.url', base)
             except Exception:
                 _logger.exception(
                     "Failed to update web.base.url configuration parameter"
                 )
     return uid
Ejemplo n.º 16
0
    def _dispatch(cls):
        """
        In case of rerouting for translate (e.g. when visiting flectrahq.com/fr_BE/),
        _dispatch calls reroute() that returns _dispatch with altered request properties.
        The second _dispatch will continue until end of process. When second _dispatch is finished, the first _dispatch
        call receive the new altered request and continue.
        At the end, 2 calls of _dispatch (and this override) are made with exact same request properties, instead of one.
        As the response has not been sent back to the client, the visitor cookie does not exist yet when second _dispatch call
        is treated in _handle_webpage_dispatch, leading to create 2 visitors with exact same properties.
        To avoid this, we check if, !!! before calling super !!!, we are in a rerouting request. If not, it means that we are
        handling the original request, in which we should create the visitor. We ignore every other rerouting requests.
        """
        is_rerouting = hasattr(request, 'routing_iteration')

        if request.session.db:
            reg = registry(request.session.db)
            with reg.cursor() as cr:
                env = api.Environment(cr, SUPERUSER_ID, {})
                request.website_routing = env['website'].get_current_website(
                ).id

        response = super(Http, cls)._dispatch()

        if not is_rerouting:
            cls._register_website_track(response)
        return response
Ejemplo n.º 17
0
def _l10n_es_edi_post_init(cr, registry):
    env = api.Environment(cr, SUPERUSER_ID, {})
    companies = env['res.company'].search([('partner_id.country_id.code', '=',
                                            'ES')])

    all_chart_templates = companies.chart_template_id
    current_chart_template = all_chart_templates
    while current_chart_template.parent_id:
        all_chart_templates |= current_chart_template.parent_id
        current_chart_template = current_chart_template.parent_id

    if all_chart_templates:
        tax_templates = env['account.tax.template'].search([
            ('chart_template_id', 'in', all_chart_templates.ids),
            '|',
            '|',
            ('l10n_es_type', '!=', False),
            ('l10n_es_exempt_reason', '!=', False),
            ('tax_scope', '!=', False),
        ])
        xml_ids = tax_templates.get_external_id()
        for company in companies:
            for tax_template in tax_templates:
                module, xml_id = xml_ids.get(tax_template.id).split('.')
                tax = env.ref('%s.%s_%s' % (module, company.id, xml_id),
                              raise_if_not_found=False)
                if tax:
                    tax.write({
                        'l10n_es_exempt_reason':
                        tax_template.l10n_es_exempt_reason,
                        'tax_scope': tax_template.tax_scope,
                        'l10n_es_type': tax_template.l10n_es_type,
                    })
Ejemplo n.º 18
0
def _configure_journals(cr, registry):
    """Setting journal and property field (if needed)"""

    env = api.Environment(cr, SUPERUSER_ID, {})

    # if we already have a coa installed, create journal and set property field
    company_ids = env['res.company'].search([('chart_template_id', '!=', False)
                                             ])

    for company_id in company_ids:
        # Check if property exists for stock account journal exists
        field = env['ir.model.fields']._get("product.category",
                                            "property_stock_journal")
        properties = env['ir.property'].sudo().search([
            ('fields_id', '=', field.id), ('company_id', '=', company_id.id)
        ])

        # If not, check if you can find a journal that is already there with the same name, otherwise create one
        if not properties:
            journal_id = env['account.journal'].search(
                [('name', '=', _('Inventory Valuation')),
                 ('company_id', '=', company_id.id), ('type', '=', 'general')],
                limit=1).id
            if not journal_id:
                journal_id = env['account.journal'].create({
                    'name':
                    _('Inventory Valuation'),
                    'type':
                    'general',
                    'code':
                    'STJ',
                    'company_id':
                    company_id.id,
                    'show_on_dashboard':
                    False
                }).id
            env['ir.property']._set_default(
                'property_stock_journal',
                'product.category',
                journal_id,
                company_id,
            )

        # Property Stock Accounts
        todo_list = [
            'property_stock_account_input_categ_id',
            'property_stock_account_output_categ_id',
            'property_stock_valuation_account_id',
        ]

        for name in todo_list:
            account = getattr(company_id, name)
            if account:
                env['ir.property']._set_default(
                    name,
                    'product.category',
                    account,
                    company_id,
                )
Ejemplo n.º 19
0
 def update_dashboard_graph_model(dbname):
     db_registry = flectra.modules.registry.Registry.new(dbname)
     with api.Environment.manage(), db_registry.cursor() as cr:
         env = api.Environment(cr, SUPERUSER_ID, {})
         if 'crm.team' in env:
             recs = env['crm.team'].search([])
             for rec in recs:
                 rec._onchange_team_type()
Ejemplo n.º 20
0
def uninstall_hook(cr, registry):
    env = api.Environment(cr, SUPERUSER_ID, {})
    env['product.template'].search([
        ('service_type', '=', 'timesheet')
    ]).write({'service_type': 'manual'})
    env['product.product'].search([
        ('service_type', '=', 'timesheet')
    ]).write({'service_type': 'manual'})
Ejemplo n.º 21
0
def set_last_price_info(cr, registry):
    """
    This post-init-hook will update last price information for all products
    """
    env = api.Environment(cr, SUPERUSER_ID, dict())
    product_obj = env['product.product']
    products = product_obj.search([('purchase_ok', '=', True)])
    products.set_product_last_purchase()
Ejemplo n.º 22
0
def environment():
    """ Return an environment with a new cursor for the current database; the
        cursor is committed and closed after the context block.
    """
    reg = registry(common.get_db_name())
    with reg.cursor() as cr:
        yield api.Environment(cr, SUPERUSER_ID, {})
        cr.commit()
Ejemplo n.º 23
0
 def rem_website_id_null(dbname):
     db_registry = flectra.modules.registry.Registry.new(dbname)
     with api.Environment.manage(), db_registry.cursor() as cr:
         env = api.Environment(cr, SUPERUSER_ID, {})
         env['ir.model.fields'].search([
             ('name', '=', 'website_id'),
             ('model', '=', 'res.config.settings'),
         ]).unlink()
Ejemplo n.º 24
0
def _disable_pec_mail_post_init(cr, registry):
    ''' Pec mail cannot be used in conjunction with SdiCoop, so disable the Pec fetchmail servers.
    '''
    env = api.Environment(cr, SUPERUSER_ID, {})

    env['fetchmail.server'].search([('l10n_it_is_pec', '=', True)
                                    ]).l10n_it_is_pec = False
    env['res.company'].search([('l10n_it_mail_pec_server_id', '!=', None)
                               ]).l10n_it_mail_pec_server_id = None
Ejemplo n.º 25
0
def post_init_hook(cr, registry):
    """Loaded after installing the module."""
    with api.Environment.manage():
        env = api.Environment(cr, SUPERUSER_ID, {})
        purchase_requests = env['purchase.request'].search([])
        _logger.info("Adding the department to %d purchase requests",
                     len(purchase_requests))
        for purchase_request in purchase_requests:
            purchase_request.onchange_requested_by()
Ejemplo n.º 26
0
def uninstall_hook(cr, registry):
    env = api.Environment(cr, SUPERUSER_ID, {})

    code_taxes = env['account.tax'].search([('amount_type', '=', 'code')])
    code_taxes.write({'amount_type': 'percent', 'active': False})

    _logger.warning(
        "The following taxes have been archived following 'account_tax_python' module uninstallation: %s"
        % code_taxes.ids)
Ejemplo n.º 27
0
def account_edi_block_level(cr, registery):
    ''' The default value for blocking_level is 'error', but without this module,
    the behavior is the same as a blocking_level of 'warning' so we need to set
    all documents in error.
    '''
    from flectra import api, SUPERUSER_ID

    env = api.Environment(cr, SUPERUSER_ID, {})
    env['account.edi.document'].search([('error', '!=', False)]).write({'blocking_level': 'warning'})
Ejemplo n.º 28
0
def _create_buy_rules(cr, registry):
    """ This hook is used to add a default buy_pull_id on every warehouse. It is
    necessary if the purchase_stock module is installed after some warehouses
    were already created.
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    warehouse_ids = env['stock.warehouse'].search([('buy_pull_id', '=', False)
                                                   ])
    warehouse_ids.write({'buy_to_resupply': True})
Ejemplo n.º 29
0
def _create_warehouse_data(cr, registry):
    """ This hook is used to add a default manufacture_pull_id, manufacture
    picking_type on every warehouse. It is necessary if the mrp module is
    installed after some warehouses were already created.
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    warehouse_ids = env['stock.warehouse'].search([('manufacture_pull_id', '=',
                                                    False)])
    warehouse_ids.write({'manufacture_to_resupply': True})
Ejemplo n.º 30
0
 def called_after():
     db_registry = registry(dbname)
     with api.Environment.manage(), db_registry.cursor() as cr:
         env = api.Environment(cr, uid, context)
         try:
             func(self.with_env(env), *args, **kwargs)
         except Exception as e:
             _logger.warning("Could not sync record now: %s" % self)
             _logger.exception(e)