def _domain_move_lines(self, search_str):
     """ Returns the domain from the search_str search
         :param search_str: search string
     """
     if not search_str:
         return []
     str_domain = self._str_domain_for_mv_line(search_str)
     if search_str[0] in ['-', '+']:
         try:
             amounts_str = search_str.split('|')
             for amount_str in amounts_str:
                 amount = amount_str[0] == '-' and float(
                     amount_str) or float(amount_str[1:])
                 amount_domain = [
                     '|',
                     ('amount_residual', '=', amount),
                     '|',
                     ('amount_residual_currency', '=', amount),
                     '|',
                     (amount_str[0] == '-' and 'credit'
                      or 'debit', '=', float(amount_str[1:])),
                     ('amount_currency', '=', amount),
                 ]
                 str_domain = expression.OR([str_domain, amount_domain])
         except:
             pass
     else:
         try:
             amount = float(search_str)
             amount_domain = [
                 '|',
                 ('amount_residual', '=', amount),
                 '|',
                 ('amount_residual_currency', '=', amount),
                 '|',
                 ('amount_residual', '=', -amount),
                 '|',
                 ('amount_residual_currency', '=', -amount),
                 '&',
                 ('account_id.internal_type', '=', 'liquidity'),
                 '|',
                 '|',
                 '|',
                 ('debit', '=', amount),
                 ('credit', '=', amount),
                 ('amount_currency', '=', amount),
                 ('amount_currency', '=', -amount),
             ]
             str_domain = expression.OR([str_domain, amount_domain])
         except:
             pass
     return str_domain
Пример #2
0
    def _domain_move_lines_for_reconciliation(self, st_line, aml_accounts, partner_id, excluded_ids=None, search_str=False):
        """ Return the domain for account.move.line records which can be used for bank statement reconciliation.

            :param aml_accounts:
            :param partner_id:
            :param excluded_ids:
            :param search_str:
        """

        domain_reconciliation = [
            '&', '&',
            ('statement_line_id', '=', False),
            ('account_id', 'in', aml_accounts),
            ('payment_id', '<>', False)
        ]

        # default domain matching
        domain_matching = expression.AND([
            [('reconciled', '=', False)],
            [('account_id.reconcile', '=', True)]
        ])

        domain = expression.OR([domain_reconciliation, domain_matching])
        if partner_id:
            domain = expression.AND([domain, [('partner_id', '=', partner_id)]])

        # Domain factorized for all reconciliation use cases
        if search_str:
            str_domain = self._domain_move_lines(search_str=search_str)
            if not partner_id:
                str_domain = expression.OR([
                    str_domain,
                    [('partner_id.name', 'ilike', search_str)]
                ])
            domain = expression.AND([
                domain,
                str_domain
            ])

        if excluded_ids:
            domain = expression.AND([
                [('id', 'not in', excluded_ids)],
                domain
            ])
        # filter on account.move.line having the same company as the statement line
        domain = expression.AND([domain, [('company_id', '=', st_line.company_id.id)]])

        if st_line.company_id.account_bank_reconciliation_start:
            domain = expression.AND([domain, [('date', '>=', st_line.company_id.account_bank_reconciliation_start)]])

        return domain
Пример #3
0
    def _remove_copied_views(self):
        """ Remove the copies of the views installed by the modules in `self`.

        Those copies do not have an external id so they will not be cleaned by
        `_module_data_uninstall`. This is why we rely on `key` instead.

        It is important to remove these copies because using them will crash if
        they rely on data that don't exist anymore if the module is removed.
        """
        domain = expression.OR([[('key', '=like', m.name + '.%')] for m in self])
        orphans = self.env['ir.ui.view'].with_context(**{'active_test': False, MODULE_UNINSTALL_FLAG: True}).search(domain)
        orphans.unlink()
Пример #4
0
 def _search_ids(model, values, force_company_id):
     ids = set()
     domain = []
     for item in values:
         if isinstance(item, int):
             ids.add(item)
         else:
             domain = expression.OR([[('name', 'ilike', item)], domain])
     if domain:
         if force_company_id:
             domain = expression.AND([[('company_id', '=', force_company_id)], domain])
         ids |= set(self.env[model].search(domain).ids)
     return ids
