Exemple #1
0
    def __init__(self, domain, model, alias=None, query=None):
        """ Initialize expression object and automatically parse the expression
            right after initialization.

            :param domain: expression (using domain ('foo', '=', 'bar') format)
            :param model: root model
            :param alias: alias for the model table if query is provided
            :param query: optional query object holding the final result

            :attr root_model: base model for the query
            :attr expression: the domain to parse, normalized and prepared
            :attr result: the result of the parsing, as a pair (query, params)
            :attr query: Query object holding the final result
        """
        self._unaccent = get_unaccent_wrapper(model._cr)
        self.root_model = model
        self.root_alias = alias or model._table

        # normalize and prepare the expression for parsing
        self.expression = distribute_not(normalize_domain(domain))

        # this object handles all the joins
        self.query = Query(model.env.cr, model._table, model._table_query) if query is None else query

        # parse the domain expression
        self.parse()
Exemple #2
0
    def _compute_application_count(self):
        self.flush(fnames=['email_from'])
        # Filter and gather emails at the same time
        applicants = self.env['hr.applicant']
        mails = set()
        for applicant in self:
            if applicant.email_from:
                applicants |= applicant
                mails.add(applicant.email_from.lower())
        # Done via SQL since read_group does not support grouping by lowercase field
        if mails:
            query = Query(self.env.cr, self._table, self._table_query)
            query.add_where('LOWER("hr_applicant".email_from) in %s', [tuple(mails)])
            self._apply_ir_rules(query)
            from_clause, where_clause, where_clause_params = query.get_sql()
            query_str = """
            SELECT LOWER("%(table)s".email_from) as l_email_from,
                COUNT("%(table)s".id) as count
            FROM %(from)s
            %(where)s
        GROUP BY l_email_from
            """ % {
                'table': self._table,
                'from': from_clause,
                'where': ('WHERE %s' % where_clause) if where_clause else '',
            }
            self.env.cr.execute(query_str, where_clause_params)

            application_data_mapped = dict((data['l_email_from'], data['count']) for data in self.env.cr.dictfetchall())
        else:
            application_data_mapped = dict()
        for applicant in applicants:
            applicant.application_count = application_data_mapped.get(applicant.email_from.lower(), 1)
        (self - applicants).application_count = False
Exemple #3
0
    def _get_sale_order_items_query(self, domain_per_model=None):
        if domain_per_model is None:
            domain_per_model = {}
        project_domain = [('id', 'in', self.ids),
                          ('sale_line_id', '!=', False)]
        if 'project.project' in domain_per_model:
            project_domain = expression.AND(
                [project_domain, domain_per_model['project.project']])
        project_query = self.env['project.project']._where_calc(project_domain)
        self._apply_ir_rules(project_query, 'read')
        project_query_str, project_params = project_query.select(
            'id', 'sale_line_id')

        Task = self.env['project.task']
        task_domain = [('project_id', 'in', self.ids),
                       ('sale_line_id', '!=', False)]
        if Task._name in domain_per_model:
            task_domain = expression.AND(
                [task_domain, domain_per_model[Task._name]])
        task_query = Task._where_calc(task_domain)
        Task._apply_ir_rules(task_query, 'read')
        task_query_str, task_params = task_query.select(
            f'{Task._table}.project_id AS id', f'{Task._table}.sale_line_id')

        query = Query(self._cr, 'project_sale_order_item',
                      ' UNION '.join([project_query_str, task_query_str]))
        query._where_params = project_params + task_params
        return query
