forked from Mapan/odoo17e
711 lines
37 KiB
Python
711 lines
37 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import json
|
|
|
|
from odoo import api, models, _, fields
|
|
from odoo.exceptions import UserError
|
|
from odoo.osv import expression
|
|
from odoo.tools.misc import format_date, get_lang
|
|
|
|
from datetime import timedelta
|
|
from collections import defaultdict
|
|
|
|
|
|
class PartnerLedgerCustomHandler(models.AbstractModel):
|
|
_name = 'account.partner.ledger.report.handler'
|
|
_inherit = 'account.report.custom.handler'
|
|
_description = 'Partner Ledger Custom Handler'
|
|
|
|
def _get_custom_display_config(self):
|
|
return {
|
|
'css_custom_class': 'partner_ledger',
|
|
'templates': {
|
|
'AccountReportLineName': 'account_reports.PartnerLedgerLineName',
|
|
},
|
|
}
|
|
|
|
def _dynamic_lines_generator(self, report, options, all_column_groups_expression_totals, warnings=None):
|
|
partner_lines, totals_by_column_group = self._build_partner_lines(report, options)
|
|
lines = report._regroup_lines_by_name_prefix(options, partner_lines, '_report_expand_unfoldable_line_partner_ledger_prefix_group', 0)
|
|
|
|
# Inject sequence on dynamic lines
|
|
lines = [(0, line) for line in lines]
|
|
|
|
# Report total line.
|
|
lines.append((0, self._get_report_line_total(options, totals_by_column_group)))
|
|
|
|
return lines
|
|
|
|
def _build_partner_lines(self, report, options, level_shift=0):
|
|
lines = []
|
|
|
|
totals_by_column_group = {
|
|
column_group_key: {
|
|
total: 0.0
|
|
for total in ['debit', 'credit', 'balance']
|
|
}
|
|
for column_group_key in options['column_groups']
|
|
}
|
|
|
|
partners_results = self._query_partners(options)
|
|
|
|
search_filter = options.get('filter_search_bar', '')
|
|
accept_unknown_in_filter = search_filter.lower() in self._get_no_partner_line_label().lower()
|
|
for partner, results in partners_results:
|
|
if options['export_mode'] == 'print' and search_filter and not partner and not accept_unknown_in_filter:
|
|
# When printing and searching for a specific partner, make it so we only show its lines, not the 'Unknown Partner' one, that would be
|
|
# shown in case a misc entry with no partner was reconciled with one of the target partner's entries.
|
|
continue
|
|
|
|
partner_values = defaultdict(dict)
|
|
for column_group_key in options['column_groups']:
|
|
partner_sum = results.get(column_group_key, {})
|
|
|
|
partner_values[column_group_key]['debit'] = partner_sum.get('debit', 0.0)
|
|
partner_values[column_group_key]['credit'] = partner_sum.get('credit', 0.0)
|
|
partner_values[column_group_key]['balance'] = partner_sum.get('balance', 0.0)
|
|
|
|
totals_by_column_group[column_group_key]['debit'] += partner_values[column_group_key]['debit']
|
|
totals_by_column_group[column_group_key]['credit'] += partner_values[column_group_key]['credit']
|
|
totals_by_column_group[column_group_key]['balance'] += partner_values[column_group_key]['balance']
|
|
|
|
lines.append(self._get_report_line_partners(options, partner, partner_values, level_shift=level_shift))
|
|
|
|
return lines, totals_by_column_group
|
|
|
|
def _report_expand_unfoldable_line_partner_ledger_prefix_group(self, line_dict_id, groupby, options, progress, offset, unfold_all_batch_data=None):
|
|
report = self.env['account.report'].browse(options['report_id'])
|
|
matched_prefix = report._get_prefix_groups_matched_prefix_from_line_id(line_dict_id)
|
|
|
|
prefix_domain = [('partner_id.name', '=ilike', f'{matched_prefix}%')]
|
|
if self._get_no_partner_line_label().upper().startswith(matched_prefix):
|
|
prefix_domain = expression.OR([prefix_domain, [('partner_id', '=', None)]])
|
|
|
|
expand_options = {
|
|
**options,
|
|
'forced_domain': options.get('forced_domain', []) + prefix_domain
|
|
}
|
|
parent_level = len(matched_prefix) * 2
|
|
partner_lines, dummy = self._build_partner_lines(report, expand_options, level_shift=parent_level)
|
|
|
|
for partner_line in partner_lines:
|
|
partner_line['id'] = report._build_subline_id(line_dict_id, partner_line['id'])
|
|
partner_line['parent_id'] = line_dict_id
|
|
|
|
lines = report._regroup_lines_by_name_prefix(
|
|
options,
|
|
partner_lines,
|
|
'_report_expand_unfoldable_line_partner_ledger_prefix_group',
|
|
parent_level,
|
|
matched_prefix=matched_prefix,
|
|
parent_line_dict_id=line_dict_id,
|
|
)
|
|
|
|
return {
|
|
'lines': lines,
|
|
'offset_increment': len(lines),
|
|
'has_more': False,
|
|
}
|
|
|
|
def _custom_options_initializer(self, report, options, previous_options=None):
|
|
super()._custom_options_initializer(report, options, previous_options=previous_options)
|
|
domain = []
|
|
|
|
company_ids = report.get_report_company_ids(options)
|
|
exch_code = self.env['res.company'].browse(company_ids).mapped('currency_exchange_journal_id')
|
|
if exch_code:
|
|
domain += ['!', '&', '&', '&', ('credit', '=', 0.0), ('debit', '=', 0.0), ('amount_currency', '!=', 0.0), ('journal_id', 'in', exch_code.ids)]
|
|
|
|
if options['export_mode'] == 'print' and options.get('filter_search_bar'):
|
|
domain += [
|
|
'|', ('matched_debit_ids.debit_move_id.partner_id.name', 'ilike', options['filter_search_bar']),
|
|
'|', ('matched_credit_ids.credit_move_id.partner_id.name', 'ilike', options['filter_search_bar']),
|
|
('partner_id.name', 'ilike', options['filter_search_bar']),
|
|
]
|
|
|
|
options['forced_domain'] = options.get('forced_domain', []) + domain
|
|
|
|
if self.user_has_groups('base.group_multi_currency'):
|
|
options['multi_currency'] = True
|
|
|
|
def _custom_unfold_all_batch_data_generator(self, report, options, lines_to_expand_by_function):
|
|
partner_ids_to_expand = []
|
|
|
|
# Regular case
|
|
for line_dict in lines_to_expand_by_function.get('_report_expand_unfoldable_line_partner_ledger', []):
|
|
markup, model, model_id = self.env['account.report']._parse_line_id(line_dict['id'])[-1]
|
|
if model == 'res.partner':
|
|
partner_ids_to_expand.append(model_id)
|
|
elif markup == 'no_partner':
|
|
partner_ids_to_expand.append(None)
|
|
|
|
# In case prefix groups are used
|
|
no_partner_line_label = self._get_no_partner_line_label().upper()
|
|
partner_prefix_domains = []
|
|
for line_dict in lines_to_expand_by_function.get('_report_expand_unfoldable_line_partner_ledger_prefix_group', []):
|
|
prefix = report._get_prefix_groups_matched_prefix_from_line_id(line_dict['id'])
|
|
partner_prefix_domains.append([('name', '=ilike', f'{prefix}%')])
|
|
|
|
# amls without partners are regrouped "Unknown Partner", which is also used to create prefix groups
|
|
if no_partner_line_label.startswith(prefix):
|
|
partner_ids_to_expand.append(None)
|
|
|
|
if partner_prefix_domains:
|
|
partner_ids_to_expand += self.env['res.partner'].with_context(active_test=False).search(expression.OR(partner_prefix_domains)).ids
|
|
|
|
return {
|
|
'initial_balances': self._get_initial_balance_values(partner_ids_to_expand, options) if partner_ids_to_expand else {},
|
|
|
|
# load_more_limit cannot be passed to this call, otherwise it won't be applied per partner but on the whole result.
|
|
# We gain perf from batching, but load every result, even if the limit restricts them later.
|
|
'aml_values': self._get_aml_values(options, partner_ids_to_expand) if partner_ids_to_expand else {},
|
|
}
|
|
|
|
@api.model
|
|
def action_open_partner(self, options, params):
|
|
dummy, record_id = self.env['account.report']._get_model_info_from_id(params['id'])
|
|
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'res.partner',
|
|
'res_id': record_id,
|
|
'views': [[False, 'form']],
|
|
'view_mode': 'form',
|
|
'target': 'current',
|
|
}
|
|
|
|
def _query_partners(self, options):
|
|
""" Executes the queries and performs all the computation.
|
|
:return: A list of tuple (partner, column_group_values) sorted by the table's model _order:
|
|
- partner is a res.parter record.
|
|
- column_group_values is a dict(column_group_key, fetched_values), where
|
|
- column_group_key is a string identifying a column group, like in options['column_groups']
|
|
- fetched_values is a dictionary containing:
|
|
- sum: {'debit': float, 'credit': float, 'balance': float}
|
|
- (optional) initial_balance: {'debit': float, 'credit': float, 'balance': float}
|
|
- (optional) lines: [line_vals_1, line_vals_2, ...]
|
|
"""
|
|
def assign_sum(row):
|
|
fields_to_assign = ['balance', 'debit', 'credit']
|
|
if any(not company_currency.is_zero(row[field]) for field in fields_to_assign):
|
|
groupby_partners.setdefault(row['groupby'], defaultdict(lambda: defaultdict(float)))
|
|
for field in fields_to_assign:
|
|
groupby_partners[row['groupby']][row['column_group_key']][field] += row[field]
|
|
|
|
company_currency = self.env.company.currency_id
|
|
|
|
# Execute the queries and dispatch the results.
|
|
query, params = self._get_query_sums(options)
|
|
|
|
groupby_partners = {}
|
|
|
|
self._cr.execute(query, params)
|
|
for res in self._cr.dictfetchall():
|
|
assign_sum(res)
|
|
|
|
# Correct the sums per partner, for the lines without partner reconciled with a line having a partner
|
|
query, params = self._get_sums_without_partner(options)
|
|
|
|
self._cr.execute(query, params)
|
|
totals = {}
|
|
for total_field in ['debit', 'credit', 'balance']:
|
|
totals[total_field] = {col_group_key: 0 for col_group_key in options['column_groups']}
|
|
|
|
for row in self._cr.dictfetchall():
|
|
totals['debit'][row['column_group_key']] += row['debit']
|
|
totals['credit'][row['column_group_key']] += row['credit']
|
|
totals['balance'][row['column_group_key']] += row['balance']
|
|
|
|
if row['groupby'] not in groupby_partners:
|
|
continue
|
|
|
|
assign_sum(row)
|
|
|
|
if None in groupby_partners:
|
|
# Debit/credit are inverted for the unknown partner as the computation is made regarding the balance of the known partner
|
|
for column_group_key in options['column_groups']:
|
|
groupby_partners[None][column_group_key]['debit'] += totals['credit'][column_group_key]
|
|
groupby_partners[None][column_group_key]['credit'] += totals['debit'][column_group_key]
|
|
groupby_partners[None][column_group_key]['balance'] -= totals['balance'][column_group_key]
|
|
|
|
# Retrieve the partners to browse.
|
|
# groupby_partners.keys() contains all account ids affected by:
|
|
# - the amls in the current period.
|
|
# - the amls affecting the initial balance.
|
|
if groupby_partners:
|
|
# Note a search is done instead of a browse to preserve the table ordering.
|
|
partners = self.env['res.partner'].with_context(active_test=False).search_fetch([('id', 'in', list(groupby_partners.keys()))], ["id", "name", "trust", "company_registry", "vat"])
|
|
else:
|
|
partners = []
|
|
|
|
# Add 'Partner Unknown' if needed
|
|
if None in groupby_partners.keys():
|
|
partners = [p for p in partners] + [None]
|
|
|
|
return [(partner, groupby_partners[partner.id if partner else None]) for partner in partners]
|
|
|
|
def _get_query_sums(self, options):
|
|
""" Construct a query retrieving all the aggregated sums to build the report. It includes:
|
|
- sums for all partners.
|
|
- sums for the initial balances.
|
|
:param options: The report options.
|
|
:return: (query, params)
|
|
"""
|
|
params = []
|
|
queries = []
|
|
report = self.env.ref('account_reports.partner_ledger_report')
|
|
|
|
# Create the currency table.
|
|
ct_query = report._get_query_currency_table(options)
|
|
for column_group_key, column_group_options in report._split_options_per_column_group(options).items():
|
|
tables, where_clause, where_params = report._query_get(column_group_options, 'normal')
|
|
params.append(column_group_key)
|
|
params += where_params
|
|
queries.append(f"""
|
|
SELECT
|
|
account_move_line.partner_id AS groupby,
|
|
%s AS column_group_key,
|
|
SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit,
|
|
SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit,
|
|
SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance
|
|
FROM {tables}
|
|
LEFT JOIN {ct_query} ON currency_table.company_id = account_move_line.company_id
|
|
WHERE {where_clause}
|
|
GROUP BY account_move_line.partner_id
|
|
""")
|
|
|
|
return ' UNION ALL '.join(queries), params
|
|
|
|
def _get_initial_balance_values(self, partner_ids, options):
|
|
queries = []
|
|
params = []
|
|
report = self.env.ref('account_reports.partner_ledger_report')
|
|
ct_query = report._get_query_currency_table(options)
|
|
for column_group_key, column_group_options in report._split_options_per_column_group(options).items():
|
|
# Get sums for the initial balance.
|
|
# period: [('date' <= options['date_from'] - 1)]
|
|
new_options = self._get_options_initial_balance(column_group_options)
|
|
tables, where_clause, where_params = report._query_get(new_options, 'normal', domain=[('partner_id', 'in', partner_ids)])
|
|
params.append(column_group_key)
|
|
params += where_params
|
|
queries.append(f"""
|
|
SELECT
|
|
account_move_line.partner_id,
|
|
%s AS column_group_key,
|
|
SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision)) AS debit,
|
|
SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision)) AS credit,
|
|
SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision)) AS balance
|
|
FROM {tables}
|
|
LEFT JOIN {ct_query} ON currency_table.company_id = account_move_line.company_id
|
|
WHERE {where_clause}
|
|
GROUP BY account_move_line.partner_id
|
|
""")
|
|
|
|
self._cr.execute(" UNION ALL ".join(queries), params)
|
|
|
|
init_balance_by_col_group = {
|
|
partner_id: {column_group_key: {} for column_group_key in options['column_groups']}
|
|
for partner_id in partner_ids
|
|
}
|
|
for result in self._cr.dictfetchall():
|
|
init_balance_by_col_group[result['partner_id']][result['column_group_key']] = result
|
|
|
|
return init_balance_by_col_group
|
|
|
|
def _get_options_initial_balance(self, options):
|
|
""" Create options used to compute the initial balances for each partner.
|
|
The resulting dates domain will be:
|
|
[('date' <= options['date_from'] - 1)]
|
|
:param options: The report options.
|
|
:return: A copy of the options, modified to match the dates to use to get the initial balances.
|
|
"""
|
|
new_date_to = fields.Date.from_string(options['date']['date_from']) - timedelta(days=1)
|
|
new_date_options = dict(options['date'], date_from=False, date_to=fields.Date.to_string(new_date_to))
|
|
return dict(options, date=new_date_options)
|
|
|
|
def _get_sums_without_partner(self, options):
|
|
""" Get the sum of lines without partner reconciled with a line with a partner, grouped by partner. Those lines
|
|
should be considered as belonging to the partner for the reconciled amount as it may clear some of the partner
|
|
invoice/bill and they have to be accounted in the partner balance."""
|
|
queries = []
|
|
params = []
|
|
report = self.env.ref('account_reports.partner_ledger_report')
|
|
ct_query = report._get_query_currency_table(options)
|
|
for column_group_key, column_group_options in report._split_options_per_column_group(options).items():
|
|
tables, where_clause, where_params = report._query_get(column_group_options, 'normal')
|
|
params += [
|
|
column_group_key,
|
|
column_group_options['date']['date_to'],
|
|
*where_params,
|
|
]
|
|
queries.append(f"""
|
|
SELECT
|
|
%s AS column_group_key,
|
|
aml_with_partner.partner_id AS groupby,
|
|
COALESCE(SUM(CASE WHEN aml_with_partner.balance > 0 THEN 0 ELSE ROUND(
|
|
partial.amount * currency_table.rate, currency_table.precision) END), 0) AS debit,
|
|
COALESCE(SUM(CASE WHEN aml_with_partner.balance < 0 THEN 0 ELSE ROUND(
|
|
partial.amount * currency_table.rate, currency_table.precision) END), 0) AS credit,
|
|
COALESCE(SUM(- sign(aml_with_partner.balance) * ROUND(
|
|
partial.amount * currency_table.rate, currency_table.precision)), 0) AS balance
|
|
FROM {tables}
|
|
JOIN account_partial_reconcile partial
|
|
ON account_move_line.id = partial.debit_move_id OR account_move_line.id = partial.credit_move_id
|
|
JOIN account_move_line aml_with_partner ON
|
|
(aml_with_partner.id = partial.debit_move_id OR aml_with_partner.id = partial.credit_move_id)
|
|
AND aml_with_partner.partner_id IS NOT NULL
|
|
LEFT JOIN {ct_query} ON currency_table.company_id = account_move_line.company_id
|
|
WHERE partial.max_date <= %s AND {where_clause}
|
|
AND account_move_line.partner_id IS NULL
|
|
GROUP BY aml_with_partner.partner_id
|
|
""")
|
|
|
|
return " UNION ALL ".join(queries), params
|
|
|
|
def _report_expand_unfoldable_line_partner_ledger(self, line_dict_id, groupby, options, progress, offset, unfold_all_batch_data=None):
|
|
def init_load_more_progress(line_dict):
|
|
return {
|
|
column['column_group_key']: line_col.get('no_format', 0)
|
|
for column, line_col in zip(options['columns'], line_dict['columns'])
|
|
if column['expression_label'] == 'balance'
|
|
}
|
|
|
|
report = self.env.ref('account_reports.partner_ledger_report')
|
|
markup, model, record_id = report._parse_line_id(line_dict_id)[-1]
|
|
|
|
if model != 'res.partner':
|
|
raise UserError(_("Wrong ID for partner ledger line to expand: %s", line_dict_id))
|
|
|
|
prefix_groups_count = 0
|
|
for markup, dummy1, dummy2 in report._parse_line_id(line_dict_id):
|
|
if markup.startswith('groupby_prefix_group:'):
|
|
prefix_groups_count += 1
|
|
level_shift = prefix_groups_count * 2
|
|
|
|
lines = []
|
|
|
|
# Get initial balance
|
|
if offset == 0:
|
|
if unfold_all_batch_data:
|
|
init_balance_by_col_group = unfold_all_batch_data['initial_balances'][record_id]
|
|
else:
|
|
init_balance_by_col_group = self._get_initial_balance_values([record_id], options)[record_id]
|
|
initial_balance_line = report._get_partner_and_general_ledger_initial_balance_line(options, line_dict_id, init_balance_by_col_group, level_shift=level_shift)
|
|
if initial_balance_line:
|
|
lines.append(initial_balance_line)
|
|
|
|
# For the first expansion of the line, the initial balance line gives the progress
|
|
progress = init_load_more_progress(initial_balance_line)
|
|
|
|
limit_to_load = report.load_more_limit + 1 if report.load_more_limit and options['export_mode'] != 'print' else None
|
|
|
|
if unfold_all_batch_data:
|
|
aml_results = unfold_all_batch_data['aml_values'][record_id]
|
|
else:
|
|
aml_results = self._get_aml_values(options, [record_id], offset=offset, limit=limit_to_load)[record_id]
|
|
|
|
has_more = False
|
|
treated_results_count = 0
|
|
next_progress = progress
|
|
for result in aml_results:
|
|
if options['export_mode'] != 'print' and report.load_more_limit and treated_results_count == report.load_more_limit:
|
|
# We loaded one more than the limit on purpose: this way we know we need a "load more" line
|
|
has_more = True
|
|
break
|
|
|
|
new_line = self._get_report_line_move_line(options, result, line_dict_id, next_progress, level_shift=level_shift)
|
|
lines.append(new_line)
|
|
next_progress = init_load_more_progress(new_line)
|
|
treated_results_count += 1
|
|
|
|
return {
|
|
'lines': lines,
|
|
'offset_increment': treated_results_count,
|
|
'has_more': has_more,
|
|
'progress': next_progress
|
|
}
|
|
|
|
def _get_additional_column_aml_values(self):
|
|
"""
|
|
Allows customization of additional fields in the partner ledger query.
|
|
|
|
This method is intended to be overridden by other modules to add custom fields
|
|
to the partner ledger query. By default, it returns an empty string.
|
|
"""
|
|
return ""
|
|
|
|
def _get_aml_values(self, options, partner_ids, offset=0, limit=None):
|
|
rslt = {partner_id: [] for partner_id in partner_ids}
|
|
|
|
partner_ids_wo_none = [x for x in partner_ids if x]
|
|
directly_linked_aml_partner_clauses = []
|
|
directly_linked_aml_partner_params = []
|
|
indirectly_linked_aml_partner_params = []
|
|
indirectly_linked_aml_partner_clause = 'aml_with_partner.partner_id IS NOT NULL'
|
|
if None in partner_ids:
|
|
directly_linked_aml_partner_clauses.append('account_move_line.partner_id IS NULL')
|
|
if partner_ids_wo_none:
|
|
directly_linked_aml_partner_clauses.append('account_move_line.partner_id IN %s')
|
|
directly_linked_aml_partner_params.append(tuple(partner_ids_wo_none))
|
|
indirectly_linked_aml_partner_clause = 'aml_with_partner.partner_id IN %s'
|
|
indirectly_linked_aml_partner_params.append(tuple(partner_ids_wo_none))
|
|
directly_linked_aml_partner_clause = '(' + ' OR '.join(directly_linked_aml_partner_clauses) + ')'
|
|
|
|
ct_query = self.env['account.report']._get_query_currency_table(options)
|
|
queries = []
|
|
all_params = []
|
|
lang = self.env.lang or get_lang(self.env).code
|
|
journal_name = f"COALESCE(journal.name->>'{lang}', journal.name->>'en_US')" if \
|
|
self.pool['account.journal'].name.translate else 'journal.name'
|
|
account_name = f"COALESCE(account.name->>'{lang}', account.name->>'en_US')" if \
|
|
self.pool['account.account'].name.translate else 'account.name'
|
|
report = self.env.ref('account_reports.partner_ledger_report')
|
|
additional_columns = self._get_additional_column_aml_values()
|
|
for column_group_key, group_options in report._split_options_per_column_group(options).items():
|
|
tables, where_clause, where_params = report._query_get(group_options, 'strict_range')
|
|
|
|
all_params += [
|
|
column_group_key,
|
|
*where_params,
|
|
*directly_linked_aml_partner_params,
|
|
column_group_key,
|
|
*indirectly_linked_aml_partner_params,
|
|
*where_params,
|
|
group_options['date']['date_from'],
|
|
group_options['date']['date_to'],
|
|
]
|
|
|
|
# For the move lines directly linked to this partner
|
|
queries.append(f'''
|
|
SELECT
|
|
account_move_line.id,
|
|
account_move_line.date_maturity,
|
|
account_move_line.name,
|
|
account_move_line.ref,
|
|
account_move_line.company_id,
|
|
account_move_line.account_id,
|
|
account_move_line.payment_id,
|
|
account_move_line.partner_id,
|
|
account_move_line.currency_id,
|
|
account_move_line.amount_currency,
|
|
account_move_line.matching_number,
|
|
{additional_columns}
|
|
COALESCE(account_move_line.invoice_date, account_move_line.date) AS invoice_date,
|
|
ROUND(account_move_line.debit * currency_table.rate, currency_table.precision) AS debit,
|
|
ROUND(account_move_line.credit * currency_table.rate, currency_table.precision) AS credit,
|
|
ROUND(account_move_line.balance * currency_table.rate, currency_table.precision) AS balance,
|
|
account_move.name AS move_name,
|
|
account_move.move_type AS move_type,
|
|
account.code AS account_code,
|
|
{account_name} AS account_name,
|
|
journal.code AS journal_code,
|
|
{journal_name} AS journal_name,
|
|
%s AS column_group_key,
|
|
'directly_linked_aml' AS key,
|
|
0 AS partial_id
|
|
FROM {tables}
|
|
JOIN account_move ON account_move.id = account_move_line.move_id
|
|
LEFT JOIN {ct_query} ON currency_table.company_id = account_move_line.company_id
|
|
LEFT JOIN res_company company ON company.id = account_move_line.company_id
|
|
LEFT JOIN res_partner partner ON partner.id = account_move_line.partner_id
|
|
LEFT JOIN account_account account ON account.id = account_move_line.account_id
|
|
LEFT JOIN account_journal journal ON journal.id = account_move_line.journal_id
|
|
WHERE {where_clause} AND {directly_linked_aml_partner_clause}
|
|
ORDER BY account_move_line.date, account_move_line.id
|
|
''')
|
|
|
|
# For the move lines linked to no partner, but reconciled with this partner. They will appear in grey in the report
|
|
queries.append(f'''
|
|
SELECT
|
|
account_move_line.id,
|
|
account_move_line.date_maturity,
|
|
account_move_line.name,
|
|
account_move_line.ref,
|
|
account_move_line.company_id,
|
|
account_move_line.account_id,
|
|
account_move_line.payment_id,
|
|
aml_with_partner.partner_id,
|
|
account_move_line.currency_id,
|
|
account_move_line.amount_currency,
|
|
account_move_line.matching_number,
|
|
{additional_columns}
|
|
COALESCE(account_move_line.invoice_date, account_move_line.date) AS invoice_date,
|
|
CASE WHEN aml_with_partner.balance > 0 THEN 0 ELSE ROUND(
|
|
partial.amount * currency_table.rate, currency_table.precision
|
|
) END AS debit,
|
|
CASE WHEN aml_with_partner.balance < 0 THEN 0 ELSE ROUND(
|
|
partial.amount * currency_table.rate, currency_table.precision
|
|
) END AS credit,
|
|
- sign(aml_with_partner.balance) * ROUND(
|
|
partial.amount * currency_table.rate, currency_table.precision
|
|
) AS balance,
|
|
account_move.name AS move_name,
|
|
account_move.move_type AS move_type,
|
|
account.code AS account_code,
|
|
{account_name} AS account_name,
|
|
journal.code AS journal_code,
|
|
{journal_name} AS journal_name,
|
|
%s AS column_group_key,
|
|
'indirectly_linked_aml' AS key,
|
|
partial.id AS partial_id
|
|
FROM {tables}
|
|
LEFT JOIN {ct_query} ON currency_table.company_id = account_move_line.company_id,
|
|
account_partial_reconcile partial,
|
|
account_move,
|
|
account_move_line aml_with_partner,
|
|
account_journal journal,
|
|
account_account account
|
|
WHERE
|
|
(account_move_line.id = partial.debit_move_id OR account_move_line.id = partial.credit_move_id)
|
|
AND account_move_line.partner_id IS NULL
|
|
AND account_move.id = account_move_line.move_id
|
|
AND (aml_with_partner.id = partial.debit_move_id OR aml_with_partner.id = partial.credit_move_id)
|
|
AND {indirectly_linked_aml_partner_clause}
|
|
AND journal.id = account_move_line.journal_id
|
|
AND account.id = account_move_line.account_id
|
|
AND {where_clause}
|
|
AND partial.max_date BETWEEN %s AND %s
|
|
ORDER BY account_move_line.date, account_move_line.id
|
|
''')
|
|
|
|
query = '(' + ') UNION ALL ('.join(queries) + ')'
|
|
|
|
if offset:
|
|
query += ' OFFSET %s '
|
|
all_params.append(offset)
|
|
|
|
if limit:
|
|
query += ' LIMIT %s '
|
|
all_params.append(limit)
|
|
|
|
self._cr.execute(query, all_params)
|
|
for aml_result in self._cr.dictfetchall():
|
|
if aml_result['key'] == 'indirectly_linked_aml':
|
|
|
|
# Append the line to the partner found through the reconciliation.
|
|
if aml_result['partner_id'] in rslt:
|
|
rslt[aml_result['partner_id']].append(aml_result)
|
|
|
|
# Balance it with an additional line in the Unknown Partner section but having reversed amounts.
|
|
if None in rslt:
|
|
rslt[None].append({
|
|
**aml_result,
|
|
'debit': aml_result['credit'],
|
|
'credit': aml_result['debit'],
|
|
'balance': -aml_result['balance'],
|
|
})
|
|
else:
|
|
rslt[aml_result['partner_id']].append(aml_result)
|
|
|
|
return rslt
|
|
|
|
####################################################
|
|
# COLUMNS/LINES
|
|
####################################################
|
|
def _get_report_line_partners(self, options, partner, partner_values, level_shift=0):
|
|
company_currency = self.env.company.currency_id
|
|
|
|
unfoldable = False
|
|
column_values = []
|
|
report = self.env['account.report'].browse(options['report_id'])
|
|
for column in options['columns']:
|
|
col_expr_label = column['expression_label']
|
|
value = partner_values[column['column_group_key']].get(col_expr_label)
|
|
unfoldable = unfoldable or (col_expr_label in ('debit', 'credit') and not company_currency.is_zero(value))
|
|
column_values.append(report._build_column_dict(value, column, options=options))
|
|
|
|
|
|
line_id = report._get_generic_line_id('res.partner', partner.id) if partner else report._get_generic_line_id('res.partner', None, markup='no_partner')
|
|
|
|
return {
|
|
'id': line_id,
|
|
'name': partner is not None and (partner.name or '')[:128] or self._get_no_partner_line_label(),
|
|
'columns': column_values,
|
|
'level': 1 + level_shift,
|
|
'trust': partner.trust if partner else None,
|
|
'unfoldable': unfoldable,
|
|
'unfolded': line_id in options['unfolded_lines'] or options['unfold_all'],
|
|
'expand_function': '_report_expand_unfoldable_line_partner_ledger',
|
|
}
|
|
|
|
def _get_no_partner_line_label(self):
|
|
return _('Unknown Partner')
|
|
|
|
@api.model
|
|
def _format_aml_name(self, line_name, move_ref, move_name=None):
|
|
''' Format the display of an account.move.line record. As its very costly to fetch the account.move.line
|
|
records, only line_name, move_ref, move_name are passed as parameters to deal with sql-queries more easily.
|
|
|
|
:param line_name: The name of the account.move.line record.
|
|
:param move_ref: The reference of the account.move record.
|
|
:param move_name: The name of the account.move record.
|
|
:return: The formatted name of the account.move.line record.
|
|
'''
|
|
names = []
|
|
if move_name is not None and move_name != '/':
|
|
names.append(move_name)
|
|
if move_ref and move_ref != '/':
|
|
names.append(move_ref)
|
|
if line_name and line_name != move_name and line_name != '/':
|
|
names.append(line_name)
|
|
name = ' - '.join(names)
|
|
return name
|
|
|
|
def _get_report_line_move_line(self, options, aml_query_result, partner_line_id, init_bal_by_col_group, level_shift=0):
|
|
if aml_query_result['payment_id']:
|
|
caret_type = 'account.payment'
|
|
else:
|
|
caret_type = 'account.move.line'
|
|
|
|
columns = []
|
|
report = self.env['account.report'].browse(options['report_id'])
|
|
for column in options['columns']:
|
|
col_expr_label = column['expression_label']
|
|
col_value = aml_query_result[col_expr_label] if column['column_group_key'] == aml_query_result['column_group_key'] else None
|
|
|
|
if col_value is None:
|
|
columns.append(report._build_column_dict(None, None))
|
|
else:
|
|
currency = False
|
|
|
|
if col_expr_label == 'balance':
|
|
col_value += init_bal_by_col_group[column['column_group_key']]
|
|
|
|
if col_expr_label == 'amount_currency':
|
|
currency = self.env['res.currency'].browse(aml_query_result['currency_id'])
|
|
|
|
if currency == self.env.company.currency_id:
|
|
col_value = ''
|
|
|
|
columns.append(report._build_column_dict(col_value, column, options=options, currency=currency))
|
|
|
|
return {
|
|
'id': report._get_generic_line_id('account.move.line', aml_query_result['id'], parent_line_id=partner_line_id, markup=aml_query_result['partial_id']),
|
|
'parent_id': partner_line_id,
|
|
'name': self._format_aml_name(aml_query_result['name'], aml_query_result['ref'], aml_query_result['move_name']),
|
|
'columns': columns,
|
|
'caret_options': caret_type,
|
|
'level': 3 + level_shift,
|
|
}
|
|
|
|
def _get_report_line_total(self, options, totals_by_column_group):
|
|
column_values = []
|
|
report = self.env['account.report'].browse(options['report_id'])
|
|
for column in options['columns']:
|
|
col_value = totals_by_column_group[column['column_group_key']].get(column['expression_label'])
|
|
column_values.append(report._build_column_dict(col_value, column, options=options))
|
|
|
|
return {
|
|
'id': report._get_generic_line_id(None, None, markup='total'),
|
|
'name': _('Total'),
|
|
'level': 1,
|
|
'columns': column_values,
|
|
}
|
|
|
|
def open_journal_items(self, options, params):
|
|
params['view_ref'] = 'account.view_move_line_tree_grouped_partner'
|
|
report = self.env['account.report'].browse(options['report_id'])
|
|
action = report.open_journal_items(options=options, params=params)
|
|
action.get('context', {}).update({'search_default_group_by_account': 0, 'search_default_group_by_partner': 1})
|
|
return action
|