Пример #5
0
    def _search_available_today(self, operator, value):
        if (not operator in ['=', '!=']) or (not value in [True, False]):
            return []

        searching_for_true = (operator == '=' and value) or (operator == '!='
                                                             and not value)
        today = fields.Date.context_today(self)
        fieldname = 'recurrency_%s' % (WEEKDAY_TO_NAME[today.weekday()])

        return expression.AND([[(fieldname, operator, value)],
                               expression.OR([
                                   [('until', '=', False)],
                                   [('until',
                                     '>' if searching_for_true else '<', today)
                                    ],
                               ])])
Пример #6
0
    def _compute_domain(self, model_name, mode="read"):
        if mode not in self._MODES:
            raise ValueError("Invalid mode: {!r}".format(mode))

        if self._uid == SUPERUSER_ID:
            return None

        query = """ SELECT r.id FROM ir_rule r JOIN ir_model m ON (r.model_id=m.id)
                    WHERE m.model=%s AND r.active AND r.perm_{mode}
                    AND (r.id IN (SELECT rule_group_id FROM rule_group_rel rg
                                  JOIN res_groups_users_rel gu ON (rg.group_id=gu.gid)
                                  WHERE gu.uid=%s)
                         OR r.global)
                """.format(mode=mode)
        self._cr.execute(query, (model_name, self._uid))
        rule_ids = [row[0] for row in self._cr.fetchall()]
        if not rule_ids:
            return []

        # browse user and rules as SUPERUSER_ID to avoid access errors!
        eval_context = self._eval_context()
        user_groups = self.env.user.groups_id
        global_domains = []  # list of domains
        group_domains = []  # list of domains
        for rule in self.browse(rule_ids).sudo():
            # BEGIN redefined part of original _compute_domain of eagle/base/addons/ir/ir_rule.
            # have to redefine all method to take in account new ir.rule ``backend_behaviour`` setting
            dom = []
            if not eval_context.get("website_id") and rule.backend_behaviour:
                dom = ([(1, "=",
                         1)] if rule.backend_behaviour == "true" else [(0, "=",
                                                                        1)])
            else:
                # evaluate the domain for the current user
                dom = (safe_eval(rule.domain_force, eval_context)
                       if rule.domain_force else [])
                dom = expression.normalize_domain(dom)
            # END redefined part of original _compute_domain
            if not rule.groups:
                global_domains.append(dom)
            elif rule.groups & user_groups:
                group_domains.append(dom)

        # combine global domains and group domains
        if not group_domains:
            return expression.AND(global_domains)
        return expression.AND(global_domains + [expression.OR(group_domains)])
Пример #7
0
    def _compute_debit_credit_balance(self):
        Curr = self.env['res.currency']
        analytic_line_obj = self.env['account.analytic.line']
        domain = [('account_id', 'in', self.ids)]
        if self._context.get('from_date', False):
            domain.append(('date', '>=', self._context['from_date']))
        if self._context.get('to_date', False):
            domain.append(('date', '<=', self._context['to_date']))
        if self._context.get('tag_ids'):
            tag_domain = expression.OR([[('tag_ids', 'in', [tag])]
                                        for tag in self._context['tag_ids']])
            domain = expression.AND([domain, tag_domain])
        if self._context.get('company_ids'):
            domain.append(('company_id', 'in', self._context['company_ids']))

        user_currency = self.env.company.currency_id
        credit_groups = analytic_line_obj.read_group(
            domain=domain + [('amount', '>=', 0.0)],
            fields=['account_id', 'currency_id', 'amount'],
            groupby=['account_id', 'currency_id'],
            lazy=False,
        )
        data_credit = defaultdict(float)
        for l in credit_groups:
            data_credit[l['account_id'][0]] += Curr.browse(
                l['currency_id'][0])._convert(l['amount'], user_currency,
                                              self.env.company,
                                              fields.Date.today())

        debit_groups = analytic_line_obj.read_group(
            domain=domain + [('amount', '<', 0.0)],
            fields=['account_id', 'currency_id', 'amount'],
            groupby=['account_id', 'currency_id'],
            lazy=False,
        )
        data_debit = defaultdict(float)
        for l in debit_groups:
            data_debit[l['account_id'][0]] += Curr.browse(
                l['currency_id'][0])._convert(l['amount'], user_currency,
                                              self.env.company,
                                              fields.Date.today())

        for account in self:
            account.debit = abs(data_debit.get(account.id, 0.0))
            account.credit = data_credit.get(account.id, 0.0)
            account.balance = account.credit - account.debit