Exemple #4
0
    def test_mixed_query_chained_explicit_implicit_joins(self):
        query = Query(None, 'product_product')
        query.add_table('product_template')
        query.add_where("product_product.template_id = product_template.id")
        # add inner join
        alias = query.join("product_template", "categ_id", "product_category",
                           "id", "categ_id")
        self.assertEqual(alias, 'product_template__categ_id')
        # add CHAINED left join
        alias = query.left_join("product_template__categ_id", "user_id",
                                "res_user", "id", "user_id")
        self.assertEqual(alias, 'product_template__categ_id__user_id')
        # additional implicit join
        query.add_table('account.account')
        query.add_where(
            "product_category.expense_account_id = account_account.id")

        from_clause, where_clause, where_params = query.get_sql()
        self.assertEqual(
            from_clause,
            '"product_product", "product_template", "account.account" JOIN "product_category" AS "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" AS "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")'
        )
        self.assertEqual(
            where_clause,
            "product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id"
        )
Exemple #5
0
 def test_long_aliases(self):
     query = Query(None, 'product_product')
     tmp = query.join('product_product', 'product_tmpl_id',
                      'product_template', 'id', 'product_tmpl_id')
     self.assertEqual(tmp, 'product_product__product_tmpl_id')
     # no hashing
     tmp_cat = query.join(tmp, 'product_category_id', 'product_category',
                          'id', 'product_category_id')
     self.assertEqual(
         tmp_cat, 'product_product__product_tmpl_id__product_category_id')
     # hashing to limit identifier length
     tmp_cat_cmp = query.join(tmp_cat, 'company_id', 'res_company', 'id',
                              'company_id')
     self.assertEqual(
         tmp_cat_cmp,
         'product_product__product_tmpl_id__product_category_id__9f0ddff7')
     tmp_cat_stm = query.join(tmp_cat, 'salesteam_id', 'res_company', 'id',
                              'salesteam_id')
     self.assertEqual(
         tmp_cat_stm,
         'product_product__product_tmpl_id__product_category_id__953a466f')
     # extend hashed identifiers
     tmp_cat_cmp_par = query.join(tmp_cat_cmp, 'partner_id', 'res_partner',
                                  'id', 'partner_id')
     self.assertEqual(
         tmp_cat_cmp_par,
         'product_product__product_tmpl_id__product_category_id__56d55687')
     tmp_cat_stm_par = query.join(tmp_cat_stm, 'partner_id', 'res_partner',
                                  'id', 'partner_id')
     self.assertEqual(
         tmp_cat_stm_par,
         'product_product__product_tmpl_id__product_category_id__9_363fdd')
Exemple #6
0
 def test_mixed_query_chained_explicit_implicit_joins(self):
     query = Query()
     query.tables.extend(['"product_product"', '"product_template"'])
     query.where_clause.append(
         "product_product.template_id = product_template.id")
     query.add_join(("product_template", "product_category", "categ_id",
                     "id", "categ_id"),
                    implicit=False,
                    outer=False)  # add normal join
     query.add_join(("product_template__categ_id", "res_user", "user_id",
                     "id", "user_id"),
                    implicit=False,
                    outer=True)  # CHAINED outer join
     query.tables.append('"account.account"')
     query.where_clause.append(
         "product_category.expense_account_id = account_account.id"
     )  # additional implicit join
     self.assertEquals(
         query.get_sql()[0].strip(),
         """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id"),"account.account" """
         .strip())
     self.assertEquals(
         query.get_sql()[1].strip(),
         """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id"""
         .strip())
Exemple #7
0
    def _search_is_authorized(self, operator, value):
        if operator not in ('=', '!=', '<>'):
            raise ValueError('Invalid operator: %s' % (operator, ))

        SS = self.env['sale.subscription']
        tbls = (self._table, SS._table)
        query = Query(tbls,
                      ["%s.subscription_template_id = %s.template_id" % tbls],
                      [])
        SS._apply_ir_rules(query)

        from_clause, where_clause, where_clause_params = query.get_sql()

        self.env.cr.execute(
            """
            SELECT {self}.id
              FROM {from_}
             WHERE {where}
        """.format(self=self._table, from_=from_clause, where=where_clause),
            where_clause_params)
        ids = [i[0] for i in self.env.cr.fetchall()]

        op = 'in' if (operator == '=' and value) or (
            operator != '=' and not value) else 'not in'
        return [('id', op, ids)]
