diff --git a/account_financial_report/report/abstract_report_xlsx.py b/account_financial_report/report/abstract_report_xlsx.py
index 886538e8..eacea35d 100644
--- a/account_financial_report/report/abstract_report_xlsx.py
+++ b/account_financial_report/report/abstract_report_xlsx.py
@@ -21,6 +21,7 @@ class AbstractReportXslx(models.AbstractModel):
# Formats
self.format_right = None
+ self.format_left = None
self.format_right_bold_italic = None
self.format_bold = None
self.format_header_left = None
@@ -41,6 +42,7 @@ class AbstractReportXslx(models.AbstractModel):
self._define_formats(workbook)
report_name = self._get_report_name()
+ report_footer = self._get_report_footer()
filters = self._get_report_filters(report)
self.columns = self._get_report_columns(report)
self.workbook = workbook
@@ -54,6 +56,8 @@ class AbstractReportXslx(models.AbstractModel):
self._generate_report_content(workbook, report)
+ self._write_report_footer(report_footer)
+
def _define_formats(self, workbook):
""" Add cell formats to current workbook.
Those formats can be used on all cell.
@@ -71,6 +75,7 @@ class AbstractReportXslx(models.AbstractModel):
"""
self.format_bold = workbook.add_format({'bold': True})
self.format_right = workbook.add_format({'align': 'right'})
+ self.format_left = workbook.add_format({'align': 'left'})
self.format_right_bold_italic = workbook.add_format(
{'align': 'right', 'bold': True, 'italic': True}
)
@@ -120,6 +125,18 @@ class AbstractReportXslx(models.AbstractModel):
)
self.row_pos += 3
+ def _write_report_footer(self, footer):
+ """Write report footer .
+ Columns are defined with `_get_report_columns` method.
+ """
+ if footer:
+ self.row_pos += 1
+ self.sheet.merge_range(
+ self.row_pos, 0, self.row_pos, len(self.columns) - 1,
+ footer, self.format_left
+ )
+ self.row_pos += 1
+
def _write_filters(self, filters):
"""Write one line per filters on starting on current line.
Columns number for filter name is defined
@@ -322,6 +339,13 @@ class AbstractReportXslx(models.AbstractModel):
"""
raise NotImplementedError()
+ def _get_report_footer(self):
+ """
+ Allow to define the report footer.
+ :return: the report footer
+ """
+ return False
+
def _get_report_columns(self, report):
"""
Allow to define the report columns
diff --git a/account_financial_report/report/templates/trial_balance.xml b/account_financial_report/report/templates/trial_balance.xml
index 3028cf5d..011cd89c 100644
--- a/account_financial_report/report/templates/trial_balance.xml
+++ b/account_financial_report/report/templates/trial_balance.xml
@@ -104,9 +104,8 @@
diff --git a/account_financial_report/report/trial_balance.py b/account_financial_report/report/trial_balance.py
index 1f7cb30b..02d65e59 100644
--- a/account_financial_report/report/trial_balance.py
+++ b/account_financial_report/report/trial_balance.py
@@ -317,6 +317,10 @@ WHERE
('company_id', '=', self.company_id.id)
])
+ if self.filter_account_ids and unaffected_earnings_account not in \
+ self.filter_account_ids:
+ return True
+
query_unaffected_earnings_account_ids = """
SELECT a.id
FROM account_account as a
@@ -332,11 +336,17 @@ WHERE
SELECT sum(aml.debit) as sum_debit,
sum(aml.credit) as sum_credit
FROM account_move_line as aml
- WHERE date >= %(date_from)s
- AND date <= %(date_to)s
- AND company_id = %(company_id)s
- AND account_id IN %(account_ids)s
+ INNER JOIN account_move as am
+ ON am.id = aml.move_id
+ WHERE aml.date >= %(date_from)s
+ AND aml.date <= %(date_to)s
+ AND aml.company_id = %(company_id)s
+ AND aml.account_id IN %(account_ids)s
"""
+ if self.only_posted_moves:
+ query_select_period_balances += """
+ AND am.state = 'posted'
+ """
query_select_period_balances_params = {
'date_from': self.date_from,
'date_to': self.date_to,
@@ -346,8 +356,6 @@ WHERE
self.env.cr.execute(query_select_period_balances,
query_select_period_balances_params)
sum_debit, sum_credit = self.env.cr.fetchone()
- unaffected_earnings_account_name = \
- '%s (*)' % unaffected_earnings_account.name
query_update_unaffected_earnings_account = """
UPDATE report_trial_balance_account
SET
@@ -361,10 +369,120 @@ WHERE
'sum_credit': sum_credit,
'unaffected_earning_account_id': unaffected_earnings_account.id,
'unaffected_earnings_account_name':
- unaffected_earnings_account_name
+ unaffected_earnings_account.name,
}
self.env.cr.execute(query_update_unaffected_earnings_account,
query_update_unaffected_earnings_account_params)
+ # P&L allocated in the current fiscal year.
+ date = fields.Datetime.from_string(self.date_from)
+ res = self.company_id.compute_fiscalyear_dates(date)
+ fy_start_date = res['date_from']
+ # Fetch the initial balance
+ query_select_initial_pl_balance = """
+ SELECT
+ sum(aml.balance) as sum_balance
+ FROM
+ account_move_line as aml
+ INNER JOIN
+ account_move as am
+ ON am.id = aml.move_id
+ WHERE aml.date >= %(date_from)s
+ AND aml.date < %(date_to)s
+ AND aml.company_id = %(company_id)s
+ AND aml.account_id IN %(account_ids)s
+ """
+ if self.only_posted_moves:
+ query_select_initial_pl_balance += """
+ AND am.state = 'posted'
+ """
+ query_select_initial_pl_balance_params = {
+ 'date_from': fy_start_date,
+ 'date_to': self.date_from,
+ 'company_id': self.company_id.id,
+ 'account_ids': tuple(pl_account_ids),
+ }
+ self.env.cr.execute(query_select_initial_pl_balance,
+ query_select_initial_pl_balance_params)
+ res = self.env.cr.fetchone()
+ allocated_pl_initial_balance = res[0] or 0.0
+ # Fetch the period balance
+ query_select_period_pl_balance = """
+ SELECT
+ sum(aml.debit) as sum_debit,
+ sum(aml.credit) as sum_credit
+ FROM account_move_line as aml
+ INNER JOIN account_move as am
+ ON am.id = aml.move_id
+ WHERE am.date >= %(date_from)s
+ AND aml.date <= %(date_to)s
+ AND aml.company_id = %(company_id)s
+ AND aml.account_id IN %(account_ids)s
+ """
+ if self.only_posted_moves:
+ query_select_period_pl_balance += """
+ AND am.state = 'posted'
+ """
+ query_select_period_pl_balance_params = {
+ 'date_from': self.date_from,
+ 'date_to': self.date_to,
+ 'company_id': self.company_id.id,
+ 'account_ids': tuple(pl_account_ids),
+ }
+ self.env.cr.execute(query_select_period_pl_balance,
+ query_select_period_pl_balance_params)
+ res = self.env.cr.fetchone()
+ allocated_pl_debit = res[0] or 0.0
+ allocated_pl_credit = res[1] or 0.0
+ allocated_pl_period_balance = allocated_pl_credit - allocated_pl_debit
+ allocated_pl_final_balance = \
+ allocated_pl_initial_balance + allocated_pl_period_balance
+ allocated_pl_initial_balance = allocated_pl_initial_balance * -1
+ query_inject_pl_allocation = """
+ INSERT INTO
+ report_trial_balance_account (
+ report_id,
+ create_uid,
+ create_date,
+ account_id,
+ code,
+ name,
+ initial_balance,
+ debit,
+ credit,
+ period_balance,
+ final_balance,
+ initial_balance_foreign_currency,
+ final_balance_foreign_currency)
+ VALUES (
+ %(report_id)s,
+ %(create_uid)s,
+ NOW(),
+ %(account_id)s,
+ %(code)s,
+ %(name)s,
+ %(initial_balance)s,
+ %(debit)s,
+ %(credit)s,
+ %(period_balance)s,
+ %(final_balance)s,
+ 0.0,
+ 0.0
+ )
+ """
+ query_inject_pl_allocation_params = {
+ 'report_id': self.id,
+ 'create_uid': self.env.uid,
+ 'account_id': unaffected_earnings_account.id,
+ 'code': unaffected_earnings_account.code,
+ 'name': '%s (*)' % unaffected_earnings_account.name,
+ 'initial_balance': allocated_pl_initial_balance,
+ 'debit': allocated_pl_credit,
+ 'credit': allocated_pl_debit,
+ 'period_balance': allocated_pl_period_balance,
+ 'final_balance': allocated_pl_final_balance
+ }
+ self.env.cr.execute(query_inject_pl_allocation,
+ query_inject_pl_allocation_params)
def _inject_partner_values(self):
"""Inject report values for report_trial_balance_partner"""
diff --git a/account_financial_report/report/trial_balance_xlsx.py b/account_financial_report/report/trial_balance_xlsx.py
index 0f213938..38929bd5 100644
--- a/account_financial_report/report/trial_balance_xlsx.py
+++ b/account_financial_report/report/trial_balance_xlsx.py
@@ -13,6 +13,13 @@ class TrialBalanceXslx(models.AbstractModel):
def _get_report_name(self):
return _('Trial Balance')
+ def _get_report_footer(self):
+ return _('(*) This report applies the current year Profit and Loss '
+ 'balances to the Undistributed Profit/Loss account.'
+ 'To ensure that the Trial Balance totals total to zero, '
+ 'this line represents the reversal of the current year '
+ 'P&L Balance.')
+
def _get_report_columns(self, report):
if not report.show_partner_details:
res = {
diff --git a/account_financial_report/tests/test_trial_balance.py b/account_financial_report/tests/test_trial_balance.py
index 1de96684..cf386c0a 100644
--- a/account_financial_report/tests/test_trial_balance.py
+++ b/account_financial_report/tests/test_trial_balance.py
@@ -63,8 +63,6 @@ class TestTrialBalance(a_t_f_c.AbstractTestForeignCurrency):
return 'show_partner_details' in filters
-@common.at_install(False)
-@common.post_install(True)
class TestTrialBalanceReport(common.TransactionCase):
def setUp(self):
@@ -500,3 +498,112 @@ class TestTrialBalanceReport(common.TransactionCase):
self.assertEqual(lines['partner_receivable'].debit, 0)
self.assertEqual(lines['partner_receivable'].credit, 2000)
self.assertEqual(lines['partner_receivable'].final_balance, -1000)
+
+ def test_04_undistributed_pl(self):
+ # Generate the trial balance and check that we have 2 lines for the
+ # undistributed P&L.
+ move_name = 'journal previous fy'
+ journal = self.env['account.journal'].search([], limit=1)
+ move_vals = {
+ 'journal_id': journal.id,
+ 'name': move_name,
+ 'date': self.previous_fy_date_end,
+ 'line_ids': [
+ (0, 0, {
+ 'name': move_name,
+ 'debit': 0.0,
+ 'credit': 1000.0,
+ 'account_id': self.account100.id}),
+ (0, 0, {
+ 'name': move_name,
+ 'debit': 1000.0,
+ 'credit': 0.0,
+ 'account_id': self.account110.id})
+ ]}
+ move = self.env['account.move'].create(move_vals)
+ move.post()
+
+ # Generate the trial balance line
+ report_account_model = self.env['report_trial_balance_account']
+ company = self.env.ref('base.main_company')
+ trial_balance = self.env['report_trial_balance'].create({
+ 'date_from': self.date_start,
+ 'date_to': self.date_end,
+ 'only_posted_moves': True,
+ 'hide_account_balance_at_0': False,
+ 'hierarchy_on': 'none',
+ 'company_id': company.id,
+ 'fy_start_date': self.fy_date_start,
+ })
+ trial_balance.compute_data_for_report()
+
+ unaffected_balance_lines = report_account_model.search([
+ ('report_id', '=', trial_balance.id),
+ ('account_id', '=', self.account110.id),
+ ])
+ self.assertEqual(len(unaffected_balance_lines), 2)
+ self.assertEqual(unaffected_balance_lines[0].initial_balance, 1000)
+ self.assertEqual(unaffected_balance_lines[0].debit, 0)
+ self.assertEqual(unaffected_balance_lines[0].credit, 0)
+ self.assertEqual(unaffected_balance_lines[0].final_balance, 1000)
+ # Test P&L Allocation
+ self.assertEqual(unaffected_balance_lines[1].initial_balance, 0)
+ self.assertEqual(unaffected_balance_lines[1].debit, 0)
+ self.assertEqual(unaffected_balance_lines[1].credit, 0)
+ self.assertEqual(unaffected_balance_lines[1].final_balance, 0)
+ # Add a P&L Move
+ move_name = 'current year pl move'
+ journal = self.env['account.journal'].search([], limit=1)
+ move_vals = {
+ 'journal_id': journal.id,
+ 'name': move_name,
+ 'date': self.date_start,
+ 'line_ids': [
+ (0, 0, {
+ 'name': move_name,
+ 'debit': 0.0,
+ 'credit': 1000.0,
+ 'account_id': self.account300.id}),
+ (0, 0, {
+ 'name': move_name,
+ 'debit': 1000.0,
+ 'credit': 0.0,
+ 'account_id': self.account100.id})
+ ]}
+ move = self.env['account.move'].create(move_vals)
+ move.post()
+ # Re Generate the trial balance line
+ trial_balance = self.env['report_trial_balance'].create({
+ 'date_from': self.date_start,
+ 'date_to': self.date_end,
+ 'only_posted_moves': True,
+ 'hide_account_balance_at_0': False,
+ 'hierarchy_on': 'none',
+ 'company_id': company.id,
+ 'fy_start_date': self.fy_date_start,
+ })
+ trial_balance.compute_data_for_report()
+ unaffected_balance_lines = report_account_model.search([
+ ('report_id', '=', trial_balance.id),
+ ('account_id', '=', self.account110.id),
+ ])
+ # The unaffected earnings account is affected by this new entry
+ self.assertEqual(len(unaffected_balance_lines), 2)
+ self.assertEqual(unaffected_balance_lines[0].initial_balance, 1000)
+ self.assertEqual(unaffected_balance_lines[0].debit, 0)
+ self.assertEqual(unaffected_balance_lines[0].credit, 1000)
+ self.assertEqual(unaffected_balance_lines[0].final_balance, 0)
+ # The P&L is affected by this new entry, basically reversing the
+ # P&L balances.
+ self.assertEqual(unaffected_balance_lines[1].initial_balance, 0)
+ self.assertEqual(unaffected_balance_lines[1].debit, 1000)
+ self.assertEqual(unaffected_balance_lines[1].credit, 0)
+ self.assertEqual(unaffected_balance_lines[1].final_balance, 1000)
+ # The totals for the Trial Balance are zero
+ all_lines = report_account_model.search([
+ ('report_id', '=', trial_balance.id),
+ ])
+ self.assertEqual(sum(all_lines.mapped('initial_balance')), 0)
+ self.assertEqual(sum(all_lines.mapped('final_balance')), 0)
+ self.assertEqual(sum(all_lines.mapped('debit')),
+ sum(all_lines.mapped('credit')))