Пример #8
0
    def _search_available_today(self, operator, value):
        if (not operator in ['=', '!=']) or (not value in [True, False]):
            return []

        searching_for_true = (operator == '=' and value) or (operator == '!='
                                                             and not value)

        now = fields.Datetime.now().replace(tzinfo=pytz.UTC).astimezone(
            pytz.timezone(self.env.user.tz or 'UTC'))
        fieldname = 'recurrency_%s' % (WEEKDAY_TO_NAME[now.weekday()])

        recurrency_domain = expression.OR([[
            ('recurrency_end_date', '=', False)
        ], [('recurrency_end_date', '>' if searching_for_true else '<', now)]])

        return expression.AND(
            [recurrency_domain, [(fieldname, operator, value)]])
Пример #9
0
 def unlink(self):
     """
     Override unlink method :
     Remove attendee from a channel, then also remove slide.slide.partner related to.
     """
     removed_slide_partner_domain = []
     for channel_partner in self:
         # find all slide link to the channel and the partner
         removed_slide_partner_domain = expression.OR([
             removed_slide_partner_domain,
             [('partner_id', '=', channel_partner.partner_id.id),
              ('slide_id', 'in', channel_partner.channel_id.slide_ids.ids)]
         ])
     if removed_slide_partner_domain:
         self.env['slide.slide.partner'].search(
             removed_slide_partner_domain).unlink()
     return super(ChannelUsersRelation, self).unlink()
Пример #10
0
    def _search_is_available_at(self, operator, value):
        supported_operators = ['in', 'not in', '=', '!=']

        if not operator in supported_operators:
            return expression.TRUE_DOMAIN

        if isinstance(value, int):
            value = [value]

        if operator in expression.NEGATIVE_TERM_OPERATORS:
            return expression.AND([[
                ('supplier_id.available_location_ids', 'not in', value)
            ], [('supplier_id.available_location_ids', '!=', False)]])

        return expression.OR([[
            ('supplier_id.available_location_ids', 'in', value)
        ], [('supplier_id.available_location_ids', '=', False)]])
Пример #11
0
    def _remove_membership(self, partner_ids):
        """ Unlink (!!!) the relationships between the passed partner_ids
        and the channels and their slides (done in the unlink of slide.channel.partner model). """
        if not partner_ids:
            raise ValueError(
                "Do not use this method with an empty partner_id recordset")

        removed_channel_partner_domain = []
        for channel in self:
            removed_channel_partner_domain = expression.OR([
                removed_channel_partner_domain,
                [('partner_id', 'in', partner_ids),
                 ('channel_id', '=', channel.id)]
            ])

        if removed_channel_partner_domain:
            self.env['slide.channel.partner'].sudo().search(
                removed_channel_partner_domain).unlink()
Пример #12
0
    def get_mention_suggestions(self, search, limit=8):
        """ Return 'limit'-first partners' id, name and email such that the name or email matches a
            'search' string. Prioritize users, and then extend the research to all partners. """
        search_dom = expression.OR([[('name', 'ilike', search)],
                                    [('email', 'ilike', search)]])
        fields = ['id', 'name', 'email']

        # Search users
        domain = expression.AND([[('user_ids.id', '!=', False)], search_dom])
        users = self.search_read(domain, fields, limit=limit)

        # Search partners if less than 'limit' users found
        partners = []
        if len(users) < limit:
            partners = self.search_read(search_dom, fields, limit=limit)
            # Remove duplicates
            partners = [
                p for p in partners
                if not len([u for u in users if u['id'] == p['id']])
            ]

        return [users, partners]