Exemple #8
0
 def test_basic_query(self):
     query = Query()
     query.tables.extend(['"product_product"', '"product_template"'])
     query.where_clause.append("product_product.template_id = product_template.id")
     query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False)  # add normal join
     query.add_join(("product_product", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True)  # outer join
     self.assertEquals(query.get_sql()[0].strip(),
         """"product_product" LEFT JOIN "res_user" as "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id"),"product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") """.strip())
     self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
Exemple #9
0
 def test_raise_missing_lhs(self):
     query = Query()
     query.tables.append('"product_product"')
     self.assertRaises(AssertionError,
                       query.add_join,
                       ("product_template", "product_category", "categ_id",
                        "id", "categ_id"),
                       implicit=False,
                       outer=False)
Exemple #10
0
    def _inherits_join_calc(self, alias, fname, query):
        """
        Adds missing table select and join clause(s) to ``query`` for reaching
        the field coming from an '_inherits' parent table (no duplicates).

        :param alias: name of the initial SQL alias
        :param fname: name of inherited field to reach
        :param query: query object on which the JOIN should be added
        :return: qualified name of field, to be used in SELECT clause
        """
        # INVARIANT: alias is the SQL alias of model._table in query
        model, field = self, self._fields[fname]
        if field.type == "many2many":
            while field.inherited:
                # retrieve the parent model where field is inherited from
                parent_model = self.env[field.related_field.model_name]
                parent_fname = field.related[0]
                # JOIN parent_model._table AS parent_alias
                # ON alias.parent_fname = parent_alias.id
                parent_alias = query.left_join(alias, parent_fname,
                                               parent_model._table, "id",
                                               parent_fname)
                model, alias, field = parent_model, parent_alias, field.related_field
            # special case for many2many fields: prepare a query on the comodel
            # in order to reuse the mechanism _apply_ir_rules, then inject the
            # query as an extra condition of the left join
            comodel = self.env[field.comodel_name]
            subquery = Query(self.env.cr, comodel._table)
            comodel._apply_ir_rules(subquery)
            # add the extra join condition only if there is an actual subquery
            extra, extra_params = None, ()
            if subquery.where_clause:

                subquery_str, extra_params = subquery.select()
                extra = '"{{rhs}}"."{}" IN ({})'.format(
                    field.column2, subquery_str)
            # LEFT JOIN field_relation ON
            #     alias.id = field_relation.field_column1
            #     AND field_relation.field_column2 IN (subquery)
            left_coumn = "id"
            if alias == "sale_report":
                left_coumn = "order_id"
            if alias == "account_invoice_report":
                left_coumn = "move_id"
            rel_alias = query.left_join(
                alias,
                left_coumn,
                field.relation,
                field.column1,
                field.name,
                extra=extra,
                extra_params=extra_params,
            )
            return '"{}"."{}"'.format(rel_alias, field.column2)
        return super()._inherits_join_calc(alias, fname, query)
Exemple #11
0
    def test_table_expression(self):
        query = Query(None, 'foo')
        from_clause, where_clause, where_params = query.get_sql()
        self.assertEqual(from_clause, '"foo"')

        query = Query(None, 'bar', 'SELECT id FROM foo')
        from_clause, where_clause, where_params = query.get_sql()
        self.assertEqual(from_clause, '(SELECT id FROM foo) AS "bar"')

        query = Query(None, 'foo')
        query.add_table('bar', 'SELECT id FROM foo')
        from_clause, where_clause, where_params = query.get_sql()
        self.assertEqual(from_clause, '"foo", (SELECT id FROM foo) AS "bar"')

        query = Query(None, 'foo')
        query.join('foo', 'bar_id', 'SELECT id FROM foo', 'id', 'bar')
        from_clause, where_clause, where_params = query.get_sql()
        self.assertEqual(
            from_clause,
            '"foo" JOIN (SELECT id FROM foo) AS "foo__bar" ON ("foo"."bar_id" = "foo__bar"."id")'
        )
Exemple #12
0
    def _get_sale_order_items_query(self, domain_per_model=None):
        if domain_per_model is None:
            domain_per_model = {}
        billable_project_domain = [('allow_billable', '=', True)]
        project_domain = [('id', 'in', self.ids),
                          ('sale_line_id', '!=', False)]
        if 'project.project' in domain_per_model:
            project_domain = expression.AND([
                domain_per_model['project.project'],
                project_domain,
                billable_project_domain,
            ])
        project_query = self.env['project.project']._where_calc(project_domain)
        self._apply_ir_rules(project_query, 'read')
        project_query_str, project_params = project_query.select(
            'id', 'sale_line_id')

        Task = self.env['project.task']
        task_domain = [('project_id', 'in', self.ids),
                       ('sale_line_id', '!=', False)]
        if Task._name in domain_per_model:
            task_domain = expression.AND([
                domain_per_model[Task._name],
                task_domain,
            ])
        task_query = Task._where_calc(task_domain)
        Task._apply_ir_rules(task_query, 'read')
        task_query_str, task_params = task_query.select(
            f'{Task._table}.project_id AS id', f'{Task._table}.sale_line_id')

        ProjectMilestone = self.env['project.milestone']
        milestone_domain = [('project_id', 'in', self.ids),
                            ('allow_billable', '=', True),
                            ('sale_line_id', '!=', False)]
        if ProjectMilestone._name in domain_per_model:
            milestone_domain = expression.AND([
                domain_per_model[ProjectMilestone._name],
                milestone_domain,
                billable_project_domain,
            ])
        milestone_query = ProjectMilestone._where_calc(milestone_domain)
        ProjectMilestone._apply_ir_rules(milestone_query)
        milestone_query_str, milestone_params = milestone_query.select(
            f'{ProjectMilestone._table}.project_id AS id',
            f'{ProjectMilestone._table}.sale_line_id',
        )

        query = Query(
            self._cr, 'project_sale_order_item', ' UNION '.join(
                [project_query_str, task_query_str, milestone_query_str]))
        query._where_params = project_params + task_params + milestone_params
        return query
Exemple #13
0
def _where_calc(self, domain, active_test=True):
    """Computes the WHERE clause needed to implement an OpenERP domain.
    :param domain: the domain to compute
    :type domain: list
    :param active_test: whether the default filtering of records with ``active``
                        field set to ``False`` should be applied.
    :return: the query expressing the given domain as provided in domain
    :rtype: osv.query.Query
    """
    # if the object has a field named 'active', filter out all inactive
    # records unless they were explicitely asked for
    if 'active' in self._fields and active_test and self._context.get(
            'active_test', True):
        # the item[0] trick below works for domain items and '&'/'|'/'!'
        # operators too
        if not any(item[0] == 'active' for item in domain):
            domain = [('active', '=', 1)] + domain
    #is_multi_search_installed = self.env['ir.module.module'].search([('state','=','installed'),('name','=','multi_search')], limit=1)
    self.env.cr.execute(
        "SELECT id FROM ir_module_module WHERE name='multi_search_with_comma' and state='installed' limit 1"
    )
    is_multi_search_installed = self.env.cr.fetchone()
    if domain:
        modified_domain = []
        #_logger.info(str(domain))
        for domain_tuple in domain:

            if not is_multi_search_installed:
                modified_domain.append(domain_tuple)
                continue
            if type(domain_tuple) in (list, tuple):
                if str(domain_tuple[1]) == 'ilike':
                    multi_name = domain_tuple[2].split(',')
                    len_name = len(multi_name)
                    if len_name > 1:
                        for length in multi_name:
                            modified_domain.append('|')
                        for f_name in multi_name:
                            modified_domain.append([
                                domain_tuple[0], domain_tuple[1],
                                f_name.strip()
                            ])
            modified_domain.append(domain_tuple)
        e = expression.expression(modified_domain, self)
        tables = e.get_tables()
        where_clause, where_params = e.to_sql()
        where_clause = [where_clause] if where_clause else []
    else:
        where_clause, where_params, tables = [], [], ['"%s"' % self._table]

    return Query(tables, where_clause, where_params)
Exemple #14
0
    def test_basic_query(self):
        query = Query(None, 'product_product')
        query.add_table('product_template')
        query.add_where("product_product.template_id = product_template.id")
        # add inner join
        alias = query.join("product_template", "categ_id", "product_category",
                           "id", "categ_id")
        self.assertEqual(alias, 'product_template__categ_id')
        # add left join
        alias = query.left_join("product_product", "user_id", "res_user", "id",
                                "user_id")
        self.assertEqual(alias, 'product_product__user_id')

        from_clause, where_clause, where_params = query.get_sql()
        self.assertEqual(
            from_clause,
            '"product_product", "product_template" JOIN "product_category" AS "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" AS "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id")'
        )
        self.assertEqual(where_clause,
                         "product_product.template_id = product_template.id")
Exemple #15
0
 def test_query_chained_explicit_joins(self):
     query = Query()
     query.add_table('product_product')
     query.add_table('product_template')
     query.where_clause.append(
         "product_product.template_id = product_template.id")
     query.add_join(("product_template", "product_category", "categ_id",
                     "id", "categ_id"),
                    implicit=False,
                    outer=False)  # add normal join
     query.add_join(("product_template__categ_id", "res_user", "user_id",
                     "id", "user_id"),
                    implicit=False,
                    outer=True)  # CHAINED outer join
     self.assertEqual(
         query.get_sql()[0].strip(),
         """"product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")"""
         .strip())
     self.assertEqual(
         query.get_sql()[1].strip(),
         """product_product.template_id = product_template.id""".strip())
Exemple #16
0
 def _compute_application_count(self):
     self.flush_model(['email_from'])
     applicants = self.env['hr.applicant']
     for applicant in self:
         if applicant.email_from or applicant.partner_phone or applicant.partner_mobile:
             applicants |= applicant
     # Done via SQL since read_group does not support grouping by lowercase field
     if applicants.ids:
         query = Query(self.env.cr, self._table, self._table_query)
         query.add_where('hr_applicant.id in %s', [tuple(applicants.ids)])
         # Count into the companies that are selected from the multi-company widget
         company_ids = self.env.context.get('allowed_company_ids')
         if company_ids:
             query.add_where('other.company_id in %s', [tuple(company_ids)])
         self._apply_ir_rules(query)
         from_clause, where_clause, where_clause_params = query.get_sql()
         # In case the applicant phone or mobile is configured in wrong field
         query_str = """
         SELECT hr_applicant.id as appl_id,
             COUNT(other.id) as count
           FROM hr_applicant
           JOIN hr_applicant other ON LOWER(other.email_from) = LOWER(hr_applicant.email_from)
             OR other.partner_phone = hr_applicant.partner_phone OR other.partner_phone = hr_applicant.partner_mobile
             OR other.partner_mobile = hr_applicant.partner_mobile OR other.partner_mobile = hr_applicant.partner_phone
         %(where)s
     GROUP BY hr_applicant.id
         """ % {
             'where': ('WHERE %s' % where_clause) if where_clause else '',
         }
         self.env.cr.execute(query_str, where_clause_params)
         application_data_mapped = dict(
             (data['appl_id'], data['count'])
             for data in self.env.cr.dictfetchall())
     else:
         application_data_mapped = dict()
     for applicant in applicants:
         applicant.application_count = application_data_mapped.get(
             applicant.id, 1) - 1
     (self - applicants).application_count = False
Exemple #17
0
 def test_raise_missing_lhs(self):
     query = Query(None, 'product_product')
     with self.assertRaises(AssertionError):
         query.join("product_template", "categ_id", "product_category",
                    "id", "categ_id")
Exemple #18
0
        def _read_from_database(self, field_names, inherited_field_names=[]):
            """ Read the given fields of the records in ``self`` from the database,
                and store them in cache. Access errors are also stored in cache.

                :param field_names: list of column names of model ``self``; all those
                    fields are guaranteed to be read
                :param inherited_field_names: list of column names from parent
                    models; some of those fields may not be read
            """
            if not self:
                return

            env = self.env
            cr, user, context = env.args

            # make a query object for selecting ids, and apply security rules to it
            param_ids = object()
            query = Query(['"%s"' % self._table],
                          ['"%s".id IN %%s' % self._table], [param_ids])
            self._apply_ir_rules(query, 'read')

            # determine the fields that are stored as columns in tables; ignore 'id'
            fields_pre = [
                field
                for field in (self._fields[name]
                              for name in field_names + inherited_field_names)
                if field.name != 'id'
                if field.base_field.store and field.base_field.column_type
                if not (
                    field.inherited and callable(field.base_field.translate))
            ]

            # the query may involve several tables: we need fully-qualified names
            def qualify(field):
                col = field.name
                res = self._inherits_join_calc(self._table, field.name, query)
                if field.type == 'binary' and (context.get('bin_size') or
                                               context.get('bin_size_' + col)):
                    # PG 9.2 introduces conflicting pg_size_pretty(numeric) -> need ::cast
                    res = 'pg_size_pretty(length(%s)::bigint)' % res
                return '%s as "%s"' % (res, col)

            qual_names = [
                qualify(name) for name in [self._fields['id']] + fields_pre
            ]

            # determine the actual query to execute
            from_clause, where_clause, params = query.get_sql()
            query_str = "SELECT %s FROM %s WHERE %s" % (
                ",".join(qual_names), from_clause, where_clause)

            result = []
            param_pos = params.index(param_ids)
            for sub_ids in cr.split_for_in_conditions(self.ids):
                params[param_pos] = tuple(sub_ids)
                cr.execute(query_str, params)
                result.extend(cr.dictfetchall())

            ids = [vals['id'] for vals in result]
            fetched = self.browse(ids)

            if ids:
                # translate the fields if necessary
                if context.get('lang'):
                    for field in fields_pre:
                        if not field.inherited and callable(field.translate):
                            name = field.name
                            translate = field.get_trans_func(fetched)
                            for vals in result:
                                vals[name] = translate(vals['id'], vals[name])

                # store result in cache
                for vals in result:
                    record = self.browse(vals.pop('id'), self._prefetch)
                    record._cache.update(
                        record._convert_to_cache(vals, validate=False))

                # determine the fields that must be processed now;
                # for the sake of simplicity, we ignore inherited fields
                for name in field_names:
                    field = self._fields[name]
                    if not field.column_type:
                        field.read(fetched)

            # Warn about deprecated fields now that fields_pre and fields_post are computed
            for name in field_names:
                field = self._fields[name]
                if field.deprecated:
                    _logger.warning('Field %s is deprecated: %s', field,
                                    field.deprecated)

            # store failed values in cache for the records that could not be read
            missing = self - fetched
            if missing:
                extras = fetched - self
                if extras:
                    raise AccessError(
                        _("Database fetch misses ids ({}) and has extra ids ({}), may be caused by a type incoherence in a previous request"
                          ).format(
                              missing._ids,
                              extras._ids,
                          ))
                # mark non-existing records in missing
                forbidden = missing.exists()
                if forbidden:
                    _logger.info(
                        _('The requested operation cannot be completed due to record rules: Document type: %s, Operation: %s, Records: %s, User: %s') % \
                        (self._name, 'read', ','.join([str(r.id) for r in self][:6]), self._uid))
                    # store an access error exception in existing records
                    exc = AccessError(
                        # _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
                        # (self._name, 'read')
                        # Modificación del mensaje de error: José Candelas
                        _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s, Forbidden: %s)') % \
                        (self._name, 'read', forbidden)
                    )
                    self.env.cache.set_failed(forbidden, self._fields.values(),
                                              exc)