# -*- coding: utf-8 -*- from dateutil.relativedelta import relativedelta from odoo import models, fields class AccountReconcileModel(models.Model): _inherit = 'account.reconcile.model' def _get_invoice_matching_so_candidates(self, st_line, partner): """ Find a match between the bank transaction and some sale orders. If none of them are invoiced, there are returned to display a message to the user allowing him to show the matched sale orders. If some of them are already matched, the journal items are suggested to the user. :param st_line: A statement line. :param partner: The partner to consider. :return: {'allow_auto_reconcile': , 'amls': } if some sale orders are invoiced. {'sale_orders': } otherwise. """ assert self.rule_type == 'invoice_matching' for model in ('sale.order', 'sale.order.line', 'account.move', 'account.move.line'): self.env[model].flush_model() _numerical_tokens, exact_tokens, text_tokens = self._get_invoice_matching_st_line_tokens(st_line) if not (exact_tokens or text_tokens): return # Find the sale orders that are not yet invoiced or already invoices. domain = [ ('company_id', '=', st_line.company_id.id), '|', ('invoice_status', 'in', ('to invoice', 'invoiced')), ('state', '=', 'sent'), ] sale_orders = self.env['sale.order'] if exact_tokens: sale_orders = self.env['sale.order'].search(domain + [('name', 'in', exact_tokens)]) if not sale_orders and text_tokens: query = self.env['sale.order']._where_calc(domain) tables, where_clause, where_params = query.get_sql() self._cr.execute( rf''' WITH sale_order_name AS ( SELECT sale_order.id, SUBSTRING(REGEXP_REPLACE(LOWER(sale_order.name), '[^0-9a-z\s]', '', 'g'), '\S(?:.*\S)*') AS name FROM {tables} WHERE {where_clause} ) SELECT sub.id FROM sale_order_name sub WHERE sub.name LIKE ANY(ARRAY[%s]) ''', where_params + [[t.lower() for t in set(text_tokens)]], ) sale_order_ids = [r[0] for r in self._cr.fetchall()] if sale_order_ids: sale_orders = sale_orders.browse(sale_order_ids) if sale_orders: results = {'sale_orders': sale_orders} # Find some related invoices. aml_domain = self._get_invoice_matching_amls_domain(st_line, partner) invoices = sale_orders.invoice_ids if not invoices: # The sale orders are not yet invoiced. Return them to allow the user to invoice them from # the bank reco widget. return results invoice_amls = invoices.line_ids.filtered_domain(aml_domain) matched_payments = invoices._get_reconciled_payments() payments_amls = matched_payments.line_ids.filtered_domain(aml_domain) amls = payments_amls | invoice_amls if not amls: # The invoices and their payments are all already reconciled. Don't match anything and let the others rules trying # to match potential payments instead. return results['amls'] = amls results['allow_auto_reconcile'] = True return results def _get_invoice_matching_rules_map(self): # EXTENDS account res = super()._get_invoice_matching_rules_map() res[0].append(self._get_invoice_matching_so_candidates) return res