From f90691ae86fe26bf2f6f2bdf476803db77c416b9 Mon Sep 17 00:00:00 2001 From: Simone Rubino Date: Mon, 21 Nov 2022 16:55:45 +0100 Subject: [PATCH 1/2] [IMP] account_budget_oca: Add report for budget lines --- account_budget_oca/__init__.py | 2 + account_budget_oca/__manifest__.py | 2 + account_budget_oca/models/account_budget.py | 10 +- account_budget_oca/report/__init__.py | 3 + .../report/report_budget_lines.py | 121 ++++++++++++++++++ .../report/report_budget_lines_views.xml | 41 ++++++ account_budget_oca/tests/__init__.py | 1 + account_budget_oca/tests/test_lines_report.py | 46 +++++++ .../views/account_budget_views.xml | 12 +- account_budget_oca/wizards/__init__.py | 3 + .../wizards/report_budget_lines_wizard.py | 24 ++++ .../report_budget_lines_wizard_views.xml | 46 +++++++ 12 files changed, 302 insertions(+), 9 deletions(-) create mode 100644 account_budget_oca/report/__init__.py create mode 100644 account_budget_oca/report/report_budget_lines.py create mode 100644 account_budget_oca/report/report_budget_lines_views.xml create mode 100644 account_budget_oca/tests/test_lines_report.py create mode 100644 account_budget_oca/wizards/__init__.py create mode 100644 account_budget_oca/wizards/report_budget_lines_wizard.py create mode 100644 account_budget_oca/wizards/report_budget_lines_wizard_views.xml diff --git a/account_budget_oca/__init__.py b/account_budget_oca/__init__.py index d6210b12..5309bad5 100644 --- a/account_budget_oca/__init__.py +++ b/account_budget_oca/__init__.py @@ -1,3 +1,5 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models +from . import report +from . import wizards diff --git a/account_budget_oca/__manifest__.py b/account_budget_oca/__manifest__.py index 3515dadf..5a4b3862 100644 --- a/account_budget_oca/__manifest__.py +++ b/account_budget_oca/__manifest__.py @@ -16,6 +16,8 @@ 'views/account_analytic_account_views.xml', 'views/account_budget_views.xml', 'views/res_config_settings_views.xml', + 'wizards/report_budget_lines_wizard_views.xml', + 'report/report_budget_lines_views.xml', ], 'demo': ['data/account_budget_demo.xml'], } diff --git a/account_budget_oca/models/account_budget.py b/account_budget_oca/models/account_budget.py index b4532319..a6347457 100644 --- a/account_budget_oca/models/account_budget.py +++ b/account_budget_oca/models/account_budget.py @@ -168,7 +168,11 @@ def _compute_practical_amount(self): @api.multi def _compute_theoretical_amount(self): - today = fields.Datetime.now() + date = self.env.context.get('budget_date') + if date: + date = date + else: + date = fields.Datetime.now() for line in self: # Used for the report @@ -183,14 +187,14 @@ def _compute_theoretical_amount(self): from_string(line.date_to) - from_string(line.date_from)) elapsed_timedelta = ( - from_string(today) - (from_string(line.date_from))) + from_string(date) - (from_string(line.date_from))) if elapsed_timedelta.days < 0: # If the budget line has not started yet, theoretical # amount should be zero theo_amt = 0.00 elif (line_timedelta.days > 0 and - from_string(today) < from_string(line.date_to)): + from_string(date) < from_string(line.date_to)): # If today is between the budget line date_from and # date_to theo_amt = ( diff --git a/account_budget_oca/report/__init__.py b/account_budget_oca/report/__init__.py new file mode 100644 index 00000000..7aced14d --- /dev/null +++ b/account_budget_oca/report/__init__.py @@ -0,0 +1,3 @@ +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from . import report_budget_lines diff --git a/account_budget_oca/report/report_budget_lines.py b/account_budget_oca/report/report_budget_lines.py new file mode 100644 index 00000000..c416d770 --- /dev/null +++ b/account_budget_oca/report/report_budget_lines.py @@ -0,0 +1,121 @@ +# Copyright 2022 Simone Rubino - TAKOBI +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from odoo import fields +from odoo.models import TransientModel + + +class ReportBudgetLinesHeader (TransientModel): + _name = 'account_budget_oca.budget_lines_report.header' + _description = "Budget Report Header" + + date = fields.Date() + line_ids = fields.One2many( + comodel_name='account_budget_oca.budget_lines_report', + inverse_name='report_header_id', + ) + + def _prepare_report_lines(self): + self.ensure_one() + budget_lines = self.env['crossovered.budget.lines'].search([]) + budget_lines_values = budget_lines \ + .with_context( + budget_date=self.date, + ) \ + .read( + fields=[ + 'id', + 'crossovered_budget_id', + 'analytic_account_id', + 'general_budget_id', + 'date_from', + 'date_to', + 'planned_amount', + 'practical_amount', + 'theoretical_amount', + 'percentage', + 'company_id', + ], + load=None, + ) + report_id = self.id + for budget_line_values in budget_lines_values: + line_id = budget_line_values.pop('id') + budget_line_values.update({ + 'crossovered_budget_line_id': line_id, + 'report_header_id': report_id + }) + return budget_lines_values + + def compute_report_data(self): + self.ensure_one() + report_lines_values = self._prepare_report_lines() + report_lines = self.env['account_budget_oca.budget_lines_report'] \ + .create(report_lines_values) + return report_lines + + def show_result(self): + lines = self.mapped('line_ids') + lines_action = self.env['ir.actions.act_window'] \ + .for_xml_id('account_budget_oca', 'budget_lines_report_action') + lines_action['domain'] = [ + ('id', 'in', lines.ids), + ] + return lines_action + + +class ReportBudgetLines (TransientModel): + _name = 'account_budget_oca.budget_lines_report' + _description = "Budget Report Lines" + + report_header_id = fields.Many2one( + comodel_name='account_budget_oca.budget_lines_report.header', + string="Report Header", + readonly=True, + ) + crossovered_budget_line_id = fields.Many2one( + comodel_name='crossovered.budget.lines', + string="Budget Line", + readonly=True, + ) + crossovered_budget_id = fields.Many2one( + comodel_name='crossovered.budget', + string="Budget", + readonly=True, + ) + analytic_account_id = fields.Many2one( + comodel_name='account.analytic.account', + string='Analytic Account', + readonly=True, + ) + general_budget_id = fields.Many2one( + comodel_name='account.budget.post', + string='Budgetary Position', + readonly=True, + ) + date_from = fields.Date( + string='Start Date', + readonly=True, + ) + date_to = fields.Date( + string='End Date', + readonly=True, + ) + planned_amount = fields.Float( + readonly=True, + ) + practical_amount = fields.Float( + readonly=True, + ) + theoretical_amount = fields.Float( + readonly=True, + ) + percentage = fields.Float( + string='Achievement', + readonly=True, + ) + company_id = fields.Many2one( + comodel_name='res.company', + string="Company", + readonly=True, + ) diff --git a/account_budget_oca/report/report_budget_lines_views.xml b/account_budget_oca/report/report_budget_lines_views.xml new file mode 100644 index 00000000..6347cc4f --- /dev/null +++ b/account_budget_oca/report/report_budget_lines_views.xml @@ -0,0 +1,41 @@ + + + + + Budget Report Lines Pivot view + account_budget_oca.budget_lines_report + + + + + + + + + + + Budget Report Lines Tree view + account_budget_oca.budget_lines_report + + + + + + + + + + + + + + + Budgets + account_budget_oca.budget_lines_report + form + pivot,tree,form + + diff --git a/account_budget_oca/tests/__init__.py b/account_budget_oca/tests/__init__.py index 47b1dc89..d7bd37f0 100644 --- a/account_budget_oca/tests/__init__.py +++ b/account_budget_oca/tests/__init__.py @@ -3,3 +3,4 @@ from . import common from . import test_theoreticalamount from . import test_account_budget +from . import test_lines_report diff --git a/account_budget_oca/tests/test_lines_report.py b/account_budget_oca/tests/test_lines_report.py new file mode 100644 index 00000000..6a5ac432 --- /dev/null +++ b/account_budget_oca/tests/test_lines_report.py @@ -0,0 +1,46 @@ +# Copyright 2022 Simone Rubino - TAKOBI +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from dateutil.relativedelta import relativedelta + +from odoo import fields +from odoo.tests import Form +from .common import TestAccountBudgetCommon + + +class TestLinesReport (TestAccountBudgetCommon): + + def test_report_theoretical_amount(self): + """ + The report shows the budget lines amounts in a specified date. + """ + # Clean any other budget line that might concur in the report + self.env['crossovered.budget.lines'].search([]).unlink() + # Arrange: Create a budget of 1000 for the next 10 days + date_from = fields.Date.today() + relativedelta(days=1) + date_to = date_from + relativedelta(days=10) + budget_form = Form(self.env['crossovered.budget']) + budget_form.name = "Test budget" + budget_form.date_from = date_from + budget_form.date_to = date_to + with budget_form.crossovered_budget_line_ids.new() as line: + line.planned_amount = 1000 + line.general_budget_id = self.account_budget_post_sales0 + budget = budget_form.save() + # pre-condition: Right now, the theoretical amount is 0 + budget_lines = budget.mapped('crossovered_budget_line_ids') + budget_theoretical_amount = sum(budget_lines.mapped('theoretical_amount')) + self.assertEqual(budget_theoretical_amount, 0) + + # Act: Create the report for 5 days from now + wizard_form = Form(self.env['account_budget_oca.budget_lines_report.wizard']) + wizard_form.date = date_from + relativedelta(days=5) + wizard = wizard_form.save() + report_action = wizard.generate() + report_lines_model = report_action.get('res_model') + report_lines_domain = report_action.get('domain') + report_lines = self.env[report_lines_model].search(report_lines_domain) + + # Assert: The report show that the theoretical amount is 500 + report_theoretical_amount = sum(report_lines.mapped('theoretical_amount')) + self.assertEqual(report_theoretical_amount, 500) diff --git a/account_budget_oca/views/account_budget_views.xml b/account_budget_oca/views/account_budget_views.xml index 669f870a..17581b9c 100644 --- a/account_budget_oca/views/account_budget_views.xml +++ b/account_budget_oca/views/account_budget_views.xml @@ -86,7 +86,7 @@