Пример #13
0
    def _get_failing(self, for_records, mode='read'):
        """ Returns the rules for the mode for the current user which fail on
        the specified records.

        Can return any global rule and/or all local rules (since local rules
        are OR-ed together, the entire group succeeds or fails, while global
        rules get AND-ed and can each fail)
        """
        Model = for_records.browse(()).sudo()
        eval_context = self._eval_context()

        all_rules = self._get_rules(Model._name, mode=mode).sudo()

        # first check if the group rules fail for any record (aka if
        # searching on (records, group_rules) filters out some of the records)
        group_rules = all_rules.filtered(
            lambda r: r.groups and r.groups & self.env.user.groups_id)
        group_domains = expression.OR([
            safe_eval(r.domain_force, eval_context) if r.domain_force else []
            for r in group_rules
        ])
        # if all records get returned, the group rules are not failing
        if Model.search_count(
                expression.AND([[('id', 'in', for_records.ids)],
                                group_domains])) == len(for_records):
            group_rules = self.browse(())

        # failing rules are previously selected group rules or any failing global rule
        def is_failing(r, ids=for_records.ids):
            dom = safe_eval(r.domain_force,
                            eval_context) if r.domain_force else []
            return Model.search_count(
                expression.AND([[('id', 'in', ids)],
                                expression.normalize_domain(dom)])) < len(ids)

        return all_rules.filtered(lambda r: r in group_rules or
                                  (not r.groups and is_failing(r))).with_user(
                                      self.env.user)
Пример #14
0
    def _compute_domain(self, model_name, mode="read"):
        if mode not in self._MODES:
            raise ValueError('Invalid mode: %r' % (mode,))

        if self._uid == SUPERUSER_ID:
            return None

        query = """ SELECT r.id FROM ir_rule r JOIN ir_model m ON (r.model_id=m.id)
                    WHERE m.model=%s AND r.active AND r.perm_{mode}
                    AND (r.id IN (SELECT rule_group_id FROM rule_group_rel rg
                                  JOIN res_groups_users_rel gu ON (rg.group_id=gu.gid)
                                  WHERE gu.uid=%s)
                         OR r.global)
                """.format(mode=mode)
        self._cr.execute(query, (model_name, self._uid))
        rule_ids = [row[0] for row in self._cr.fetchall()]
        if not rule_ids:
            return []

        # browse user and rules as SUPERUSER_ID to avoid access errors!
        eval_context = self._eval_context()
        user_groups = self.env.user.groups_id
        global_domains = []                     # list of domains
        group_domains = []                      # list of domains
        for rule in self.browse(rule_ids).sudo():
            # evaluate the domain for the current user
            dom = safe_eval(rule.domain_force, eval_context) if rule.domain_force else []
            dom = expression.normalize_domain(dom)
            if not rule.groups:
                global_domains.append(dom)
            elif rule.groups & user_groups:
                group_domains.append(dom)

        # combine global domains and group domains
        if not group_domains:
            return expression.AND(global_domains)
        return expression.AND(global_domains + [expression.OR(group_domains)])
Пример #15
0
    def _compute_domain(self, model_name, mode="read"):
        rules = self._get_rules(model_name, mode=mode)
        if not rules:
            return

        # browse user and rules as SUPERUSER_ID to avoid access errors!
        eval_context = self._eval_context()
        user_groups = self.env.user.groups_id
        global_domains = []  # list of domains
        group_domains = []  # list of domains
        for rule in rules.sudo():
            # evaluate the domain for the current user
            dom = safe_eval(rule.domain_force,
                            eval_context) if rule.domain_force else []
            dom = expression.normalize_domain(dom)
            if not rule.groups:
                global_domains.append(dom)
            elif rule.groups & user_groups:
                group_domains.append(dom)

        # combine global domains and group domains
        if not group_domains:
            return expression.AND(global_domains)
        return expression.AND(global_domains + [expression.OR(group_domains)])
Пример #16
0
 def _default_sale_line_domain(self):
     domain = super(AccountAnalyticLine, self)._default_sale_line_domain()
     return expression.OR(
         [domain, [('qty_delivered_method', '=', 'timesheet')]])
Пример #17
0
 def _get_translation_frontend_modules_domain(cls):
     domain = super(IrHttp, cls)._get_translation_frontend_modules_domain()
     return expression.OR([domain, [('name', '=', 'im_livechat')]])
