add a new module for aged partner balance reports, NA style
This commit is contained in:
parent
137789cd79
commit
a050ce6221
7 changed files with 323 additions and 2 deletions
10
aged_partner_balance_na/__init__.py
Normal file
10
aged_partner_balance_na/__init__.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from . import models
|
||||
|
||||
|
||||
def post_init(env):
|
||||
new_receivable_report = env.ref('account_reports.aged_receivable_report').copy()
|
||||
new_payable_report = env.ref('account_reports.aged_payable_report').copy()
|
||||
new_receivable_report.line_ids.mapped('expression_ids').write({
|
||||
'formula': '_report_custom_engine_aged_receivable_na'
|
||||
})
|
||||
new_payable_report.line_ids.mapped('expression_ids').write({'formula': '_report_custom_engine_aged_payable_na'})
|
||||
32
aged_partner_balance_na/__manifest__.py
Normal file
32
aged_partner_balance_na/__manifest__.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Bemade Inc.
|
||||
#
|
||||
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||
#
|
||||
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
||||
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||
# or modified copies of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
{
|
||||
'name': 'Aged Partner Balance (North American Style)',
|
||||
'version': '17.0.1.0.0',
|
||||
'summary': 'Present aged partner balance as predictive rather than past due.',
|
||||
'category': 'Accounting',
|
||||
'author': 'Bemade Inc.',
|
||||
'website': 'http://www.bemade.org',
|
||||
'license': 'LGPL-3',
|
||||
'depends': ['account_reports'],
|
||||
'assets': {},
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'post_init_hook': 'post_init',
|
||||
}
|
||||
1
aged_partner_balance_na/models/__init__.py
Normal file
1
aged_partner_balance_na/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import aged_partner_balance
|
||||
242
aged_partner_balance_na/models/aged_partner_balance.py
Normal file
242
aged_partner_balance_na/models/aged_partner_balance.py
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
from odoo import models, fields
|
||||
from itertools import chain
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
|
||||
class AgedPartnerBalanceCustomHandler(models.AbstractModel):
|
||||
_inherit = "account.aged.partner.balance.report.handler"
|
||||
|
||||
def _report_custom_engine_aged_receivable_na(
|
||||
self,
|
||||
expressions,
|
||||
options,
|
||||
date_scope,
|
||||
current_groupby,
|
||||
next_groupby,
|
||||
offset=0,
|
||||
limit=None,
|
||||
warnings=None
|
||||
):
|
||||
return self._aged_partner_report_custom_engine_common_na(
|
||||
options,
|
||||
'asset_receivable',
|
||||
current_groupby,
|
||||
next_groupby,
|
||||
offset=offset,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
def _report_custom_engine_aged_payable_na(
|
||||
self,
|
||||
expressions,
|
||||
options,
|
||||
date_scope,
|
||||
current_groupby,
|
||||
next_groupby,
|
||||
offset=0,
|
||||
limit=None,
|
||||
warnings=None
|
||||
):
|
||||
return self._aged_partner_report_custom_engine_common_na(
|
||||
options,
|
||||
'liability_payable',
|
||||
current_groupby,
|
||||
next_groupby,
|
||||
offset=offset,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
def _aged_partner_report_custom_engine_common_na(self, options, internal_type, current_groupby, next_groupby, offset=0, limit=None):
|
||||
report = self.env['account.report'].browse(options['report_id'])
|
||||
report._check_groupby_fields((next_groupby.split(',') if next_groupby else []) + ([current_groupby] if current_groupby else []))
|
||||
|
||||
def plus_days(date_obj, days):
|
||||
return fields.Date.to_string(date_obj - relativedelta(days=days))
|
||||
|
||||
date_to = fields.Date.from_string(options['date']['date_to'])
|
||||
periods = [
|
||||
(False, fields.Date.to_string(date_to)),
|
||||
(date_to, plus_days(date_to, 29)),
|
||||
(plus_days(date_to, 30), plus_days(date_to, 59)),
|
||||
(plus_days(date_to, 60), plus_days(date_to, 89)),
|
||||
(plus_days(date_to, 90), plus_days(date_to, 119)),
|
||||
(plus_days(date_to, 120), False),
|
||||
]
|
||||
|
||||
def build_result_dict(report, query_res_lines):
|
||||
rslt = {f'period{i}': 0 for i in range(len(periods))}
|
||||
|
||||
for query_res in query_res_lines:
|
||||
for i in range(len(periods)):
|
||||
period_key = f'period{i}'
|
||||
rslt[period_key] += query_res[period_key]
|
||||
|
||||
if current_groupby == 'id':
|
||||
query_res = query_res_lines[0] # We're grouping by id, so there is only 1 element in query_res_lines anyway
|
||||
currency = self.env['res.currency'].browse(query_res['currency_id'][0]) if len(query_res['currency_id']) == 1 else None
|
||||
expected_date = len(query_res['expected_date']) == 1 and query_res['expected_date'][0] or len(query_res['due_date']) == 1 and query_res['due_date'][0]
|
||||
rslt.update({
|
||||
'invoice_date': query_res['invoice_date'][0] if len(query_res['invoice_date']) == 1 else None,
|
||||
'due_date': query_res['due_date'][0] if len(query_res['due_date']) == 1 else None,
|
||||
'amount_currency': query_res['amount_currency'],
|
||||
'currency_id': query_res['currency_id'][0] if len(query_res['currency_id']) == 1 else None,
|
||||
'currency': currency.display_name if currency else None,
|
||||
'account_name': query_res['account_name'][0] if len(query_res['account_name']) == 1 else None,
|
||||
'expected_date': expected_date or None,
|
||||
'total': None,
|
||||
'has_sublines': query_res['aml_count'] > 0,
|
||||
|
||||
# Needed by the custom_unfold_all_batch_data_generator, to speed-up unfold_all
|
||||
'partner_id': query_res['partner_id'][0] if query_res['partner_id'] else None,
|
||||
})
|
||||
else:
|
||||
rslt.update({
|
||||
'invoice_date': None,
|
||||
'due_date': None,
|
||||
'amount_currency': None,
|
||||
'currency_id': None,
|
||||
'currency': None,
|
||||
'account_name': None,
|
||||
'expected_date': None,
|
||||
'total': sum(rslt[f'period{i}'] for i in range(len(periods))),
|
||||
'has_sublines': False,
|
||||
})
|
||||
|
||||
return rslt
|
||||
|
||||
# Build period table
|
||||
period_table_format = ('(VALUES %s)' % ','.join("(%s, %s, %s)" for period in periods))
|
||||
params = list(chain.from_iterable(
|
||||
(period[0] or None, period[1] or None, i)
|
||||
for i, period in enumerate(periods)
|
||||
))
|
||||
period_table = self.env.cr.mogrify(period_table_format, params).decode(self.env.cr.connection.encoding)
|
||||
|
||||
# Build query
|
||||
tables, where_clause, where_params = report._query_get(options, 'strict_range', domain=[('account_id.account_type', '=', internal_type)])
|
||||
|
||||
currency_table = report._get_query_currency_table(options)
|
||||
always_present_groupby = "period_table.period_index, currency_table.rate, currency_table.precision"
|
||||
if current_groupby:
|
||||
select_from_groupby = f"account_move_line.{current_groupby} AS grouping_key,"
|
||||
groupby_clause = f"account_move_line.{current_groupby}, {always_present_groupby}"
|
||||
else:
|
||||
select_from_groupby = ''
|
||||
groupby_clause = always_present_groupby
|
||||
select_period_query = ','.join(
|
||||
f"""
|
||||
CASE WHEN period_table.period_index = {i}
|
||||
THEN %s * (
|
||||
SUM(ROUND(account_move_line.balance * currency_table.rate, currency_table.precision))
|
||||
- COALESCE(SUM(ROUND(part_debit.amount * currency_table.rate, currency_table.precision)), 0)
|
||||
+ COALESCE(SUM(ROUND(part_credit.amount * currency_table.rate, currency_table.precision)), 0)
|
||||
)
|
||||
ELSE 5 END AS period{i}
|
||||
"""
|
||||
for i in range(len(periods))
|
||||
)
|
||||
|
||||
tail_query, tail_params = report._get_engine_query_tail(offset, limit)
|
||||
query = f"""
|
||||
WITH period_table(date_start, date_stop, period_index) AS ({period_table})
|
||||
|
||||
SELECT
|
||||
{select_from_groupby}
|
||||
%s * (
|
||||
SUM(account_move_line.amount_currency)
|
||||
- COALESCE(SUM(part_debit.debit_amount_currency), 0)
|
||||
+ COALESCE(SUM(part_credit.credit_amount_currency), 0)
|
||||
) AS amount_currency,
|
||||
ARRAY_AGG(DISTINCT account_move_line.partner_id) AS partner_id,
|
||||
ARRAY_AGG(account_move_line.payment_id) AS payment_id,
|
||||
ARRAY_AGG(DISTINCT move.invoice_date) AS invoice_date,
|
||||
ARRAY_AGG(DISTINCT COALESCE(account_move_line.date_maturity, account_move_line.date)) AS report_date,
|
||||
ARRAY_AGG(DISTINCT account_move_line.expected_pay_date) AS expected_date,
|
||||
ARRAY_AGG(DISTINCT account.code) AS account_name,
|
||||
ARRAY_AGG(DISTINCT COALESCE(account_move_line.date_maturity, account_move_line.date)) AS due_date,
|
||||
ARRAY_AGG(DISTINCT account_move_line.currency_id) AS currency_id,
|
||||
COUNT(account_move_line.id) AS aml_count,
|
||||
ARRAY_AGG(account.code) AS account_code,
|
||||
{select_period_query}
|
||||
|
||||
FROM {tables}
|
||||
|
||||
JOIN account_journal journal ON journal.id = account_move_line.journal_id
|
||||
JOIN account_account account ON account.id = account_move_line.account_id
|
||||
JOIN account_move move ON move.id = account_move_line.move_id
|
||||
JOIN {currency_table} ON currency_table.company_id = account_move_line.company_id
|
||||
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
SUM(part.amount) AS amount,
|
||||
SUM(part.debit_amount_currency) AS debit_amount_currency,
|
||||
part.debit_move_id
|
||||
FROM account_partial_reconcile part
|
||||
WHERE part.max_date <= %s AND part.debit_move_id = account_move_line.id
|
||||
GROUP BY part.debit_move_id
|
||||
) part_debit ON TRUE
|
||||
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
SUM(part.amount) AS amount,
|
||||
SUM(part.credit_amount_currency) AS credit_amount_currency,
|
||||
part.credit_move_id
|
||||
FROM account_partial_reconcile part
|
||||
WHERE part.max_date <= %s AND part.credit_move_id = account_move_line.id
|
||||
GROUP BY part.credit_move_id
|
||||
) part_credit ON TRUE
|
||||
|
||||
JOIN period_table ON
|
||||
(
|
||||
period_table.date_start IS NULL
|
||||
OR COALESCE(account_move_line.date_maturity, account_move_line.date) <= DATE(period_table.date_start)
|
||||
)
|
||||
AND
|
||||
(
|
||||
period_table.date_stop IS NULL
|
||||
OR COALESCE(account_move_line.date_maturity, account_move_line.date) >= DATE(period_table.date_stop)
|
||||
)
|
||||
|
||||
WHERE {where_clause}
|
||||
|
||||
GROUP BY {groupby_clause}
|
||||
|
||||
HAVING
|
||||
(
|
||||
SUM(ROUND(account_move_line.debit * currency_table.rate, currency_table.precision))
|
||||
- COALESCE(SUM(ROUND(part_debit.amount * currency_table.rate, currency_table.precision)), 0)
|
||||
) != 0
|
||||
OR
|
||||
(
|
||||
SUM(ROUND(account_move_line.credit * currency_table.rate, currency_table.precision))
|
||||
- COALESCE(SUM(ROUND(part_credit.amount * currency_table.rate, currency_table.precision)), 0)
|
||||
) != 0
|
||||
{tail_query}
|
||||
"""
|
||||
|
||||
multiplicator = -1 if internal_type == 'liability_payable' else 1
|
||||
params = [
|
||||
multiplicator,
|
||||
*([multiplicator] * len(periods)),
|
||||
date_to,
|
||||
date_to,
|
||||
*where_params,
|
||||
*tail_params,
|
||||
]
|
||||
self._cr.execute(query, params)
|
||||
query_res_lines = self._cr.dictfetchall()
|
||||
|
||||
if not current_groupby:
|
||||
return build_result_dict(report, query_res_lines)
|
||||
else:
|
||||
rslt = []
|
||||
|
||||
all_res_per_grouping_key = {}
|
||||
for query_res in query_res_lines:
|
||||
grouping_key = query_res['grouping_key']
|
||||
all_res_per_grouping_key.setdefault(grouping_key, []).append(query_res)
|
||||
|
||||
for grouping_key, query_res_lines in all_res_per_grouping_key.items():
|
||||
rslt.append((grouping_key, build_result_dict(report, query_res_lines)))
|
||||
|
||||
return rslt
|
||||
|
|
@ -7,10 +7,14 @@
|
|||
<field name="arch" type="xml">
|
||||
<field name="delivery_package_type_id" position="replace">
|
||||
<field invisible="1" name="auto_create_package"/>
|
||||
<field context="{'form_view_ref':'stock.stock_package_type_form'}" domain="[('package_carrier_type', '=', context.get('current_package_carrier_type', 'none'))]" invisible="auto_create_package == True" name="delivery_package_type_id"/>
|
||||
<field context="{'form_view_ref':'stock.stock_package_type_form'}"
|
||||
domain="[('package_carrier_type', '=', context.get('current_package_carrier_type', 'none'))]"
|
||||
invisible="auto_create_package == True" name="delivery_package_type_id"/>
|
||||
<field invisible="auto_create_package == False" name="length"/>
|
||||
<field invisible="auto_create_package == False" name="width"/>
|
||||
<field invisible="auto_create_package == False" name="height"/>
|
||||
<field invisible="1" name="provider"/></field></field>
|
||||
<field invisible="1" name="provider"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
0
multi_account_statement_import/__init__.py
Normal file
0
multi_account_statement_import/__init__.py
Normal file
32
multi_account_statement_import/__manifest__.py
Normal file
32
multi_account_statement_import/__manifest__.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Bemade Inc.
|
||||
#
|
||||
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||
#
|
||||
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
||||
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||
# or modified copies of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
{
|
||||
'name': 'Multi Account Statement Import',
|
||||
'version': '17.0.0.1.0',
|
||||
'summary': 'Import Bank Statements for Multiple Accounts',
|
||||
'category': 'Accounting/Accounting',
|
||||
'author': 'Bemade Inc.',
|
||||
'website': 'http://www.bemade.org',
|
||||
'license': 'LGPL-3',
|
||||
'depends': ['account_bank_statement_import'],
|
||||
'data': [],
|
||||
'assets': {},
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
Loading…
Reference in a new issue