Пример #18
0
 def _name_search(self,
                  name,
                  args=None,
                  operator='ilike',
                  limit=100,
                  name_get_uid=None):
     if not args:
         args = []
     if name:
         positive_operators = ['=', 'ilike', '=ilike', 'like', '=like']
         product_ids = []
         if operator in positive_operators:
             product_ids = self._search([('default_code', '=', name)] +
                                        args,
                                        limit=limit,
                                        access_rights_uid=name_get_uid)
             if not product_ids:
                 product_ids = self._search([('barcode', '=', name)] + args,
                                            limit=limit,
                                            access_rights_uid=name_get_uid)
         if not product_ids and operator not in expression.NEGATIVE_TERM_OPERATORS:
             # Do not merge the 2 next lines into one single search, SQL search performance would be abysmal
             # on a database with thousands of matching products, due to the huge merge+unique needed for the
             # OR operator (and given the fact that the 'name' lookup results come from the ir.translation table
             # Performing a quick memory merge of ids in Python will give much better performance
             product_ids = self._search(args +
                                        [('default_code', operator, name)],
                                        limit=limit)
             if not limit or len(product_ids) < limit:
                 # we may underrun the limit because of dupes in the results, that's fine
                 limit2 = (limit - len(product_ids)) if limit else False
                 product2_ids = self._search(
                     args + [('name', operator, name),
                             ('id', 'not in', product_ids)],
                     limit=limit2,
                     access_rights_uid=name_get_uid)
                 product_ids.extend(product2_ids)
         elif not product_ids and operator in expression.NEGATIVE_TERM_OPERATORS:
             domain = expression.OR([
                 [
                     '&', ('default_code', operator, name),
                     ('name', operator, name)
                 ],
                 [
                     '&', ('default_code', '=', False),
                     ('name', operator, name)
                 ],
             ])
             domain = expression.AND([args, domain])
             product_ids = self._search(domain,
                                        limit=limit,
                                        access_rights_uid=name_get_uid)
         if not product_ids and operator in positive_operators:
             ptrn = re.compile('(\[(.*?)\])')
             res = ptrn.search(name)
             if res:
                 product_ids = self._search(
                     [('default_code', '=', res.group(2))] + args,
                     limit=limit,
                     access_rights_uid=name_get_uid)
         # still no results, partner in context: search on supplier info as last hope to find something
         if not product_ids and self._context.get('partner_id'):
             suppliers_ids = self.env['product.supplierinfo']._search(
                 [('name', '=', self._context.get('partner_id')), '|',
                  ('product_code', operator, name),
                  ('product_name', operator, name)],
                 access_rights_uid=name_get_uid)
             if suppliers_ids:
                 product_ids = self._search(
                     [('product_tmpl_id.seller_ids', 'in', suppliers_ids)],
                     limit=limit,
                     access_rights_uid=name_get_uid)
     else:
         product_ids = self._search(args,
                                    limit=limit,
                                    access_rights_uid=name_get_uid)
     return self.browse(product_ids).name_get()
    def _domain_move_lines_for_reconciliation(self,
                                              st_line,
                                              aml_accounts,
                                              partner_id,
                                              excluded_ids=[],
                                              search_str=False,
                                              mode='rp'):
        """ Return the domain for account.move.line records which can be used for bank statement reconciliation.

            :param aml_accounts:
            :param partner_id:
            :param excluded_ids:
            :param search_str:
            :param mode: 'rp' for receivable/payable or 'other'
        """
        AccountMoveLine = self.env['account.move.line']

        #Always exclude the journal items that have been marked as 'to be checked' in a former bank statement reconciliation
        to_check_excluded = AccountMoveLine.search(
            AccountMoveLine._get_suspense_moves_domain()).ids
        excluded_ids.extend(to_check_excluded)

        domain_reconciliation = [
            '&',
            '&',
            '&',
            ('statement_line_id', '=', False),
            ('account_id', 'in', aml_accounts),
            ('payment_id', '<>', False),
            ('balance', '!=', 0.0),
        ]

        # default domain matching
        domain_matching = [
            '&',
            '&',
            ('reconciled', '=', False),
            ('account_id.reconcile', '=', True),
            ('balance', '!=', 0.0),
        ]

        domain = expression.OR([domain_reconciliation, domain_matching])
        if partner_id:
            domain = expression.AND(
                [domain, [('partner_id', '=', partner_id)]])
        if mode == 'rp':
            domain = expression.AND([
                domain,
                [('account_id.internal_type', 'in',
                  ['receivable', 'payable', 'liquidity'])]
            ])
        else:
            domain = expression.AND([
                domain,
                [('account_id.internal_type', 'not in',
                  ['receivable', 'payable', 'liquidity'])]
            ])

        # Domain factorized for all reconciliation use cases
        if search_str:
            str_domain = self._domain_move_lines(search_str=search_str)
            str_domain = expression.OR(
                [str_domain, [('partner_id.name', 'ilike', search_str)]])
            domain = expression.AND([domain, str_domain])

        if excluded_ids:
            domain = expression.AND([[('id', 'not in', excluded_ids)], domain])
        # filter on account.move.line having the same company as the statement line
        domain = expression.AND(
            [domain, [('company_id', '=', st_line.company_id.id)]])

        # take only moves in valid state. Draft is accepted only when "Post At" is set
        # to "Bank Reconciliation" in the associated journal
        domain_post_at = [
            '|',
            '&',
            ('move_id.state', '=', 'draft'),
            ('journal_id.post_at', '=', 'bank_rec'),
            ('move_id.state', 'not in', ['draft', 'cancel']),
        ]
        domain = expression.AND([domain, domain_post_at])

        if st_line.company_id.account_bank_reconciliation_start:
            domain = expression.AND([
                domain,
                [('date', '>=',
                  st_line.company_id.account_bank_reconciliation_start)]
            ])
        return domain
Пример #20
0
    def _decode_ubl_2_1(self, tree):
        self.ensure_one()
        namespaces = self._get_ubl_namespaces(tree)

        elements = tree.xpath('//cbc:InvoiceTypeCode', namespaces=namespaces)
        if elements:
            type_code = elements[0].text
            type = 'in_refund' if type_code == '381' else 'in_invoice'
        else:
            type = 'in_invoice'

        default_journal = self.with_context(
            default_type=type)._get_default_journal()

        with Form(
                self.with_context(
                    default_type=type,
                    default_journal_id=default_journal.id)) as invoice_form:
            # Reference
            elements = tree.xpath('//cbc:ID', namespaces=namespaces)
            if elements:
                invoice_form.ref = elements[0].text
            elements = tree.xpath('//cbc:InstructionID', namespaces=namespaces)
            if elements:
                invoice_form.invoice_payment_ref = elements[0].text

            # Dates
            elements = tree.xpath('//cbc:IssueDate', namespaces=namespaces)
            if elements:
                invoice_form.invoice_date = elements[0].text
            elements = tree.xpath('//cbc:PaymentDueDate',
                                  namespaces=namespaces)
            if elements:
                invoice_form.invoice_date_due = elements[0].text
            # allow both cbc:PaymentDueDate and cbc:DueDate
            elements = tree.xpath('//cbc:DueDate', namespaces=namespaces)
            invoice_form.invoice_date_due = invoice_form.invoice_date_due or elements and elements[
                0].text

            # Currency
            elements = tree.xpath('//cbc:DocumentCurrencyCode',
                                  namespaces=namespaces)
            currency_code = elements and elements[0].text or ''
            currency = self.env['res.currency'].search(
                [('name', '=', currency_code.upper())], limit=1)
            if elements:
                invoice_form.currency_id = currency

            # Incoterm
            elements = tree.xpath(
                '//cbc:TransportExecutionTerms/cac:DeliveryTerms/cbc:ID',
                namespaces=namespaces)
            if elements:
                invoice_form.invoice_incoterm_id = self.env[
                    'account.incoterms'].search(
                        [('code', '=', elements[0].text)], limit=1)

            # Partner
            partner_element = tree.xpath(
                '//cac:AccountingSupplierParty/cac:Party',
                namespaces=namespaces)
            if partner_element:
                domains = []
                partner_element = partner_element[0]
                elements = partner_element.xpath(
                    '//cac:AccountingSupplierParty/cac:Party//cbc:Name',
                    namespaces=namespaces)
                if elements:
                    partner_name = elements[0].text
                    domains.append([('name', 'ilike', partner_name)])
                elements = partner_element.xpath(
                    '//cac:AccountingSupplierParty/cac:Party//cbc:Telephone',
                    namespaces=namespaces)
                if elements:
                    partner_telephone = elements[0].text
                    domains.append([('phone', '=', partner_telephone),
                                    ('mobile', '=', partner_telephone)])
                elements = partner_element.xpath(
                    '//cac:AccountingSupplierParty/cac:Party//cbc:ElectronicMail',
                    namespaces=namespaces)
                if elements:
                    partner_mail = elements[0].text
                    domains.append([('email', '=', partner_mail)])
                elements = partner_element.xpath(
                    '//cac:AccountingSupplierParty/cac:Party//cbc:ID',
                    namespaces=namespaces)
                if elements:
                    partner_id = elements[0].text
                    domains.append([('vat', 'like', partner_id)])

                if domains:
                    partner = self.env['res.partner'].search(
                        expression.OR(domains), limit=1)
                    if partner:
                        invoice_form.partner_id = partner
                    else:
                        invoice_form.partner_id = self.env['res.partner']

            # Regenerate PDF
            attachments = self.env['ir.attachment']
            elements = tree.xpath('//cac:AdditionalDocumentReference',
                                  namespaces=namespaces)
            for element in elements:
                attachment_name = element.xpath('cbc:ID',
                                                namespaces=namespaces)
                attachment_data = element.xpath(
                    'cac:Attachment//cbc:EmbeddedDocumentBinaryObject',
                    namespaces=namespaces)
                if attachment_name and attachment_data:
                    attachments |= self.env['ir.attachment'].create({
                        'name':
                        attachment_name[0].text,
                        'res_id':
                        self.id,
                        'res_model':
                        'account.move',
                        'datas':
                        attachment_data[0].text,
                        'type':
                        'binary',
                    })
            if attachments:
                self.with_context(no_new_invoice=True).message_post(
                    attachment_ids=attachments.ids)

            # Lines
            lines_elements = tree.xpath('//cac:InvoiceLine',
                                        namespaces=namespaces)
            for eline in lines_elements:
                with invoice_form.invoice_line_ids.new() as invoice_line_form:
                    # Quantity
                    elements = eline.xpath('cbc:InvoicedQuantity',
                                           namespaces=namespaces)
                    quantity = elements and float(elements[0].text) or 1.0
                    invoice_line_form.quantity = quantity

                    # Price Unit
                    elements = eline.xpath('cac:Price/cbc:PriceAmount',
                                           namespaces=namespaces)
                    invoice_line_form.price_unit = elements and float(
                        elements[0].text) or 0.0

                    # Name
                    elements = eline.xpath('cac:Item/cbc:Description',
                                           namespaces=namespaces)
                    invoice_line_form.name = elements and elements[0].text or ''
                    invoice_line_form.name = invoice_line_form.name.replace(
                        '%month%',
                        str(
                            fields.Date.to_date(invoice_form.invoice_date).
                            month))  # TODO: full name in locale
                    invoice_line_form.name = invoice_line_form.name.replace(
                        '%year%',
                        str(
                            fields.Date.to_date(
                                invoice_form.invoice_date).year))

                    # Product
                    elements = eline.xpath(
                        'cac:Item/cac:SellersItemIdentification/cbc:ID',
                        namespaces=namespaces)
                    domains = []
                    if elements:
                        product_code = elements[0].text
                        domains.append([('default_code', '=', product_code)])
                    elements = eline.xpath(
                        'cac:Item/cac:StandardItemIdentification/cbc:ID[@schemeID=\'GTIN\']',
                        namespaces=namespaces)
                    if elements:
                        product_ean13 = elements[0].text
                        domains.append([('ean13', '=', product_ean13)])
                    if domains:
                        product = self.env['product.product'].search(
                            expression.OR(domains), limit=1)
                        if product:
                            invoice_line_form.product_id = product

                    # Taxes
                    taxes_elements = eline.xpath(
                        'cac:TaxTotal/cac:TaxSubtotal', namespaces=namespaces)
                    invoice_line_form.tax_ids.clear()
                    for etax in taxes_elements:
                        elements = etax.xpath('cbc:Percent',
                                              namespaces=namespaces)
                        if elements:
                            tax = self.env['account.tax'].search(
                                [
                                    ('company_id', '=', self.env.company.id),
                                    ('amount', '=', float(elements[0].text)),
                                    ('type_tax_use', '=',
                                     invoice_form.journal_id.type),
                                ],
                                order='sequence ASC',
                                limit=1)
                            if tax:
                                invoice_line_form.tax_ids.add(tax)

        return invoice_form.save()