From 47a9e9dc81ff7ced710a6860a08bf2bc207943a9 Mon Sep 17 00:00:00 2001 From: ps-tubtim Date: Wed, 11 Jan 2023 10:18:26 +0700 Subject: [PATCH 01/13] Initial 15.0 --- budget_activity/README.rst | 97 ++++ budget_activity/__init__.py | 3 + budget_activity/__manifest__.py | 28 ++ budget_activity/models/__init__.py | 15 + .../models/account_analytic_line.py | 13 + budget_activity/models/account_move.py | 38 ++ budget_activity/models/base_budget_move.py | 72 +++ budget_activity/models/budget_activity.py | 75 +++ .../models/budget_activity_group.py | 21 + budget_activity/models/budget_control.py | 20 + .../models/budget_move_adjustment.py | 19 + budget_activity/models/budget_period.py | 57 +++ budget_activity/models/mis_budget_item.py | 13 + budget_activity/models/mis_report.py | 31 ++ budget_activity/models/mis_report_instance.py | 16 + budget_activity/models/mis_report_kpi.py | 74 +++ budget_activity/models/res_config_settings.py | 12 + budget_activity/readme/CONTRIBUTORS.rst | 5 + budget_activity/readme/DESCRIPTION.rst | 1 + budget_activity/readme/USAGE.rst | 16 + budget_activity/report/__init__.py | 2 + .../report/budget_monitor_report.py | 48 ++ .../report/budget_monitor_report_view.xml | 39 ++ .../security/budget_activity_security.xml | 27 ++ budget_activity/security/ir.model.access.csv | 7 + budget_activity/static/description/icon.png | Bin 0 -> 9455 bytes budget_activity/static/description/index.html | 444 ++++++++++++++++++ budget_activity/tests/__init__.py | 2 + budget_activity/tests/test_budget_activity.py | 69 +++ budget_activity/views/account_move_views.xml | 22 + .../views/budget_activity_group_view.xml | 79 ++++ .../views/budget_activity_view.xml | 199 ++++++++ budget_activity/views/budget_menuitem.xml | 10 + .../views/budget_move_adjustment_view.xml | 25 + budget_activity/views/mis_budget_item.xml | 16 + budget_activity/views/mis_report.xml | 21 + budget_activity/views/mis_report_kpi.xml | 26 + .../views/res_config_settings_views.xml | 32 ++ 38 files changed, 1694 insertions(+) create mode 100644 budget_activity/README.rst create mode 100644 budget_activity/__init__.py create mode 100644 budget_activity/__manifest__.py create mode 100644 budget_activity/models/__init__.py create mode 100644 budget_activity/models/account_analytic_line.py create mode 100644 budget_activity/models/account_move.py create mode 100644 budget_activity/models/base_budget_move.py create mode 100644 budget_activity/models/budget_activity.py create mode 100644 budget_activity/models/budget_activity_group.py create mode 100644 budget_activity/models/budget_control.py create mode 100644 budget_activity/models/budget_move_adjustment.py create mode 100644 budget_activity/models/budget_period.py create mode 100644 budget_activity/models/mis_budget_item.py create mode 100644 budget_activity/models/mis_report.py create mode 100644 budget_activity/models/mis_report_instance.py create mode 100644 budget_activity/models/mis_report_kpi.py create mode 100644 budget_activity/models/res_config_settings.py create mode 100644 budget_activity/readme/CONTRIBUTORS.rst create mode 100644 budget_activity/readme/DESCRIPTION.rst create mode 100644 budget_activity/readme/USAGE.rst create mode 100644 budget_activity/report/__init__.py create mode 100644 budget_activity/report/budget_monitor_report.py create mode 100644 budget_activity/report/budget_monitor_report_view.xml create mode 100644 budget_activity/security/budget_activity_security.xml create mode 100644 budget_activity/security/ir.model.access.csv create mode 100644 budget_activity/static/description/icon.png create mode 100644 budget_activity/static/description/index.html create mode 100644 budget_activity/tests/__init__.py create mode 100644 budget_activity/tests/test_budget_activity.py create mode 100644 budget_activity/views/account_move_views.xml create mode 100644 budget_activity/views/budget_activity_group_view.xml create mode 100644 budget_activity/views/budget_activity_view.xml create mode 100644 budget_activity/views/budget_menuitem.xml create mode 100644 budget_activity/views/budget_move_adjustment_view.xml create mode 100644 budget_activity/views/mis_budget_item.xml create mode 100644 budget_activity/views/mis_report.xml create mode 100644 budget_activity/views/mis_report_kpi.xml create mode 100644 budget_activity/views/res_config_settings_views.xml diff --git a/budget_activity/README.rst b/budget_activity/README.rst new file mode 100644 index 00000000..585c0410 --- /dev/null +++ b/budget_activity/README.rst @@ -0,0 +1,97 @@ +=============== +Budget Activity +=============== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--budgeting-lightgray.png?logo=github + :target: https://github.com/OCA/account-budgeting/tree/14.0/budget_activity + :alt: OCA/account-budgeting +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-budgeting-14-0/account-budgeting-14-0-budget_activity + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/88/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module add "Activity" element for budget actual (move, move line, analytic line) + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +#. Go to Budgeting > Configurations > Budget Activity +#. Create new activity and match with Account > Save +#. On invoice lines, User can select Activity field and it will auto change account to match with Activity + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Ecosoft + +Contributors +~~~~~~~~~~~~ + +* `Ecosoft `__: + + * Kitti Upariphutthiphong + * Saran Lim. + * Pimolnat Suntian + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-kittiu| image:: https://github.com/kittiu.png?size=40px + :target: https://github.com/kittiu + :alt: kittiu + +Current `maintainer `__: + +|maintainer-kittiu| + +This module is part of the `OCA/account-budgeting `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/budget_activity/__init__.py b/budget_activity/__init__.py new file mode 100644 index 00000000..08d4ad0b --- /dev/null +++ b/budget_activity/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import models +from . import report diff --git a/budget_activity/__manifest__.py b/budget_activity/__manifest__.py new file mode 100644 index 00000000..c102ebde --- /dev/null +++ b/budget_activity/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Budget Activity", + "version": "14.0.1.0.0", + "category": "Accounting", + "license": "AGPL-3", + "author": "Ecosoft, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-budgeting", + "depends": ["budget_control"], + "data": [ + "security/budget_activity_security.xml", + "security/ir.model.access.csv", + "report/budget_monitor_report_view.xml", + "views/res_config_settings_views.xml", + "views/mis_report.xml", + "views/mis_report_kpi.xml", + "views/mis_budget_item.xml", + "views/account_move_views.xml", + "views/budget_activity_view.xml", + "views/budget_activity_group_view.xml", + "views/budget_menuitem.xml", + "views/budget_move_adjustment_view.xml", + ], + "installable": True, + "maintainers": ["kittiu"], + "development_status": "Alpha", +} diff --git a/budget_activity/models/__init__.py b/budget_activity/models/__init__.py new file mode 100644 index 00000000..f5516b20 --- /dev/null +++ b/budget_activity/models/__init__.py @@ -0,0 +1,15 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import res_config_settings +from . import account_move +from . import budget_activity +from . import budget_activity_group +from . import account_analytic_line +from . import mis_budget_item +from . import mis_report +from . import mis_report_kpi +from . import mis_report_instance +from . import base_budget_move +from . import budget_period +from . import budget_control +from . import budget_move_adjustment diff --git a/budget_activity/models/account_analytic_line.py b/budget_activity/models/account_analytic_line.py new file mode 100644 index 00000000..e6130e8f --- /dev/null +++ b/budget_activity/models/account_analytic_line.py @@ -0,0 +1,13 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class AccountAnalyticLine(models.Model): + _inherit = "account.analytic.line" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) diff --git a/budget_activity/models/account_move.py b/budget_activity/models/account_move.py new file mode 100644 index 00000000..e63934e4 --- /dev/null +++ b/budget_activity/models/account_move.py @@ -0,0 +1,38 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) + + def _get_computed_account(self): + self.ensure_one() + account = super()._get_computed_account() + # Case non-realtime inventory product + # activity's account takes priority over product's account + realtime = ( + self.product_id.type == "product" + and hasattr(self.product_id.categ_id, "property_valuation") + and self.product_id.categ_id.property_valuation == "real_time" + ) + if not realtime and self.activity_id: + account = self.activity_id.account_id + return account + + def _prepare_analytic_line(self): + res = super()._prepare_analytic_line() + for i, ml in enumerate(self): + res[i]["activity_id"] = ml.activity_id.id + return res + + @api.onchange("activity_id") + def _onchange_activity_id(self): + if self.activity_id: + self.account_id = self.activity_id.account_id diff --git a/budget_activity/models/base_budget_move.py b/budget_activity/models/base_budget_move.py new file mode 100644 index 00000000..16dd8f1c --- /dev/null +++ b/budget_activity/models/base_budget_move.py @@ -0,0 +1,72 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class BaseBudgetMove(models.AbstractModel): + _inherit = "base.budget.move" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) + activity_group_id = fields.Many2one( + comodel_name="budget.activity.group", + string="Activity Group", + related="activity_id.activity_group_id", + ) + account_id = fields.Many2one( + compute="_compute_activity_account", + store=True, + help="When activity is selected, always use activity's account to " + "enusre that the KPI budgeting which rely on account is valid. " + "Because in some case, i.e., perpetual inventory, " + "account in account.move.line can be different with " + "account in account.budget.move.", + ) + + @api.depends("activity_id") + def _compute_activity_account(self): + for rec in self: + rec.account_id = ( + rec.activity_id.account_id if rec.activity_id else rec.account_id + ) + + @api.constrains("activity_id", "account_id") + def _check_activity_account(self): + for rec in self.filtered("activity_id"): + if rec.account_id != rec.activity_id.account_id: + raise UserError( + _("Account not equal to Activity's Account: %s") + % rec.activity_id.account_id.display_name + ) + + +class BudgetDoclineMixinBase(models.AbstractModel): + _inherit = "budget.docline.mixin.base" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + domain=lambda self: self._domain_activity(), + index=True, + ) + + def _domain_activity(self): + return [] + + +class BudgetDoclineMixin(models.AbstractModel): + _inherit = "budget.docline.mixin" + + def _update_budget_commitment(self, budget_vals, reverse=False): + budget_vals = super()._update_budget_commitment(budget_vals, reverse=reverse) + budget_vals["activity_id"] = self.activity_id.id + # For case object without account_id (PR/PO), normally account is from + # product, it should now changed to follow activity. + # But if account_id is part of object (INV), use whatever is passed-in. + if "account_id" not in self: + budget_vals["account_id"] = self["activity_id"].account_id.id + return budget_vals diff --git a/budget_activity/models/budget_activity.py b/budget_activity/models/budget_activity.py new file mode 100644 index 00000000..f237eea3 --- /dev/null +++ b/budget_activity/models/budget_activity.py @@ -0,0 +1,75 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class BudgetActivityTag(models.Model): + _name = "budget.activity.tag" + _description = "Budget Activity Tag" + + name = fields.Char(string="Name", required=True) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + required=True, + ) + active = fields.Boolean( + default=True, + help="Set active to false to hide the Budget Activity Tag without removing it.", + ) + _sql_constraints = [ + ( + "name_company_unique", + "unique(name, company_id)", + "This tag name is already used!", + ) + ] + + +class BudgetActivity(models.Model): + _name = "budget.activity" + _description = "Budget Activity" + + name = fields.Char( + required=True, + ) + active = fields.Boolean(default=True) + activity_group_id = fields.Many2one( + comodel_name="budget.activity.group", + index=True, + ondelete="restrict", + ) + account_id = fields.Many2one( + comodel_name="account.account", + string="Account", + compute="_compute_account_id", + store=True, + domain=[("deprecated", "=", False)], + readonly=False, + required=False, + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + required=True, + ) + code = fields.Char(related="account_id.code") + tag_ids = fields.Many2many( + comodel_name="budget.activity.tag", + relation="budget_activity_tag_rel", + column1="budget_activity_id", + column2="budget_activity_tag_id", + string="Tags", + help="Optional tags you may want to assign for search", + ) + + @api.depends("activity_group_id") + def _compute_account_id(self): + for rec in self: + rec.account_id = ( + not rec.account_id + and rec.activity_group_id.account_id + or rec.account_id + ) diff --git a/budget_activity/models/budget_activity_group.py b/budget_activity/models/budget_activity_group.py new file mode 100644 index 00000000..1566d024 --- /dev/null +++ b/budget_activity/models/budget_activity_group.py @@ -0,0 +1,21 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class BudgetActivityGroup(models.Model): + _name = "budget.activity.group" + _description = "Budget Activity Group" + + name = fields.Char( + required=True, + ) + active = fields.Boolean(default=True) + activity_ids = fields.One2many( + comodel_name="budget.activity", inverse_name="activity_group_id" + ) + account_id = fields.Many2one( + comodel_name="account.account", + string="Default Account", + domain=[("deprecated", "=", False)], + ) diff --git a/budget_activity/models/budget_control.py b/budget_activity/models/budget_control.py new file mode 100644 index 00000000..0c8adfda --- /dev/null +++ b/budget_activity/models/budget_control.py @@ -0,0 +1,20 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class BudgetControl(models.Model): + _inherit = "budget.control" + + def _get_context_budget_monitoring(self): + ctx = super()._get_context_budget_monitoring() + ctx.update({"search_default_group_by_activity_group": 1}) + return ctx + + def _get_value_items(self, date_range, kpi_expression): + items = super()._get_value_items(date_range, kpi_expression) + activity_group_id = kpi_expression.kpi_id.activity_group_id.id + for item in items: + item["activity_group_id"] = activity_group_id + return items diff --git a/budget_activity/models/budget_move_adjustment.py b/budget_activity/models/budget_move_adjustment.py new file mode 100644 index 00000000..75e9d7e4 --- /dev/null +++ b/budget_activity/models/budget_move_adjustment.py @@ -0,0 +1,19 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class BudgetMoveAdjustmentItem(models.Model): + _inherit = "budget.move.adjustment.item" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) + + @api.onchange("activity_id") + def _onchange_activity_id(self): + if self.activity_id: + self.account_id = self.activity_id.account_id diff --git a/budget_activity/models/budget_period.py b/budget_activity/models/budget_period.py new file mode 100644 index 00000000..470f6426 --- /dev/null +++ b/budget_activity/models/budget_period.py @@ -0,0 +1,57 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import _, api, models +from odoo.exceptions import UserError + + +class BudgetPeriod(models.Model): + _inherit = "budget.period" + + def _prepare_controls_activity(self, budget_period, doclines): + controls = set() + control_analytics = budget_period.control_analytic_account_ids + budget_moves = doclines.mapped(doclines._budget_field()) + # get budget move from period only + budget_moves_period = budget_moves.filtered( + lambda l: l.date >= budget_period.bm_date_from + and l.date <= budget_period.bm_date_to + ) + for i in budget_moves_period: + if budget_period.control_all_analytic_accounts: + if i.analytic_account_id and i.activity_id: + controls.add((i.analytic_account_id.id, i.activity_id.id)) + else: # Only analtyic in control + if i.analytic_account_id in control_analytics and i.activity_id: + controls.add((i.analytic_account_id.id, i.activity_id.id)) + # Convert to list of dict, for readibility + return [{"analytic_id": x[0], "activity_id": x[1]} for x in controls] + + @api.model + def _prepare_controls(self, budget_period, doclines): + if budget_period.report_id.is_activity: + return self._prepare_controls_activity(budget_period, doclines) + return super()._prepare_controls(budget_period, doclines) + + @api.model + def _get_kpi_by_control_key(self, instance, kpis, control): + if instance.report_id.is_activity: + activity_id = control["activity_id"] + kpi = kpis.get(activity_id, []) + if len(kpi) == 1: + return kpi + # Invalid KPI + activity = self.env["budget.activity"].browse(activity_id) + if not kpi: + raise UserError( + _("Chosen activity %s is not valid for budgeting") + % activity.display_name + ) + else: + raise UserError( + _( + "KPI Template '%s' has more than one KPI being " + "refereced by same activity %s" + ) + % (instance.report_id.name, activity.display_name) + ) + return super()._get_kpi_by_control_key(instance, kpis, control) diff --git a/budget_activity/models/mis_budget_item.py b/budget_activity/models/mis_budget_item.py new file mode 100644 index 00000000..33b0d244 --- /dev/null +++ b/budget_activity/models/mis_budget_item.py @@ -0,0 +1,13 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class MisBudgetItem(models.Model): + _inherit = "mis.budget.item" + + activity_group_id = fields.Many2one( + comodel_name="budget.activity.group", + ondelete="restrict", + index=True, + ) diff --git a/budget_activity/models/mis_report.py b/budget_activity/models/mis_report.py new file mode 100644 index 00000000..d0e419a1 --- /dev/null +++ b/budget_activity/models/mis_report.py @@ -0,0 +1,31 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from collections import defaultdict + +from odoo import fields, models + + +class MisReport(models.Model): + _inherit = "mis.report" + + is_activity = fields.Boolean( + help="if check, Expression will compute Activity instead Account" + ) + + def get_kpis(self, company): + self.ensure_one() + if self.is_activity: + return self.get_kpis_by_activity_id(company) + return super().get_kpis(company) + + def get_kpis_by_activity_id(self, company): + """Return { activity_id: set(kpi) }""" + res = defaultdict(set) + for kpi in self.kpi_ids: + for expression in kpi.expression_ids: + if not expression.name: + continue + activity_ids = kpi.activity_group_id.activity_ids.ids + for activity_id in activity_ids: + res[activity_id].add(kpi) + return res diff --git a/budget_activity/models/mis_report_instance.py b/budget_activity/models/mis_report_instance.py new file mode 100644 index 00000000..7a3a5c23 --- /dev/null +++ b/budget_activity/models/mis_report_instance.py @@ -0,0 +1,16 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class MisReportInstance(models.Model): + _inherit = "mis.report.instance" + + def _get_context_filter_matrix(self): + ctx = super()._get_context_filter_matrix() + if ctx.get("filter_analytic_ids") and ctx.get("filter_activity_group"): + ctx["mis_report_filters"]["activity_group_id"] = { + "value": ctx["filter_activity_group"], + "operator": "all", + } + return ctx diff --git a/budget_activity/models/mis_report_kpi.py b/budget_activity/models/mis_report_kpi.py new file mode 100644 index 00000000..e9f56780 --- /dev/null +++ b/budget_activity/models/mis_report_kpi.py @@ -0,0 +1,74 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class MisReportKpi(models.Model): + _inherit = "mis.report.kpi" + + description = fields.Char( + compute="_compute_name_activity", store=True, readonly=False + ) + activity_expression = fields.Boolean( + compute="_compute_is_activity", + readonly=False, + store=True, + ) + activity_group_id = fields.Many2one( + comodel_name="budget.activity.group", + index=True, + string="Activity Group", + ) + respectively_variation = fields.Char( + help="respectively variation over the " + "period (p), initial balance (i), ending balance (e)" + ) + + @api.depends("activity_group_id") + def _compute_name_activity(self): + for rec in self: + rec.description = False + if rec.activity_expression and rec.activity_group_id: + rec.description = rec.activity_group_id.name + + @api.depends("report_id.is_activity") + def _compute_is_activity(self): + for rec in self: + rec.activity_expression = rec.report_id.is_activity + + def _filter_balance_mis(self, activity_ids): + self.ensure_one() + dom = "('activity_id', 'in', {})".format(tuple(activity_ids.ids)) + return dom + + @api.depends( + "expression_ids.subkpi_id.name", + "expression_ids.name", + "activity_group_id.activity_ids", + "respectively_variation", + ) + def _compute_expression(self): + super()._compute_expression() + for kpi in self: + if kpi.activity_expression and kpi.activity_group_id: + activity_ids = kpi.activity_group_id.activity_ids + if not activity_ids: + raise UserError( + _( + "Activity Group {} has no activities".format( + kpi.activity_group_id.name + ) + ) + ) + accounts = activity_ids.mapped("account_id") + account_str = "[%s]" % ",".join([acc.code for acc in accounts]) + kpi.expression = "bal{}{}[{}]".format( + kpi.respectively_variation or "", + account_str, + kpi._filter_balance_mis(activity_ids), + ) + # Update expression_ids for display realtime + # TODO: please check follwoing logic again. + kpi._inverse_expression() diff --git a/budget_activity/models/res_config_settings.py b/budget_activity/models/res_config_settings.py new file mode 100644 index 00000000..dfde5f28 --- /dev/null +++ b/budget_activity/models/res_config_settings.py @@ -0,0 +1,12 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + group_budget_activity_tag = fields.Boolean( + string="Budget Activity Tags", + implied_group="budget_activity.group_budget_activity_tag", + ) diff --git a/budget_activity/readme/CONTRIBUTORS.rst b/budget_activity/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..92e451a5 --- /dev/null +++ b/budget_activity/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* `Ecosoft `__: + + * Kitti Upariphutthiphong + * Saran Lim. + * Pimolnat Suntian diff --git a/budget_activity/readme/DESCRIPTION.rst b/budget_activity/readme/DESCRIPTION.rst new file mode 100644 index 00000000..f2dffd6b --- /dev/null +++ b/budget_activity/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module add "Activity" element for budget actual (move, move line, analytic line) diff --git a/budget_activity/readme/USAGE.rst b/budget_activity/readme/USAGE.rst new file mode 100644 index 00000000..f26ddbe4 --- /dev/null +++ b/budget_activity/readme/USAGE.rst @@ -0,0 +1,16 @@ +To used this module you have to configued Budget Activity and Budget Activity Group first. + +#. Go to Budgeting > Configurations > Budget Activity +#. Create new activity and match with Account > Save +#. On invoice lines, User can select Activity field and it will auto change account to match with Activity + + +How to configure an Activity Group with a MIS Builder: + +#. Go to Invoicing > Configuration > MIS Reporting > MIS Report Templates +#. Choose you template that you would to see report by activity group +#. check 'Is Activity' +#. create new KPI line and select Activity Group field +#. It will generate expression related with activity group > Save + +Then, you can test by create new bills and view your report. diff --git a/budget_activity/report/__init__.py b/budget_activity/report/__init__.py new file mode 100644 index 00000000..9ec4b2a8 --- /dev/null +++ b/budget_activity/report/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import budget_monitor_report diff --git a/budget_activity/report/budget_monitor_report.py b/budget_activity/report/budget_monitor_report.py new file mode 100644 index 00000000..eaa3c454 --- /dev/null +++ b/budget_activity/report/budget_monitor_report.py @@ -0,0 +1,48 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class BudgetMonitorReport(models.Model): + _inherit = "budget.monitor.report" + + activity = fields.Char(string="Activity") + activity_group = fields.Char(string="Activity Group") + + # Budget + def _select_budget(self): + select_budget_query = super()._select_budget() + select_budget_query[ + 20 + ] = "bag.description as activity_group, null::char as activity" + return select_budget_query + + def _from_budget(self): + from_budget_query = super()._from_budget() + from_budget_query = "\n".join( + [ + from_budget_query, + "join mis_report_kpi_expression mrke on " + "a.kpi_expression_id = mrke.id", + "join mis_report_kpi bag on mrke.kpi_id = bag.id", + ] + ) + return from_budget_query + + # All consumed + def _select_statement(self, amount_type): + select_statement = super()._select_statement(amount_type) + select_statement[20] = "bag.name as activity_group, ba.name as activity" + return select_statement + + def _from_statement(self, amount_type): + from_statment = super()._from_statement(amount_type) + from_statment = "\n".join( + [ + from_statment, + "left outer join budget_activity ba " "on a.activity_id = ba.id ", + "left outer join budget_activity_group bag " + "on ba.activity_group_id = bag.id", + ] + ) + return from_statment diff --git a/budget_activity/report/budget_monitor_report_view.xml b/budget_activity/report/budget_monitor_report_view.xml new file mode 100644 index 00000000..700f1970 --- /dev/null +++ b/budget_activity/report/budget_monitor_report_view.xml @@ -0,0 +1,39 @@ + + + + budget.monitor.report.tree + budget.monitor.report + + + + + + + + + + budget.monitor.report.search + budget.monitor.report + + + + + + + + + diff --git a/budget_activity/security/budget_activity_security.xml b/budget_activity/security/budget_activity_security.xml new file mode 100644 index 00000000..63851257 --- /dev/null +++ b/budget_activity/security/budget_activity_security.xml @@ -0,0 +1,27 @@ + + + + + Budget Activity multi company rule + + + ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + + Budget Activity Tag multi company rule + + + ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + + + + Budget Activity Tags + + + + diff --git a/budget_activity/security/ir.model.access.csv b/budget_activity/security/ir.model.access.csv new file mode 100644 index 00000000..a1a541c2 --- /dev/null +++ b/budget_activity/security/ir.model.access.csv @@ -0,0 +1,7 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +manage_budget_activity,manage_budget_activity,model_budget_activity,budget_control.group_budget_control_manager,1,1,1,1 +user_budget_activity,user_budget_activity,model_budget_activity,budget_control.group_budget_control_user,1,0,0,0 +manage_budget_activity_group,manage_budget_activity_group,model_budget_activity_group,budget_control.group_budget_control_manager,1,1,1,1 +user_budget_activity_group,user_budget_activity_group,model_budget_activity_group,budget_control.group_budget_control_user,1,0,0,0 +manage_budget_activity_tag,manage_budget_activity_tag,model_budget_activity_tag,budget_control.group_budget_control_manager,1,1,1,1 +user_budget_activity_tag,user_budget_activity_tag,model_budget_activity_tag,budget_control.group_budget_control_user,1,0,0,0 diff --git a/budget_activity/static/description/icon.png b/budget_activity/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/budget_activity/static/description/index.html b/budget_activity/static/description/index.html new file mode 100644 index 00000000..a2e52fd2 --- /dev/null +++ b/budget_activity/static/description/index.html @@ -0,0 +1,444 @@ + + + + + + +Budget Activity + + + +
+

Budget Activity

+ + +

Alpha License: AGPL-3 OCA/account-budgeting Translate me on Weblate Try me on Runbot

+

This module add “Activity” element for budget actual (move, move line, analytic line)

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Usage

+
    +
  1. Go to Budgeting > Configurations > Budget Activity
  2. +
  3. Create new activity and match with Account > Save
  4. +
  5. On invoice lines, User can select Activity field and it will auto change account to match with Activity
  6. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Ecosoft
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

kittiu

+

This module is part of the OCA/account-budgeting project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/budget_activity/tests/__init__.py b/budget_activity/tests/__init__.py new file mode 100644 index 00000000..dd391c72 --- /dev/null +++ b/budget_activity/tests/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import test_budget_activity diff --git a/budget_activity/tests/test_budget_activity.py b/budget_activity/tests/test_budget_activity.py new file mode 100644 index 00000000..c5651e9d --- /dev/null +++ b/budget_activity/tests/test_budget_activity.py @@ -0,0 +1,69 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from freezegun import freeze_time + +from odoo.exceptions import UserError +from odoo.tests import tagged +from odoo.tests.common import Form + +from odoo.addons.budget_control.tests.common import BudgetControlCommon + + +@tagged("post_install", "-at_install") +class TestBudgetControl(BudgetControlCommon): + @classmethod + @freeze_time("2001-02-01") + def setUpClass(cls): + super().setUpClass() + BudgetActivity = cls.env["budget.activity"] # Create sample activity + cls.activity1 = BudgetActivity.create( + {"name": "Activity 1", "account_id": cls.account_kpi1.id} + ) + cls.activity2 = BudgetActivity.create( + {"name": "Activity 2", "account_id": cls.account_kpi2.id} + ) + + @freeze_time("2001-02-01") + def test_01_budget_activity_account(self): + """ + On vendor bill, + - If no activity, budget follows product's account + - If activity is selected, account follows activity's regardless of product + - User can always change account code afterwards + - Posting invoice, will create budget move with activity + """ + self.budget_period.account = False + invoice = self._create_simple_bill(self.costcenter1, self.account_kpi1, 10) + # Change to product2, account should change to account_kpi2 + with Form(invoice) as invoice_form: + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.product_id = self.product2 + line_form.price_unit = 10 # Change product, amount will reset + invoice_form.save() + self.assertEqual(self.account_kpi2, invoice.invoice_line_ids[0].account_id) + # Set activity1, account should change to account_kpi1 + with Form(invoice) as invoice_form: + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.activity_id = self.activity1 + invoice_form.save() + self.assertEqual(self.account_kpi1, invoice.invoice_line_ids[0].account_id) + # Set account only to account_kpi3 + with Form(invoice) as invoice_form: + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.account_id = self.account_kpi3 + invoice_form.save() + # Invoice line is not set up as following, + self.assertEqual(self.account_kpi3, invoice.invoice_line_ids[0].account_id) + self.assertEqual(self.product2, invoice.invoice_line_ids[0].product_id) + self.assertEqual(self.activity1, invoice.invoice_line_ids[0].activity_id) + with self.assertRaises(UserError): + invoice.action_post() # Account in Activity and Account is not equal. + # Reset state and set account = account in activity + invoice.invoice_line_ids[0].account_id = self.activity1.account_id + invoice.state = "draft" + # All values will be passed to budget move + invoice.action_post() + self.assertEqual(self.account_kpi1, invoice.budget_move_ids[0].account_id) + self.assertEqual(self.product2, invoice.budget_move_ids[0].product_id) + self.assertEqual(self.activity1, invoice.budget_move_ids[0].activity_id) diff --git a/budget_activity/views/account_move_views.xml b/budget_activity/views/account_move_views.xml new file mode 100644 index 00000000..d4f2167d --- /dev/null +++ b/budget_activity/views/account_move_views.xml @@ -0,0 +1,22 @@ + + + + account.move.form + account.move + + + + + + + + + + + diff --git a/budget_activity/views/budget_activity_group_view.xml b/budget_activity/views/budget_activity_group_view.xml new file mode 100644 index 00000000..d91cf1d8 --- /dev/null +++ b/budget_activity/views/budget_activity_group_view.xml @@ -0,0 +1,79 @@ + + + + budget.activity.group.view.tree + budget.activity.group + + + + + + + + view.budget.activity.group.filter + budget.activity.group + + + + + + + + + + budget.activity.group.view.form + budget.activity.group + +
+ + +
+
+ + + + + + + + + + + + + +
+
+
+
+ + Budget Activity Group + + budget.activity.group + tree,form + + +
diff --git a/budget_activity/views/budget_activity_view.xml b/budget_activity/views/budget_activity_view.xml new file mode 100644 index 00000000..79621880 --- /dev/null +++ b/budget_activity/views/budget_activity_view.xml @@ -0,0 +1,199 @@ + + + + + budget.activity.tag.view.tree + budget.activity.tag + + + + + + + + + + budget.activity.tag.view.search + budget.activity.tag + + + + + + + + + + + + + budget.activity.view.form + budget.activity.tag + +
+ + + + + + + + + + + + +
+
+
+ + Budget Activity Tags + + budget.activity.tag + tree,form + + + + budget.activity.view.tree + budget.activity + + + + + + + + + + + view.budget.activity.filter + budget.activity + + + + + + + + + + + + + + budget.activity.view.form + budget.activity + +
+ + +
+
+ + + + + + + + + + +
+
+
+
+ + Budget Activity + + budget.activity + tree,form + + + + + +
diff --git a/budget_activity/views/budget_menuitem.xml b/budget_activity/views/budget_menuitem.xml new file mode 100644 index 00000000..01450c42 --- /dev/null +++ b/budget_activity/views/budget_menuitem.xml @@ -0,0 +1,10 @@ + + + + diff --git a/budget_activity/views/budget_move_adjustment_view.xml b/budget_activity/views/budget_move_adjustment_view.xml new file mode 100644 index 00000000..0301b7b2 --- /dev/null +++ b/budget_activity/views/budget_move_adjustment_view.xml @@ -0,0 +1,25 @@ + + + + view.budget.move.adjustment.form + budget.move.adjustment + + + + + + + + + + + diff --git a/budget_activity/views/mis_budget_item.xml b/budget_activity/views/mis_budget_item.xml new file mode 100644 index 00000000..85d2de64 --- /dev/null +++ b/budget_activity/views/mis_budget_item.xml @@ -0,0 +1,16 @@ + + + + mis.budget.item.tree.view.readonly + mis.budget.item + + + + + + + + diff --git a/budget_activity/views/mis_report.xml b/budget_activity/views/mis_report.xml new file mode 100644 index 00000000..add622c2 --- /dev/null +++ b/budget_activity/views/mis_report.xml @@ -0,0 +1,21 @@ + + + + + mis.report.view.form + mis.report + + + + + + + + + + + diff --git a/budget_activity/views/mis_report_kpi.xml b/budget_activity/views/mis_report_kpi.xml new file mode 100644 index 00000000..e03ce25b --- /dev/null +++ b/budget_activity/views/mis_report_kpi.xml @@ -0,0 +1,26 @@ + + + + + mis.report.view.kpi.form + mis.report.kpi + + + + + + + + + + diff --git a/budget_activity/views/res_config_settings_views.xml b/budget_activity/views/res_config_settings_views.xml new file mode 100644 index 00000000..80620b3d --- /dev/null +++ b/budget_activity/views/res_config_settings_views.xml @@ -0,0 +1,32 @@ + + + + res.config.settings.view.form.inherit.budget + res.config.settings + + + +

Activity

+
+
+
+ +
+
+
+
+
+
+
+
+
From b6ff33b5f036d7b613b80c41b1ac82c9db24146d Mon Sep 17 00:00:00 2001 From: ps-tubtim Date: Wed, 11 Jan 2023 10:19:06 +0700 Subject: [PATCH 02/13] Initial 15.0 --- budget_activity/README.rst | 97 ---- budget_activity/__init__.py | 3 - budget_activity/__manifest__.py | 28 -- budget_activity/models/__init__.py | 15 - .../models/account_analytic_line.py | 13 - budget_activity/models/account_move.py | 38 -- budget_activity/models/base_budget_move.py | 72 --- budget_activity/models/budget_activity.py | 75 --- .../models/budget_activity_group.py | 21 - budget_activity/models/budget_control.py | 20 - .../models/budget_move_adjustment.py | 19 - budget_activity/models/budget_period.py | 57 --- budget_activity/models/mis_budget_item.py | 13 - budget_activity/models/mis_report.py | 31 -- budget_activity/models/mis_report_instance.py | 16 - budget_activity/models/mis_report_kpi.py | 74 --- budget_activity/models/res_config_settings.py | 12 - budget_activity/readme/CONTRIBUTORS.rst | 5 - budget_activity/readme/DESCRIPTION.rst | 1 - budget_activity/readme/USAGE.rst | 16 - budget_activity/report/__init__.py | 2 - .../report/budget_monitor_report.py | 48 -- .../report/budget_monitor_report_view.xml | 39 -- .../security/budget_activity_security.xml | 27 -- budget_activity/security/ir.model.access.csv | 7 - budget_activity/static/description/icon.png | Bin 9455 -> 0 bytes budget_activity/static/description/index.html | 444 ------------------ budget_activity/tests/__init__.py | 2 - budget_activity/tests/test_budget_activity.py | 69 --- budget_activity/views/account_move_views.xml | 22 - .../views/budget_activity_group_view.xml | 79 ---- .../views/budget_activity_view.xml | 199 -------- budget_activity/views/budget_menuitem.xml | 10 - .../views/budget_move_adjustment_view.xml | 25 - budget_activity/views/mis_budget_item.xml | 16 - budget_activity/views/mis_report.xml | 21 - budget_activity/views/mis_report_kpi.xml | 26 - .../views/res_config_settings_views.xml | 32 -- 38 files changed, 1694 deletions(-) delete mode 100644 budget_activity/README.rst delete mode 100644 budget_activity/__init__.py delete mode 100644 budget_activity/__manifest__.py delete mode 100644 budget_activity/models/__init__.py delete mode 100644 budget_activity/models/account_analytic_line.py delete mode 100644 budget_activity/models/account_move.py delete mode 100644 budget_activity/models/base_budget_move.py delete mode 100644 budget_activity/models/budget_activity.py delete mode 100644 budget_activity/models/budget_activity_group.py delete mode 100644 budget_activity/models/budget_control.py delete mode 100644 budget_activity/models/budget_move_adjustment.py delete mode 100644 budget_activity/models/budget_period.py delete mode 100644 budget_activity/models/mis_budget_item.py delete mode 100644 budget_activity/models/mis_report.py delete mode 100644 budget_activity/models/mis_report_instance.py delete mode 100644 budget_activity/models/mis_report_kpi.py delete mode 100644 budget_activity/models/res_config_settings.py delete mode 100644 budget_activity/readme/CONTRIBUTORS.rst delete mode 100644 budget_activity/readme/DESCRIPTION.rst delete mode 100644 budget_activity/readme/USAGE.rst delete mode 100644 budget_activity/report/__init__.py delete mode 100644 budget_activity/report/budget_monitor_report.py delete mode 100644 budget_activity/report/budget_monitor_report_view.xml delete mode 100644 budget_activity/security/budget_activity_security.xml delete mode 100644 budget_activity/security/ir.model.access.csv delete mode 100644 budget_activity/static/description/icon.png delete mode 100644 budget_activity/static/description/index.html delete mode 100644 budget_activity/tests/__init__.py delete mode 100644 budget_activity/tests/test_budget_activity.py delete mode 100644 budget_activity/views/account_move_views.xml delete mode 100644 budget_activity/views/budget_activity_group_view.xml delete mode 100644 budget_activity/views/budget_activity_view.xml delete mode 100644 budget_activity/views/budget_menuitem.xml delete mode 100644 budget_activity/views/budget_move_adjustment_view.xml delete mode 100644 budget_activity/views/mis_budget_item.xml delete mode 100644 budget_activity/views/mis_report.xml delete mode 100644 budget_activity/views/mis_report_kpi.xml delete mode 100644 budget_activity/views/res_config_settings_views.xml diff --git a/budget_activity/README.rst b/budget_activity/README.rst deleted file mode 100644 index 585c0410..00000000 --- a/budget_activity/README.rst +++ /dev/null @@ -1,97 +0,0 @@ -=============== -Budget Activity -=============== - -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! This file is generated by oca-gen-addon-readme !! - !! changes will be overwritten. !! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png - :target: https://odoo-community.org/page/development-status - :alt: Alpha -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--budgeting-lightgray.png?logo=github - :target: https://github.com/OCA/account-budgeting/tree/14.0/budget_activity - :alt: OCA/account-budgeting -.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-budgeting-14-0/account-budgeting-14-0-budget_activity - :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/88/14.0 - :alt: Try me on Runbot - -|badge1| |badge2| |badge3| |badge4| |badge5| - -This module add "Activity" element for budget actual (move, move line, analytic line) - -.. IMPORTANT:: - This is an alpha version, the data model and design can change at any time without warning. - Only for development or testing purpose, do not use in production. - `More details on development status `_ - -**Table of contents** - -.. contents:: - :local: - -Usage -===== - -#. Go to Budgeting > Configurations > Budget Activity -#. Create new activity and match with Account > Save -#. On invoice lines, User can select Activity field and it will auto change account to match with Activity - -Bug Tracker -=========== - -Bugs are tracked on `GitHub Issues `_. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. - -Do not contact contributors directly about support or help with technical issues. - -Credits -======= - -Authors -~~~~~~~ - -* Ecosoft - -Contributors -~~~~~~~~~~~~ - -* `Ecosoft `__: - - * Kitti Upariphutthiphong - * Saran Lim. - * Pimolnat Suntian - -Maintainers -~~~~~~~~~~~ - -This module is maintained by the OCA. - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -OCA, or the Odoo Community Association, is a nonprofit organization whose -mission is to support the collaborative development of Odoo features and -promote its widespread use. - -.. |maintainer-kittiu| image:: https://github.com/kittiu.png?size=40px - :target: https://github.com/kittiu - :alt: kittiu - -Current `maintainer `__: - -|maintainer-kittiu| - -This module is part of the `OCA/account-budgeting `_ project on GitHub. - -You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/budget_activity/__init__.py b/budget_activity/__init__.py deleted file mode 100644 index 08d4ad0b..00000000 --- a/budget_activity/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import models -from . import report diff --git a/budget_activity/__manifest__.py b/budget_activity/__manifest__.py deleted file mode 100644 index c102ebde..00000000 --- a/budget_activity/__manifest__.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -{ - "name": "Budget Activity", - "version": "14.0.1.0.0", - "category": "Accounting", - "license": "AGPL-3", - "author": "Ecosoft, Odoo Community Association (OCA)", - "website": "https://github.com/OCA/account-budgeting", - "depends": ["budget_control"], - "data": [ - "security/budget_activity_security.xml", - "security/ir.model.access.csv", - "report/budget_monitor_report_view.xml", - "views/res_config_settings_views.xml", - "views/mis_report.xml", - "views/mis_report_kpi.xml", - "views/mis_budget_item.xml", - "views/account_move_views.xml", - "views/budget_activity_view.xml", - "views/budget_activity_group_view.xml", - "views/budget_menuitem.xml", - "views/budget_move_adjustment_view.xml", - ], - "installable": True, - "maintainers": ["kittiu"], - "development_status": "Alpha", -} diff --git a/budget_activity/models/__init__.py b/budget_activity/models/__init__.py deleted file mode 100644 index f5516b20..00000000 --- a/budget_activity/models/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from . import res_config_settings -from . import account_move -from . import budget_activity -from . import budget_activity_group -from . import account_analytic_line -from . import mis_budget_item -from . import mis_report -from . import mis_report_kpi -from . import mis_report_instance -from . import base_budget_move -from . import budget_period -from . import budget_control -from . import budget_move_adjustment diff --git a/budget_activity/models/account_analytic_line.py b/budget_activity/models/account_analytic_line.py deleted file mode 100644 index e6130e8f..00000000 --- a/budget_activity/models/account_analytic_line.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class AccountAnalyticLine(models.Model): - _inherit = "account.analytic.line" - - activity_id = fields.Many2one( - comodel_name="budget.activity", - string="Activity", - index=True, - ) diff --git a/budget_activity/models/account_move.py b/budget_activity/models/account_move.py deleted file mode 100644 index e63934e4..00000000 --- a/budget_activity/models/account_move.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models - - -class AccountMoveLine(models.Model): - _inherit = "account.move.line" - - activity_id = fields.Many2one( - comodel_name="budget.activity", - string="Activity", - index=True, - ) - - def _get_computed_account(self): - self.ensure_one() - account = super()._get_computed_account() - # Case non-realtime inventory product - # activity's account takes priority over product's account - realtime = ( - self.product_id.type == "product" - and hasattr(self.product_id.categ_id, "property_valuation") - and self.product_id.categ_id.property_valuation == "real_time" - ) - if not realtime and self.activity_id: - account = self.activity_id.account_id - return account - - def _prepare_analytic_line(self): - res = super()._prepare_analytic_line() - for i, ml in enumerate(self): - res[i]["activity_id"] = ml.activity_id.id - return res - - @api.onchange("activity_id") - def _onchange_activity_id(self): - if self.activity_id: - self.account_id = self.activity_id.account_id diff --git a/budget_activity/models/base_budget_move.py b/budget_activity/models/base_budget_move.py deleted file mode 100644 index 16dd8f1c..00000000 --- a/budget_activity/models/base_budget_move.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, api, fields, models -from odoo.exceptions import UserError - - -class BaseBudgetMove(models.AbstractModel): - _inherit = "base.budget.move" - - activity_id = fields.Many2one( - comodel_name="budget.activity", - string="Activity", - index=True, - ) - activity_group_id = fields.Many2one( - comodel_name="budget.activity.group", - string="Activity Group", - related="activity_id.activity_group_id", - ) - account_id = fields.Many2one( - compute="_compute_activity_account", - store=True, - help="When activity is selected, always use activity's account to " - "enusre that the KPI budgeting which rely on account is valid. " - "Because in some case, i.e., perpetual inventory, " - "account in account.move.line can be different with " - "account in account.budget.move.", - ) - - @api.depends("activity_id") - def _compute_activity_account(self): - for rec in self: - rec.account_id = ( - rec.activity_id.account_id if rec.activity_id else rec.account_id - ) - - @api.constrains("activity_id", "account_id") - def _check_activity_account(self): - for rec in self.filtered("activity_id"): - if rec.account_id != rec.activity_id.account_id: - raise UserError( - _("Account not equal to Activity's Account: %s") - % rec.activity_id.account_id.display_name - ) - - -class BudgetDoclineMixinBase(models.AbstractModel): - _inherit = "budget.docline.mixin.base" - - activity_id = fields.Many2one( - comodel_name="budget.activity", - string="Activity", - domain=lambda self: self._domain_activity(), - index=True, - ) - - def _domain_activity(self): - return [] - - -class BudgetDoclineMixin(models.AbstractModel): - _inherit = "budget.docline.mixin" - - def _update_budget_commitment(self, budget_vals, reverse=False): - budget_vals = super()._update_budget_commitment(budget_vals, reverse=reverse) - budget_vals["activity_id"] = self.activity_id.id - # For case object without account_id (PR/PO), normally account is from - # product, it should now changed to follow activity. - # But if account_id is part of object (INV), use whatever is passed-in. - if "account_id" not in self: - budget_vals["account_id"] = self["activity_id"].account_id.id - return budget_vals diff --git a/budget_activity/models/budget_activity.py b/budget_activity/models/budget_activity.py deleted file mode 100644 index f237eea3..00000000 --- a/budget_activity/models/budget_activity.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models - - -class BudgetActivityTag(models.Model): - _name = "budget.activity.tag" - _description = "Budget Activity Tag" - - name = fields.Char(string="Name", required=True) - company_id = fields.Many2one( - comodel_name="res.company", - string="Company", - default=lambda self: self.env.company, - required=True, - ) - active = fields.Boolean( - default=True, - help="Set active to false to hide the Budget Activity Tag without removing it.", - ) - _sql_constraints = [ - ( - "name_company_unique", - "unique(name, company_id)", - "This tag name is already used!", - ) - ] - - -class BudgetActivity(models.Model): - _name = "budget.activity" - _description = "Budget Activity" - - name = fields.Char( - required=True, - ) - active = fields.Boolean(default=True) - activity_group_id = fields.Many2one( - comodel_name="budget.activity.group", - index=True, - ondelete="restrict", - ) - account_id = fields.Many2one( - comodel_name="account.account", - string="Account", - compute="_compute_account_id", - store=True, - domain=[("deprecated", "=", False)], - readonly=False, - required=False, - ) - company_id = fields.Many2one( - comodel_name="res.company", - string="Company", - default=lambda self: self.env.company, - required=True, - ) - code = fields.Char(related="account_id.code") - tag_ids = fields.Many2many( - comodel_name="budget.activity.tag", - relation="budget_activity_tag_rel", - column1="budget_activity_id", - column2="budget_activity_tag_id", - string="Tags", - help="Optional tags you may want to assign for search", - ) - - @api.depends("activity_group_id") - def _compute_account_id(self): - for rec in self: - rec.account_id = ( - not rec.account_id - and rec.activity_group_id.account_id - or rec.account_id - ) diff --git a/budget_activity/models/budget_activity_group.py b/budget_activity/models/budget_activity_group.py deleted file mode 100644 index 1566d024..00000000 --- a/budget_activity/models/budget_activity_group.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class BudgetActivityGroup(models.Model): - _name = "budget.activity.group" - _description = "Budget Activity Group" - - name = fields.Char( - required=True, - ) - active = fields.Boolean(default=True) - activity_ids = fields.One2many( - comodel_name="budget.activity", inverse_name="activity_group_id" - ) - account_id = fields.Many2one( - comodel_name="account.account", - string="Default Account", - domain=[("deprecated", "=", False)], - ) diff --git a/budget_activity/models/budget_control.py b/budget_activity/models/budget_control.py deleted file mode 100644 index 0c8adfda..00000000 --- a/budget_activity/models/budget_control.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import models - - -class BudgetControl(models.Model): - _inherit = "budget.control" - - def _get_context_budget_monitoring(self): - ctx = super()._get_context_budget_monitoring() - ctx.update({"search_default_group_by_activity_group": 1}) - return ctx - - def _get_value_items(self, date_range, kpi_expression): - items = super()._get_value_items(date_range, kpi_expression) - activity_group_id = kpi_expression.kpi_id.activity_group_id.id - for item in items: - item["activity_group_id"] = activity_group_id - return items diff --git a/budget_activity/models/budget_move_adjustment.py b/budget_activity/models/budget_move_adjustment.py deleted file mode 100644 index 75e9d7e4..00000000 --- a/budget_activity/models/budget_move_adjustment.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, fields, models - - -class BudgetMoveAdjustmentItem(models.Model): - _inherit = "budget.move.adjustment.item" - - activity_id = fields.Many2one( - comodel_name="budget.activity", - string="Activity", - index=True, - ) - - @api.onchange("activity_id") - def _onchange_activity_id(self): - if self.activity_id: - self.account_id = self.activity_id.account_id diff --git a/budget_activity/models/budget_period.py b/budget_activity/models/budget_period.py deleted file mode 100644 index 470f6426..00000000 --- a/budget_activity/models/budget_period.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, api, models -from odoo.exceptions import UserError - - -class BudgetPeriod(models.Model): - _inherit = "budget.period" - - def _prepare_controls_activity(self, budget_period, doclines): - controls = set() - control_analytics = budget_period.control_analytic_account_ids - budget_moves = doclines.mapped(doclines._budget_field()) - # get budget move from period only - budget_moves_period = budget_moves.filtered( - lambda l: l.date >= budget_period.bm_date_from - and l.date <= budget_period.bm_date_to - ) - for i in budget_moves_period: - if budget_period.control_all_analytic_accounts: - if i.analytic_account_id and i.activity_id: - controls.add((i.analytic_account_id.id, i.activity_id.id)) - else: # Only analtyic in control - if i.analytic_account_id in control_analytics and i.activity_id: - controls.add((i.analytic_account_id.id, i.activity_id.id)) - # Convert to list of dict, for readibility - return [{"analytic_id": x[0], "activity_id": x[1]} for x in controls] - - @api.model - def _prepare_controls(self, budget_period, doclines): - if budget_period.report_id.is_activity: - return self._prepare_controls_activity(budget_period, doclines) - return super()._prepare_controls(budget_period, doclines) - - @api.model - def _get_kpi_by_control_key(self, instance, kpis, control): - if instance.report_id.is_activity: - activity_id = control["activity_id"] - kpi = kpis.get(activity_id, []) - if len(kpi) == 1: - return kpi - # Invalid KPI - activity = self.env["budget.activity"].browse(activity_id) - if not kpi: - raise UserError( - _("Chosen activity %s is not valid for budgeting") - % activity.display_name - ) - else: - raise UserError( - _( - "KPI Template '%s' has more than one KPI being " - "refereced by same activity %s" - ) - % (instance.report_id.name, activity.display_name) - ) - return super()._get_kpi_by_control_key(instance, kpis, control) diff --git a/budget_activity/models/mis_budget_item.py b/budget_activity/models/mis_budget_item.py deleted file mode 100644 index 33b0d244..00000000 --- a/budget_activity/models/mis_budget_item.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class MisBudgetItem(models.Model): - _inherit = "mis.budget.item" - - activity_group_id = fields.Many2one( - comodel_name="budget.activity.group", - ondelete="restrict", - index=True, - ) diff --git a/budget_activity/models/mis_report.py b/budget_activity/models/mis_report.py deleted file mode 100644 index d0e419a1..00000000 --- a/budget_activity/models/mis_report.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from collections import defaultdict - -from odoo import fields, models - - -class MisReport(models.Model): - _inherit = "mis.report" - - is_activity = fields.Boolean( - help="if check, Expression will compute Activity instead Account" - ) - - def get_kpis(self, company): - self.ensure_one() - if self.is_activity: - return self.get_kpis_by_activity_id(company) - return super().get_kpis(company) - - def get_kpis_by_activity_id(self, company): - """Return { activity_id: set(kpi) }""" - res = defaultdict(set) - for kpi in self.kpi_ids: - for expression in kpi.expression_ids: - if not expression.name: - continue - activity_ids = kpi.activity_group_id.activity_ids.ids - for activity_id in activity_ids: - res[activity_id].add(kpi) - return res diff --git a/budget_activity/models/mis_report_instance.py b/budget_activity/models/mis_report_instance.py deleted file mode 100644 index 7a3a5c23..00000000 --- a/budget_activity/models/mis_report_instance.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models - - -class MisReportInstance(models.Model): - _inherit = "mis.report.instance" - - def _get_context_filter_matrix(self): - ctx = super()._get_context_filter_matrix() - if ctx.get("filter_analytic_ids") and ctx.get("filter_activity_group"): - ctx["mis_report_filters"]["activity_group_id"] = { - "value": ctx["filter_activity_group"], - "operator": "all", - } - return ctx diff --git a/budget_activity/models/mis_report_kpi.py b/budget_activity/models/mis_report_kpi.py deleted file mode 100644 index e9f56780..00000000 --- a/budget_activity/models/mis_report_kpi.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from odoo import _, api, fields, models -from odoo.exceptions import UserError - - -class MisReportKpi(models.Model): - _inherit = "mis.report.kpi" - - description = fields.Char( - compute="_compute_name_activity", store=True, readonly=False - ) - activity_expression = fields.Boolean( - compute="_compute_is_activity", - readonly=False, - store=True, - ) - activity_group_id = fields.Many2one( - comodel_name="budget.activity.group", - index=True, - string="Activity Group", - ) - respectively_variation = fields.Char( - help="respectively variation over the " - "period (p), initial balance (i), ending balance (e)" - ) - - @api.depends("activity_group_id") - def _compute_name_activity(self): - for rec in self: - rec.description = False - if rec.activity_expression and rec.activity_group_id: - rec.description = rec.activity_group_id.name - - @api.depends("report_id.is_activity") - def _compute_is_activity(self): - for rec in self: - rec.activity_expression = rec.report_id.is_activity - - def _filter_balance_mis(self, activity_ids): - self.ensure_one() - dom = "('activity_id', 'in', {})".format(tuple(activity_ids.ids)) - return dom - - @api.depends( - "expression_ids.subkpi_id.name", - "expression_ids.name", - "activity_group_id.activity_ids", - "respectively_variation", - ) - def _compute_expression(self): - super()._compute_expression() - for kpi in self: - if kpi.activity_expression and kpi.activity_group_id: - activity_ids = kpi.activity_group_id.activity_ids - if not activity_ids: - raise UserError( - _( - "Activity Group {} has no activities".format( - kpi.activity_group_id.name - ) - ) - ) - accounts = activity_ids.mapped("account_id") - account_str = "[%s]" % ",".join([acc.code for acc in accounts]) - kpi.expression = "bal{}{}[{}]".format( - kpi.respectively_variation or "", - account_str, - kpi._filter_balance_mis(activity_ids), - ) - # Update expression_ids for display realtime - # TODO: please check follwoing logic again. - kpi._inverse_expression() diff --git a/budget_activity/models/res_config_settings.py b/budget_activity/models/res_config_settings.py deleted file mode 100644 index dfde5f28..00000000 --- a/budget_activity/models/res_config_settings.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class ResConfigSettings(models.TransientModel): - _inherit = "res.config.settings" - - group_budget_activity_tag = fields.Boolean( - string="Budget Activity Tags", - implied_group="budget_activity.group_budget_activity_tag", - ) diff --git a/budget_activity/readme/CONTRIBUTORS.rst b/budget_activity/readme/CONTRIBUTORS.rst deleted file mode 100644 index 92e451a5..00000000 --- a/budget_activity/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1,5 +0,0 @@ -* `Ecosoft `__: - - * Kitti Upariphutthiphong - * Saran Lim. - * Pimolnat Suntian diff --git a/budget_activity/readme/DESCRIPTION.rst b/budget_activity/readme/DESCRIPTION.rst deleted file mode 100644 index f2dffd6b..00000000 --- a/budget_activity/readme/DESCRIPTION.rst +++ /dev/null @@ -1 +0,0 @@ -This module add "Activity" element for budget actual (move, move line, analytic line) diff --git a/budget_activity/readme/USAGE.rst b/budget_activity/readme/USAGE.rst deleted file mode 100644 index f26ddbe4..00000000 --- a/budget_activity/readme/USAGE.rst +++ /dev/null @@ -1,16 +0,0 @@ -To used this module you have to configued Budget Activity and Budget Activity Group first. - -#. Go to Budgeting > Configurations > Budget Activity -#. Create new activity and match with Account > Save -#. On invoice lines, User can select Activity field and it will auto change account to match with Activity - - -How to configure an Activity Group with a MIS Builder: - -#. Go to Invoicing > Configuration > MIS Reporting > MIS Report Templates -#. Choose you template that you would to see report by activity group -#. check 'Is Activity' -#. create new KPI line and select Activity Group field -#. It will generate expression related with activity group > Save - -Then, you can test by create new bills and view your report. diff --git a/budget_activity/report/__init__.py b/budget_activity/report/__init__.py deleted file mode 100644 index 9ec4b2a8..00000000 --- a/budget_activity/report/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import budget_monitor_report diff --git a/budget_activity/report/budget_monitor_report.py b/budget_activity/report/budget_monitor_report.py deleted file mode 100644 index eaa3c454..00000000 --- a/budget_activity/report/budget_monitor_report.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models - - -class BudgetMonitorReport(models.Model): - _inherit = "budget.monitor.report" - - activity = fields.Char(string="Activity") - activity_group = fields.Char(string="Activity Group") - - # Budget - def _select_budget(self): - select_budget_query = super()._select_budget() - select_budget_query[ - 20 - ] = "bag.description as activity_group, null::char as activity" - return select_budget_query - - def _from_budget(self): - from_budget_query = super()._from_budget() - from_budget_query = "\n".join( - [ - from_budget_query, - "join mis_report_kpi_expression mrke on " - "a.kpi_expression_id = mrke.id", - "join mis_report_kpi bag on mrke.kpi_id = bag.id", - ] - ) - return from_budget_query - - # All consumed - def _select_statement(self, amount_type): - select_statement = super()._select_statement(amount_type) - select_statement[20] = "bag.name as activity_group, ba.name as activity" - return select_statement - - def _from_statement(self, amount_type): - from_statment = super()._from_statement(amount_type) - from_statment = "\n".join( - [ - from_statment, - "left outer join budget_activity ba " "on a.activity_id = ba.id ", - "left outer join budget_activity_group bag " - "on ba.activity_group_id = bag.id", - ] - ) - return from_statment diff --git a/budget_activity/report/budget_monitor_report_view.xml b/budget_activity/report/budget_monitor_report_view.xml deleted file mode 100644 index 700f1970..00000000 --- a/budget_activity/report/budget_monitor_report_view.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - budget.monitor.report.tree - budget.monitor.report - - - - - - - - - - budget.monitor.report.search - budget.monitor.report - - - - - - - - - diff --git a/budget_activity/security/budget_activity_security.xml b/budget_activity/security/budget_activity_security.xml deleted file mode 100644 index 63851257..00000000 --- a/budget_activity/security/budget_activity_security.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Budget Activity multi company rule - - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - - - Budget Activity Tag multi company rule - - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - - - - - Budget Activity Tags - - - - diff --git a/budget_activity/security/ir.model.access.csv b/budget_activity/security/ir.model.access.csv deleted file mode 100644 index a1a541c2..00000000 --- a/budget_activity/security/ir.model.access.csv +++ /dev/null @@ -1,7 +0,0 @@ -"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -manage_budget_activity,manage_budget_activity,model_budget_activity,budget_control.group_budget_control_manager,1,1,1,1 -user_budget_activity,user_budget_activity,model_budget_activity,budget_control.group_budget_control_user,1,0,0,0 -manage_budget_activity_group,manage_budget_activity_group,model_budget_activity_group,budget_control.group_budget_control_manager,1,1,1,1 -user_budget_activity_group,user_budget_activity_group,model_budget_activity_group,budget_control.group_budget_control_user,1,0,0,0 -manage_budget_activity_tag,manage_budget_activity_tag,model_budget_activity_tag,budget_control.group_budget_control_manager,1,1,1,1 -user_budget_activity_tag,user_budget_activity_tag,model_budget_activity_tag,budget_control.group_budget_control_user,1,0,0,0 diff --git a/budget_activity/static/description/icon.png b/budget_activity/static/description/icon.png deleted file mode 100644 index 3a0328b516c4980e8e44cdb63fd945757ddd132d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I diff --git a/budget_activity/static/description/index.html b/budget_activity/static/description/index.html deleted file mode 100644 index a2e52fd2..00000000 --- a/budget_activity/static/description/index.html +++ /dev/null @@ -1,444 +0,0 @@ - - - - - - -Budget Activity - - - -
-

Budget Activity

- - -

Alpha License: AGPL-3 OCA/account-budgeting Translate me on Weblate Try me on Runbot

-

This module add “Activity” element for budget actual (move, move line, analytic line)

-
-

Important

-

This is an alpha version, the data model and design can change at any time without warning. -Only for development or testing purpose, do not use in production. -More details on development status

-
-

Table of contents

- -
-

Usage

-
    -
  1. Go to Budgeting > Configurations > Budget Activity
  2. -
  3. Create new activity and match with Account > Save
  4. -
  5. On invoice lines, User can select Activity field and it will auto change account to match with Activity
  6. -
-
-
-

Bug Tracker

-

Bugs are tracked on GitHub Issues. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

-

Do not contact contributors directly about support or help with technical issues.

-
-
-

Credits

-
-

Authors

-
    -
  • Ecosoft
  • -
-
-
-

Contributors

- -
-
-

Maintainers

-

This module is maintained by the OCA.

-Odoo Community Association -

OCA, or the Odoo Community Association, is a nonprofit organization whose -mission is to support the collaborative development of Odoo features and -promote its widespread use.

-

Current maintainer:

-

kittiu

-

This module is part of the OCA/account-budgeting project on GitHub.

-

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

-
-
-
- - diff --git a/budget_activity/tests/__init__.py b/budget_activity/tests/__init__.py deleted file mode 100644 index dd391c72..00000000 --- a/budget_activity/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import test_budget_activity diff --git a/budget_activity/tests/test_budget_activity.py b/budget_activity/tests/test_budget_activity.py deleted file mode 100644 index c5651e9d..00000000 --- a/budget_activity/tests/test_budget_activity.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from freezegun import freeze_time - -from odoo.exceptions import UserError -from odoo.tests import tagged -from odoo.tests.common import Form - -from odoo.addons.budget_control.tests.common import BudgetControlCommon - - -@tagged("post_install", "-at_install") -class TestBudgetControl(BudgetControlCommon): - @classmethod - @freeze_time("2001-02-01") - def setUpClass(cls): - super().setUpClass() - BudgetActivity = cls.env["budget.activity"] # Create sample activity - cls.activity1 = BudgetActivity.create( - {"name": "Activity 1", "account_id": cls.account_kpi1.id} - ) - cls.activity2 = BudgetActivity.create( - {"name": "Activity 2", "account_id": cls.account_kpi2.id} - ) - - @freeze_time("2001-02-01") - def test_01_budget_activity_account(self): - """ - On vendor bill, - - If no activity, budget follows product's account - - If activity is selected, account follows activity's regardless of product - - User can always change account code afterwards - - Posting invoice, will create budget move with activity - """ - self.budget_period.account = False - invoice = self._create_simple_bill(self.costcenter1, self.account_kpi1, 10) - # Change to product2, account should change to account_kpi2 - with Form(invoice) as invoice_form: - with invoice_form.invoice_line_ids.edit(0) as line_form: - line_form.product_id = self.product2 - line_form.price_unit = 10 # Change product, amount will reset - invoice_form.save() - self.assertEqual(self.account_kpi2, invoice.invoice_line_ids[0].account_id) - # Set activity1, account should change to account_kpi1 - with Form(invoice) as invoice_form: - with invoice_form.invoice_line_ids.edit(0) as line_form: - line_form.activity_id = self.activity1 - invoice_form.save() - self.assertEqual(self.account_kpi1, invoice.invoice_line_ids[0].account_id) - # Set account only to account_kpi3 - with Form(invoice) as invoice_form: - with invoice_form.invoice_line_ids.edit(0) as line_form: - line_form.account_id = self.account_kpi3 - invoice_form.save() - # Invoice line is not set up as following, - self.assertEqual(self.account_kpi3, invoice.invoice_line_ids[0].account_id) - self.assertEqual(self.product2, invoice.invoice_line_ids[0].product_id) - self.assertEqual(self.activity1, invoice.invoice_line_ids[0].activity_id) - with self.assertRaises(UserError): - invoice.action_post() # Account in Activity and Account is not equal. - # Reset state and set account = account in activity - invoice.invoice_line_ids[0].account_id = self.activity1.account_id - invoice.state = "draft" - # All values will be passed to budget move - invoice.action_post() - self.assertEqual(self.account_kpi1, invoice.budget_move_ids[0].account_id) - self.assertEqual(self.product2, invoice.budget_move_ids[0].product_id) - self.assertEqual(self.activity1, invoice.budget_move_ids[0].activity_id) diff --git a/budget_activity/views/account_move_views.xml b/budget_activity/views/account_move_views.xml deleted file mode 100644 index d4f2167d..00000000 --- a/budget_activity/views/account_move_views.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - account.move.form - account.move - - - - - - - - - - - diff --git a/budget_activity/views/budget_activity_group_view.xml b/budget_activity/views/budget_activity_group_view.xml deleted file mode 100644 index d91cf1d8..00000000 --- a/budget_activity/views/budget_activity_group_view.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - budget.activity.group.view.tree - budget.activity.group - - - - - - - - view.budget.activity.group.filter - budget.activity.group - - - - - - - - - - budget.activity.group.view.form - budget.activity.group - -
- - -
-
- - - - - - - - - - - - - -
-
-
-
- - Budget Activity Group - - budget.activity.group - tree,form - - -
diff --git a/budget_activity/views/budget_activity_view.xml b/budget_activity/views/budget_activity_view.xml deleted file mode 100644 index 79621880..00000000 --- a/budget_activity/views/budget_activity_view.xml +++ /dev/null @@ -1,199 +0,0 @@ - - - - - budget.activity.tag.view.tree - budget.activity.tag - - - - - - - - - - budget.activity.tag.view.search - budget.activity.tag - - - - - - - - - - - - - budget.activity.view.form - budget.activity.tag - -
- - - - - - - - - - - - -
-
-
- - Budget Activity Tags - - budget.activity.tag - tree,form - - - - budget.activity.view.tree - budget.activity - - - - - - - - - - - view.budget.activity.filter - budget.activity - - - - - - - - - - - - - - budget.activity.view.form - budget.activity - -
- - -
-
- - - - - - - - - - -
-
-
-
- - Budget Activity - - budget.activity - tree,form - - - - - -
diff --git a/budget_activity/views/budget_menuitem.xml b/budget_activity/views/budget_menuitem.xml deleted file mode 100644 index 01450c42..00000000 --- a/budget_activity/views/budget_menuitem.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/budget_activity/views/budget_move_adjustment_view.xml b/budget_activity/views/budget_move_adjustment_view.xml deleted file mode 100644 index 0301b7b2..00000000 --- a/budget_activity/views/budget_move_adjustment_view.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - view.budget.move.adjustment.form - budget.move.adjustment - - - - - - - - - - - diff --git a/budget_activity/views/mis_budget_item.xml b/budget_activity/views/mis_budget_item.xml deleted file mode 100644 index 85d2de64..00000000 --- a/budget_activity/views/mis_budget_item.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - mis.budget.item.tree.view.readonly - mis.budget.item - - - - - - - - diff --git a/budget_activity/views/mis_report.xml b/budget_activity/views/mis_report.xml deleted file mode 100644 index add622c2..00000000 --- a/budget_activity/views/mis_report.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - mis.report.view.form - mis.report - - - - - - - - - - - diff --git a/budget_activity/views/mis_report_kpi.xml b/budget_activity/views/mis_report_kpi.xml deleted file mode 100644 index e03ce25b..00000000 --- a/budget_activity/views/mis_report_kpi.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - mis.report.view.kpi.form - mis.report.kpi - - - - - - - - - - diff --git a/budget_activity/views/res_config_settings_views.xml b/budget_activity/views/res_config_settings_views.xml deleted file mode 100644 index 80620b3d..00000000 --- a/budget_activity/views/res_config_settings_views.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - res.config.settings.view.form.inherit.budget - res.config.settings - - - -

Activity

-
-
-
- -
-
-
-
-
-
-
-
-
From dd9d1f20cb88ea77c84ebce7bc97420ad31e1113 Mon Sep 17 00:00:00 2001 From: Saran440 Date: Wed, 11 Jan 2023 10:55:58 +0700 Subject: [PATCH 03/13] [ADD] budgeting --- budget_activity/README.rst | 110 +++++ budget_activity/__init__.py | 4 + budget_activity/__manifest__.py | 26 + budget_activity/models/__init__.py | 10 + .../models/account_analytic_line.py | 13 + budget_activity/models/account_move.py | 40 ++ budget_activity/models/base_budget_move.py | 69 +++ budget_activity/models/budget_activity.py | 83 ++++ budget_activity/models/budget_kpi_template.py | 25 + .../models/budget_move_adjustment.py | 19 + budget_activity/models/budget_period.py | 37 ++ budget_activity/models/budget_subkpi.py | 14 + budget_activity/readme/CONTRIBUTORS.rst | 5 + budget_activity/readme/DESCRIPTION.rst | 5 + budget_activity/readme/USAGE.rst | 12 + budget_activity/report/__init__.py | 3 + .../report/budget_monitor_report.py | 32 ++ .../report/budget_monitor_report_view.xml | 33 ++ .../security/budget_activity_security.xml | 11 + budget_activity/security/ir.model.access.csv | 5 + budget_activity/static/description/icon.png | Bin 0 -> 9455 bytes budget_activity/static/description/index.html | 454 ++++++++++++++++++ budget_activity/tests/__init__.py | 2 + budget_activity/tests/test_budget_activity.py | 186 +++++++ budget_activity/views/account_move_views.xml | 28 ++ .../views/budget_activity_view.xml | 162 +++++++ budget_activity/views/budget_kpi_view.xml | 17 + budget_activity/views/budget_menuitem.xml | 23 + .../views/budget_move_adjustment_view.xml | 25 + .../views/budget_template_view.xml | 44 ++ 30 files changed, 1497 insertions(+) create mode 100644 budget_activity/README.rst create mode 100644 budget_activity/__init__.py create mode 100644 budget_activity/__manifest__.py create mode 100644 budget_activity/models/__init__.py create mode 100644 budget_activity/models/account_analytic_line.py create mode 100644 budget_activity/models/account_move.py create mode 100644 budget_activity/models/base_budget_move.py create mode 100644 budget_activity/models/budget_activity.py create mode 100644 budget_activity/models/budget_kpi_template.py create mode 100644 budget_activity/models/budget_move_adjustment.py create mode 100644 budget_activity/models/budget_period.py create mode 100644 budget_activity/models/budget_subkpi.py create mode 100644 budget_activity/readme/CONTRIBUTORS.rst create mode 100644 budget_activity/readme/DESCRIPTION.rst create mode 100644 budget_activity/readme/USAGE.rst create mode 100644 budget_activity/report/__init__.py create mode 100644 budget_activity/report/budget_monitor_report.py create mode 100644 budget_activity/report/budget_monitor_report_view.xml create mode 100644 budget_activity/security/budget_activity_security.xml create mode 100644 budget_activity/security/ir.model.access.csv create mode 100644 budget_activity/static/description/icon.png create mode 100644 budget_activity/static/description/index.html create mode 100644 budget_activity/tests/__init__.py create mode 100644 budget_activity/tests/test_budget_activity.py create mode 100644 budget_activity/views/account_move_views.xml create mode 100644 budget_activity/views/budget_activity_view.xml create mode 100644 budget_activity/views/budget_kpi_view.xml create mode 100644 budget_activity/views/budget_menuitem.xml create mode 100644 budget_activity/views/budget_move_adjustment_view.xml create mode 100644 budget_activity/views/budget_template_view.xml diff --git a/budget_activity/README.rst b/budget_activity/README.rst new file mode 100644 index 00000000..732c2da4 --- /dev/null +++ b/budget_activity/README.rst @@ -0,0 +1,110 @@ +=============== +Budget Activity +=============== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--budgeting-lightgray.png?logo=github + :target: https://github.com/OCA/account-budgeting/tree/15.0/budget_activity + :alt: OCA/account-budgeting +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-budgeting-15-0/account-budgeting-15-0-budget_activity + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/88/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module helps the system accurately record the account code for budget recording. +It includes an "Activity" linking the two, where the activity serves as a match between what the user understands and the account code. +The user can select from a list of activity that have been created in the system, +and the system will then match the selected activity to the corresponding account, +so the user does not need to understand the account themselves. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To used this module you have to configued Budget Activity first. + +#. Go to Budgeting > Activity > Budget Activity +#. Create new activity, select a KPI (if you want to group it) and match the activity to an account. +#. Add a `Keyword` if you need to search for other words to show this activity. For example, the activity name is `Activity1` and the keywords are `Ticket` and `Transportation`. In the Activity field, the user can search for the name `Ticket` to see the activity `Activity1`. +#. Go to Budgeting > Configurations > Budget Template +#. Create new template or use old template (if you have) +#. When you select a KPI, it will automatically select the corresponding activity and account. If you haven't set up an activity in the KPI, you can select the activity and it will automatically select the corresponding account + + +In the usage window, you will see a new field called "Activity" where the user can select from a list of options. +The system will then match the selected activity to the corresponding account that has been set up. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Ecosoft + +Contributors +~~~~~~~~~~~~ + +* `Ecosoft `__: + + * Kitti Upariphutthiphong + * Saran Lim. + * Pimolnat Suntian + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-kittiu| image:: https://github.com/kittiu.png?size=40px + :target: https://github.com/kittiu + :alt: kittiu + +Current `maintainer `__: + +|maintainer-kittiu| + +This module is part of the `OCA/account-budgeting `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/budget_activity/__init__.py b/budget_activity/__init__.py new file mode 100644 index 00000000..37e105d0 --- /dev/null +++ b/budget_activity/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models +from . import report diff --git a/budget_activity/__manifest__.py b/budget_activity/__manifest__.py new file mode 100644 index 00000000..2c832086 --- /dev/null +++ b/budget_activity/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Budget Activity", + "version": "15.0.1.0.0", + "category": "Accounting", + "license": "AGPL-3", + "author": "Ecosoft, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-analytic", + "depends": ["budget_control"], + "data": [ + "security/budget_activity_security.xml", + "security/ir.model.access.csv", + "report/budget_monitor_report_view.xml", + "views/account_move_views.xml", + "views/budget_activity_view.xml", + "views/budget_kpi_view.xml", + "views/budget_template_view.xml", + "views/budget_menuitem.xml", + "views/budget_move_adjustment_view.xml", + ], + "installable": True, + "maintainers": ["kittiu"], + "development_status": "Alpha", +} diff --git a/budget_activity/models/__init__.py b/budget_activity/models/__init__.py new file mode 100644 index 00000000..d91be8e1 --- /dev/null +++ b/budget_activity/models/__init__.py @@ -0,0 +1,10 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import account_move +from . import budget_activity +from . import account_analytic_line +from . import base_budget_move +from . import budget_subkpi +from . import budget_kpi_template +from . import budget_period +from . import budget_move_adjustment diff --git a/budget_activity/models/account_analytic_line.py b/budget_activity/models/account_analytic_line.py new file mode 100644 index 00000000..e6130e8f --- /dev/null +++ b/budget_activity/models/account_analytic_line.py @@ -0,0 +1,13 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class AccountAnalyticLine(models.Model): + _inherit = "account.analytic.line" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) diff --git a/budget_activity/models/account_move.py b/budget_activity/models/account_move.py new file mode 100644 index 00000000..8765d6ba --- /dev/null +++ b/budget_activity/models/account_move.py @@ -0,0 +1,40 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) + + def _is_realtime_inventory_product(self): + # Case non-realtime inventory product + # activity's account takes priority over product's account + if self.product_id.type != "product": + return False + if not hasattr(self.product_id.categ_id, "property_valuation"): + return False + return self.product_id.categ_id.property_valuation == "real_time" + + def _get_computed_account(self): + self.ensure_one() + if self.activity_id and not self._is_realtime_inventory_product(): + return self.activity_id.account_id + return super()._get_computed_account() + + def _prepare_analytic_line(self): + res = super()._prepare_analytic_line() + for i, ml in enumerate(self): + res[i]["activity_id"] = ml.activity_id.id + return res + + @api.onchange("activity_id") + def _onchange_activity_id(self): + if self.activity_id: + self.account_id = self.activity_id.account_id diff --git a/budget_activity/models/base_budget_move.py b/budget_activity/models/base_budget_move.py new file mode 100644 index 00000000..b4a5232b --- /dev/null +++ b/budget_activity/models/base_budget_move.py @@ -0,0 +1,69 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class BaseBudgetMove(models.AbstractModel): + _inherit = "base.budget.move" + _budget_control_field = "activity_id" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) + account_id = fields.Many2one( + compute="_compute_activity_account", + store=True, + help="When activity is selected, always use activity's account to " + "enusre that the KPI budgeting which rely on account is valid. " + "Because in some case, i.e., perpetual inventory, " + "account in account.move.line can be different with " + "account in account.budget.move.", + ) + + @api.depends("activity_id") + def _compute_activity_account(self): + for rec in self: + rec.account_id = ( + rec.activity_id.account_id if rec.activity_id else rec.account_id + ) + + @api.constrains("activity_id", "account_id") + def _check_activity_account(self): + for rec in self.filtered("activity_id"): + if rec.account_id != rec.activity_id.account_id: + raise UserError( + _("Account not equal to Activity's Account: %s") + % rec.activity_id.account_id.display_name + ) + + +class BudgetDoclineMixinBase(models.AbstractModel): + _inherit = "budget.docline.mixin.base" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + domain=lambda self: self._domain_activity(), + index=True, + ) + + def _domain_activity(self): + return [] + + +class BudgetDoclineMixin(models.AbstractModel): + _inherit = "budget.docline.mixin" + + def _update_budget_commitment(self, budget_vals, reverse=False): + budget_vals = super()._update_budget_commitment(budget_vals, reverse=reverse) + budget_vals["activity_id"] = self.activity_id.id + # For case object without account_id (PR/PO), normally account is from + # product, it should now changed to follow activity. + # But if account_id is part of object (INV), use whatever is passed-in. + if "account_id" not in self: + budget_vals["account_id"] = self["activity_id"].account_id.id + return budget_vals diff --git a/budget_activity/models/budget_activity.py b/budget_activity/models/budget_activity.py new file mode 100644 index 00000000..68d68372 --- /dev/null +++ b/budget_activity/models/budget_activity.py @@ -0,0 +1,83 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class BudgetActivity(models.Model): + _name = "budget.activity" + _description = "Budget Activity" + + name = fields.Char( + required=True, + ) + kpi_id = fields.Many2one( + comodel_name="budget.kpi", + index=True, + ) + active = fields.Boolean(default=True) + account_id = fields.Many2one( + comodel_name="account.account", + string="Account", + domain=[("deprecated", "=", False)], + readonly=False, + index=True, + required=False, + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + required=True, + ) + keyword_ids = fields.Many2many( + comodel_name="budget.activity.keyword", + relation="budget_activity_keyword_rel", + column1="budget_activity_id", + column2="budget_activity_keyword_id", + string="Keyword", + help="Optional keyword you may want to assign for search", + ) + + _sql_constraints = [ + ("name_uniq", "UNIQUE(name)", "Name must be unique!"), + ] + + @api.model + def name_search(self, name, args=None, operator="ilike", limit=100): + args = args or [] + domain = [] + if name: + domain = [ + "|", + "|", + ("name", operator, name), + ("keyword_ids", operator, name), + ("account_id", operator, name), + ] + activitys = self.search(domain + args, limit=limit) + return activitys.name_get() + + +class BudgetActivityKeyword(models.Model): + _name = "budget.activity.keyword" + _description = "Search budget activity with keyword" + + name = fields.Char(required=True) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + required=True, + ) + active = fields.Boolean( + default=True, + help="Set active to false to hide the Budget Activity keyword without removing it.", + ) + _sql_constraints = [ + ( + "name_company_unique", + "unique(name, company_id)", + "This keyword is already used!", + ) + ] diff --git a/budget_activity/models/budget_kpi_template.py b/budget_activity/models/budget_kpi_template.py new file mode 100644 index 00000000..30481f82 --- /dev/null +++ b/budget_activity/models/budget_kpi_template.py @@ -0,0 +1,25 @@ +# Copyright 2022 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class BudgetKPI(models.Model): + _inherit = "budget.template.line" + + activity_ids = fields.Many2many( + comodel_name="budget.activity", + relation="budget_kpi_activity_rel", + column1="budget_kpi_id", + column2="activity_id", + ondelete="restrict", + required=True, + ) + + @api.onchange("kpi_id") + def _onchange_kpi_id(self): + self.activity_ids = self.kpi_id.activity_ids.ids + + @api.onchange("activity_ids") + def _onchange_account_ids(self): + self.account_ids = self.activity_ids.mapped("account_id").ids diff --git a/budget_activity/models/budget_move_adjustment.py b/budget_activity/models/budget_move_adjustment.py new file mode 100644 index 00000000..75e9d7e4 --- /dev/null +++ b/budget_activity/models/budget_move_adjustment.py @@ -0,0 +1,19 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class BudgetMoveAdjustmentItem(models.Model): + _inherit = "budget.move.adjustment.item" + + activity_id = fields.Many2one( + comodel_name="budget.activity", + string="Activity", + index=True, + ) + + @api.onchange("activity_id") + def _onchange_activity_id(self): + if self.activity_id: + self.account_id = self.activity_id.account_id diff --git a/budget_activity/models/budget_period.py b/budget_activity/models/budget_period.py new file mode 100644 index 00000000..a1f11ec0 --- /dev/null +++ b/budget_activity/models/budget_period.py @@ -0,0 +1,37 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import _, api, models +from odoo.exceptions import UserError + + +class BudgetPeriod(models.Model): + _inherit = "budget.period" + + @api.model + def _get_kpi_by_control_key(self, template_lines, control): + activity_id = control["activity_id"] + template_line = self._get_filter_template_line(template_lines, control) + if len(template_line) == 1: + return template_line + # Invalid Template Lines + activity = self.env["budget.activity"].browse(activity_id) + if not template_line: + raise UserError( + _("Chosen activity %s is not valid for budgeting") + % activity.display_name + ) + raise UserError( + _( + "Template Lines has more than one KPI being " + "referenced by the same account code %s" + ) + % (activity.display_name) + ) + + def _get_filter_template_line(self, all_template_lines, control): + """Overwrite filter template line from account_id to activity_id""" + activity_id = control["activity_id"] + template_lines = all_template_lines.filtered( + lambda l: activity_id in l.activity_ids.ids + ) + return template_lines diff --git a/budget_activity/models/budget_subkpi.py b/budget_activity/models/budget_subkpi.py new file mode 100644 index 00000000..5f4dd18a --- /dev/null +++ b/budget_activity/models/budget_subkpi.py @@ -0,0 +1,14 @@ +# Copyright 2022 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class BudgetKPITemplate(models.Model): + _inherit = "budget.kpi" + + activity_ids = fields.One2many( + comodel_name="budget.activity", + inverse_name="kpi_id", + readonly=True, + ) diff --git a/budget_activity/readme/CONTRIBUTORS.rst b/budget_activity/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..92e451a5 --- /dev/null +++ b/budget_activity/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* `Ecosoft `__: + + * Kitti Upariphutthiphong + * Saran Lim. + * Pimolnat Suntian diff --git a/budget_activity/readme/DESCRIPTION.rst b/budget_activity/readme/DESCRIPTION.rst new file mode 100644 index 00000000..b44bc6e8 --- /dev/null +++ b/budget_activity/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module helps the system accurately record the account code for budget recording. +It includes an "Activity" linking the two, where the activity serves as a match between what the user understands and the account code. +The user can select from a list of activity that have been created in the system, +and the system will then match the selected activity to the corresponding account, +so the user does not need to understand the account themselves. diff --git a/budget_activity/readme/USAGE.rst b/budget_activity/readme/USAGE.rst new file mode 100644 index 00000000..675de521 --- /dev/null +++ b/budget_activity/readme/USAGE.rst @@ -0,0 +1,12 @@ +To used this module you have to configued Budget Activity first. + +#. Go to Budgeting > Activity > Budget Activity +#. Create new activity, select a KPI (if you want to group it) and match the activity to an account. +#. Add a `Keyword` if you need to search for other words to show this activity. For example, the activity name is `Activity1` and the keywords are `Ticket` and `Transportation`. In the Activity field, the user can search for the name `Ticket` to see the activity `Activity1`. +#. Go to Budgeting > Configurations > Budget Template +#. Create new template or use old template (if you have). +#. When you select a KPI, it will automatically select the corresponding activity and account. If you haven't set up an activity in the KPI, you can select the activity and it will automatically select the corresponding account. + + +In the usage window, you will see a new field called "Activity" where the user can select from a list of options. +The system will then match the selected activity to the corresponding account that has been set up. diff --git a/budget_activity/report/__init__.py b/budget_activity/report/__init__.py new file mode 100644 index 00000000..16216c42 --- /dev/null +++ b/budget_activity/report/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import budget_monitor_report diff --git a/budget_activity/report/budget_monitor_report.py b/budget_activity/report/budget_monitor_report.py new file mode 100644 index 00000000..57733d08 --- /dev/null +++ b/budget_activity/report/budget_monitor_report.py @@ -0,0 +1,32 @@ +# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class BudgetMonitorReport(models.Model): + _inherit = "budget.monitor.report" + + activity = fields.Char() + + # Budget + def _select_budget(self): + select_budget_query = super()._select_budget() + # Budget can't find activity + select_budget_query[20] = "null::char as activity" + return select_budget_query + + # All consumed + def _select_statement(self, amount_type): + select_statement = super()._select_statement(amount_type) + select_statement[20] = "ba.name as activity" + return select_statement + + def _from_statement(self, amount_type): + from_statment = super()._from_statement(amount_type) + from_statment = "\n".join( + [ + from_statment, + "left outer join budget_activity ba on a.activity_id = ba.id ", + ] + ) + return from_statment diff --git a/budget_activity/report/budget_monitor_report_view.xml b/budget_activity/report/budget_monitor_report_view.xml new file mode 100644 index 00000000..139c87ff --- /dev/null +++ b/budget_activity/report/budget_monitor_report_view.xml @@ -0,0 +1,33 @@ + + + + budget.monitor.report.tree + budget.monitor.report + + + + + + + + + budget.monitor.report.search + budget.monitor.report + + + + + + + + diff --git a/budget_activity/security/budget_activity_security.xml b/budget_activity/security/budget_activity_security.xml new file mode 100644 index 00000000..3d873324 --- /dev/null +++ b/budget_activity/security/budget_activity_security.xml @@ -0,0 +1,11 @@ + + + + Budget Activity multi company rule + + + ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + diff --git a/budget_activity/security/ir.model.access.csv b/budget_activity/security/ir.model.access.csv new file mode 100644 index 00000000..5f5dde59 --- /dev/null +++ b/budget_activity/security/ir.model.access.csv @@ -0,0 +1,5 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_budget_activity_manager,access_budget_activity_manager,model_budget_activity,budget_control.group_budget_control_manager,1,1,1,1 +access_budget_activity_user,access_budget_activity_user,model_budget_activity,budget_control.group_budget_control_user,1,0,0,0 +access_budget_activity_keyword_manager,access_budget_activity_keyword_manager,model_budget_activity_keyword,budget_control.group_budget_control_manager,1,1,1,1 +access_budget_activity_keyword_user,access_budget_activity_keyword_user,model_budget_activity_keyword,budget_control.group_budget_control_user,1,0,0,0 diff --git a/budget_activity/static/description/icon.png b/budget_activity/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/budget_activity/static/description/index.html b/budget_activity/static/description/index.html new file mode 100644 index 00000000..53b43307 --- /dev/null +++ b/budget_activity/static/description/index.html @@ -0,0 +1,454 @@ + + + + + + +Budget Activity + + + +
+

Budget Activity

+ + +

Alpha License: AGPL-3 OCA/account-budgeting Translate me on Weblate Try me on Runbot

+

This module helps the system accurately record the account code for budget recording. +It includes an “Activity” linking the two, where the activity serves as a match between what the user understands and the account code. +The user can select from a list of activity that have been created in the system, +and the system will then match the selected activity to the corresponding account, +so the user does not need to understand the account themselves.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Usage

+

To used this module you have to configued Budget Activity first.

+
    +
  1. Go to Budgeting > Activity > Budget Activity
  2. +
  3. Create new activity, select a KPI (if you want to group it) and match the activity to an account.
  4. +
  5. Add a Keyword if you need to search for other words to show this activity. For example, the activity name is Activity1 and the keywords are Ticket and Transportation. In the Activity field, the user can search for the name Ticket to see the activity Activity1.
  6. +
  7. Go to Budgeting > Configurations > Budget Template
  8. +
  9. Create new template or use old template (if you have)
  10. +
  11. When you select a KPI, it will automatically select the corresponding activity and account. If you haven’t set up an activity in the KPI, you can select the activity and it will automatically select the corresponding account
  12. +
+

In the usage window, you will see a new field called “Activity” where the user can select from a list of options. +The system will then match the selected activity to the corresponding account that has been set up.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Ecosoft
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

kittiu

+

This module is part of the OCA/account-budgeting project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/budget_activity/tests/__init__.py b/budget_activity/tests/__init__.py new file mode 100644 index 00000000..dd391c72 --- /dev/null +++ b/budget_activity/tests/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import test_budget_activity diff --git a/budget_activity/tests/test_budget_activity.py b/budget_activity/tests/test_budget_activity.py new file mode 100644 index 00000000..107e66c3 --- /dev/null +++ b/budget_activity/tests/test_budget_activity.py @@ -0,0 +1,186 @@ +# Copyright 2020 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime + +from freezegun import freeze_time + +from odoo.exceptions import UserError +from odoo.tests import tagged +from odoo.tests.common import Form + +from odoo.addons.budget_control.tests.common import BudgetControlCommon + + +@tagged("post_install", "-at_install") +class TestBudgetActivity(BudgetControlCommon): + @classmethod + @freeze_time("2001-02-01") + def setUpClass(cls): + super().setUpClass() + BudgetActivity = cls.env["budget.activity"] # Create sample activity + cls.activity1 = BudgetActivity.create( + { + "name": "Activity 1", + "kpi_id": cls.kpi1.id, + "account_id": cls.account_kpi1.id, + } + ) + cls.activity2 = BudgetActivity.create( + { + "name": "Activity 2", + "kpi_id": cls.kpi2.id, + "account_id": cls.account_kpi2.id, + } + ) + cls.activity3 = BudgetActivity.create( + { + "name": "Activity 3", + "kpi_id": cls.kpi3.id, + "account_id": cls.account_kpi3.id, + } + ) + # Add activity on template line + with Form(cls.template_line1) as line: + line.kpi_id = cls.kpi1 + with Form(cls.template_line2) as line: + line.kpi_id = cls.kpi2 + with Form(cls.template_line3) as line: + line.kpi_id = cls.kpi3 + # Create sample ready to use Budget Control + cls.budget_control = cls.BudgetControl.create( + { + "name": "CostCenter1/%s" % cls.year, + "template_id": cls.budget_period.template_id.id, + "budget_period_id": cls.budget_period.id, + "analytic_account_id": cls.costcenter1.id, + "plan_date_range_type_id": cls.date_range_type.id, + "allocated_amount": 2400.0, + "template_line_ids": [ + cls.template_line1.id, + cls.template_line2.id, + cls.template_line3.id, + ], + } + ) + # Test item created for 3 kpi x 4 quarters = 12 budget items + cls.budget_control.prepare_budget_control_matrix() + assert len(cls.budget_control.line_ids) == 12 + # Assign budget.control amount: KPI1 = 100x4=400, KPI2=800, KPI3=1,200 + cls.budget_control.line_ids.filtered(lambda x: x.kpi_id == cls.kpi1).write( + {"amount": 100} + ) + cls.budget_control.line_ids.filtered(lambda x: x.kpi_id == cls.kpi2).write( + {"amount": 200} + ) + cls.budget_control.line_ids.filtered(lambda x: x.kpi_id == cls.kpi3).write( + {"amount": 300} + ) + + def _create_simple_bill_activity(self, analytic, activity, account, amount): + Invoice = self.env["account.move"] + view_id = "account.view_move_form" + with Form( + Invoice.with_context(default_move_type="in_invoice"), view=view_id + ) as inv: + inv.partner_id = self.vendor + inv.invoice_date = datetime.today() + with inv.invoice_line_ids.new() as line: + line.quantity = 1 + line.account_id = account + line.price_unit = amount + line.analytic_account_id = analytic + line.activity_id = activity # required when select analytic account + invoice = inv.save() + return invoice + + @freeze_time("2001-02-01") + def test_01_budget_activity_account(self): + """ + On vendor bill, + - If no activity, budget follows product's account + - If activity is selected, account follows activity's regardless of product + - User can always change account code afterwards + - Posting invoice, will create budget move with activity + """ + # Control budget + self.budget_period.control_budget = True + self.budget_control.action_done() + price_unit = 10.0 + invoice = self._create_simple_bill_activity( + self.costcenter1, self.activity1, self.account_kpi1, price_unit + ) + self.assertEqual( + self.activity1.account_id, invoice.invoice_line_ids[0].account_id + ) + # Change to product2, account should not change. + with Form(invoice) as invoice_form: + with invoice_form.invoice_line_ids.edit(0) as line_form: + line_form.product_id = self.product2 + line_form.price_unit = price_unit # Change product, amount will reset + invoice_form.save() + self.assertEqual( + self.activity1.account_id, invoice.invoice_line_ids[0].account_id + ) + + # Invoice line is not set up as following, + self.assertEqual( + self.activity1.account_id, invoice.invoice_line_ids[0].account_id + ) + self.assertEqual(self.product2, invoice.invoice_line_ids[0].product_id) + self.assertEqual(self.activity1, invoice.invoice_line_ids[0].activity_id) + # Change activity on template line for test no activity in template line + with Form(self.template_line1) as line: + line.kpi_id = self.kpi2 + with self.assertRaises(UserError): + invoice.action_post() + # Change activity on template line for test multi activity in template line + with Form(self.template_line1) as line: + line.kpi_id = self.kpi1 + with Form(self.template_line2) as line: + line.kpi_id = self.kpi1 + with self.assertRaises(UserError): + invoice.action_post() + # Change back to basic + with Form(self.template_line2) as line: + line.kpi_id = self.kpi2 + # Reset state and set account = account in activity + invoice.invoice_line_ids[0].account_id = self.activity1.account_id + # All values will be passed to budget move + invoice.action_post() + self.assertEqual(self.account_kpi1, invoice.budget_move_ids[0].account_id) + self.assertEqual(self.product2, invoice.budget_move_ids[0].product_id) + self.assertEqual(self.activity1, invoice.budget_move_ids[0].activity_id) + # Check budget move must account equal accuont in activity + with self.assertRaises(UserError): + invoice.budget_move_ids[0].account_id = self.account_kpi3.id + + @freeze_time("2001-02-01") + def test_02_budget_adjustment_activity(self): + """ + On budget adjustment, + - If no activity, budget follows product's account + - If activity is selected, account follows activity's regardless of product + - User can always change account code afterwards + """ + self.assertEqual(self.budget_control.amount_balance, 2400.0) + budget_adjust = self.env["budget.move.adjustment"].create( + { + "date_commit": "2001-02-01", + } + ) + with Form(budget_adjust.adjust_item_ids) as line: + line.adjust_id = budget_adjust + line.adjust_type = "consume" + line.product_id = self.product1 + line.analytic_account_id = self.costcenter1 + line.amount = 100.0 + adjust_line = line.save() + self.assertEqual(adjust_line.account_id, self.account_kpi1) + # Change to activity2, account should change to account_kpi2 + with Form(adjust_line) as line: + line.activity_id = self.activity2 + self.assertEqual(adjust_line.account_id, self.activity2.account_id) + # balance in budget control must be 'Decrease' + budget_adjust.action_adjust() + self.assertEqual(self.budget_control.amount_balance, 2300.0) diff --git a/budget_activity/views/account_move_views.xml b/budget_activity/views/account_move_views.xml new file mode 100644 index 00000000..603acd95 --- /dev/null +++ b/budget_activity/views/account_move_views.xml @@ -0,0 +1,28 @@ + + + + account.move.form + account.move + + + + + + + + + + + diff --git a/budget_activity/views/budget_activity_view.xml b/budget_activity/views/budget_activity_view.xml new file mode 100644 index 00000000..454828b1 --- /dev/null +++ b/budget_activity/views/budget_activity_view.xml @@ -0,0 +1,162 @@ + + + + + budget.activity.keyword.view.tree + budget.activity.keyword + + + + + + + + + + budget.activity.keyword.view.search + budget.activity.keyword + + + + + + + + + + + + + budget.activity.view.form + budget.activity.keyword + +
+ + + + + + + + + + + + +
+
+
+ + Budget Activity Keywords + + budget.activity.keyword + tree,form + + + + budget.activity.view.tree + budget.activity + + + + + + + + + + + view.budget.activity.filter + budget.activity + + + + + + + + + + + + + + + + budget.activity.view.form + budget.activity + +
+ + +
+
+ + + + + + + + + + +
+
+
+
+ + Budget Activity + + budget.activity + tree,form + +
diff --git a/budget_activity/views/budget_kpi_view.xml b/budget_activity/views/budget_kpi_view.xml new file mode 100644 index 00000000..55dd4de6 --- /dev/null +++ b/budget_activity/views/budget_kpi_view.xml @@ -0,0 +1,17 @@ + + + + budget.kpi.view.form + budget.kpi + + + + + + + + + + + + diff --git a/budget_activity/views/budget_menuitem.xml b/budget_activity/views/budget_menuitem.xml new file mode 100644 index 00000000..14c68820 --- /dev/null +++ b/budget_activity/views/budget_menuitem.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/budget_activity/views/budget_move_adjustment_view.xml b/budget_activity/views/budget_move_adjustment_view.xml new file mode 100644 index 00000000..0301b7b2 --- /dev/null +++ b/budget_activity/views/budget_move_adjustment_view.xml @@ -0,0 +1,25 @@ + + + + view.budget.move.adjustment.form + budget.move.adjustment + + + + + + + + + + + diff --git a/budget_activity/views/budget_template_view.xml b/budget_activity/views/budget_template_view.xml new file mode 100644 index 00000000..ee2b329b --- /dev/null +++ b/budget_activity/views/budget_template_view.xml @@ -0,0 +1,44 @@ + + + + budget.template.view.form + budget.template + + + + + + + 1 + 1 + + +
  • Activity: Replaces binding with an account, where 1 activity = 1 account and each activity is associated with a KPI.
  • +
    + + Hierarchy: +
    +                            KPI 1                   KPI 2
    +                            /  \                   /   \
    +                          /      \               /       \
    +                        /          \           /           \
    +                    Activity1   Activity2   Activity3   Activity4
    +                        |            \      /               |
    +                        |              \  /                 |
    +                    Account1          Account2          Account3
    +                
    + So, Activity must not be repeated in order for users to be able to select them, + and the system will correctly check the budget. +
    + +
    +
    +
    From 1ab49ee9ef91e596256b563a385757dbd61c53ec Mon Sep 17 00:00:00 2001 From: Saran440 Date: Fri, 20 Jan 2023 11:30:19 +0700 Subject: [PATCH 04/13] [FIX] activity demo --- budget_activity/__manifest__.py | 1 + budget_activity/demo/budget_activity_demo.xml | 48 +++++++++++++++++++ budget_activity/models/budget_activity.py | 2 +- .../views/budget_activity_view.xml | 4 +- budget_activity/views/budget_menuitem.xml | 2 +- 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 budget_activity/demo/budget_activity_demo.xml diff --git a/budget_activity/__manifest__.py b/budget_activity/__manifest__.py index 2c832086..93b139ea 100644 --- a/budget_activity/__manifest__.py +++ b/budget_activity/__manifest__.py @@ -20,6 +20,7 @@ "views/budget_menuitem.xml", "views/budget_move_adjustment_view.xml", ], + "demo": ["demo/budget_activity_demo.xml"], "installable": True, "maintainers": ["kittiu"], "development_status": "Alpha", diff --git a/budget_activity/demo/budget_activity_demo.xml b/budget_activity/demo/budget_activity_demo.xml new file mode 100644 index 00000000..b51724d3 --- /dev/null +++ b/budget_activity/demo/budget_activity_demo.xml @@ -0,0 +1,48 @@ + + + + + Expense + + + + + Purchase of Equipments + + + + + Rent + + + + + + + + + + + + + + + diff --git a/budget_activity/models/budget_activity.py b/budget_activity/models/budget_activity.py index 68d68372..16873b82 100644 --- a/budget_activity/models/budget_activity.py +++ b/budget_activity/models/budget_activity.py @@ -35,7 +35,7 @@ class BudgetActivity(models.Model): relation="budget_activity_keyword_rel", column1="budget_activity_id", column2="budget_activity_keyword_id", - string="Keyword", + string="Keywords", help="Optional keyword you may want to assign for search", ) diff --git a/budget_activity/views/budget_activity_view.xml b/budget_activity/views/budget_activity_view.xml index 454828b1..7f2b2ad3 100644 --- a/budget_activity/views/budget_activity_view.xml +++ b/budget_activity/views/budget_activity_view.xml @@ -17,7 +17,7 @@ budget.activity.keyword - + budget.activity.view.form budget.activity.keyword -
    + From 57edd272d35ee7d9ab1043b7778e5b25436ddbec Mon Sep 17 00:00:00 2001 From: Saran440 Date: Wed, 15 Feb 2023 16:12:33 +0700 Subject: [PATCH 05/13] [FIX] where domain with fund_id in allocation --- budget_activity/models/budget_period.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/budget_activity/models/budget_period.py b/budget_activity/models/budget_period.py index a1f11ec0..6a00ef1e 100644 --- a/budget_activity/models/budget_period.py +++ b/budget_activity/models/budget_period.py @@ -1,5 +1,6 @@ # Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + from odoo import _, api, models from odoo.exceptions import UserError @@ -17,8 +18,7 @@ def _get_kpi_by_control_key(self, template_lines, control): activity = self.env["budget.activity"].browse(activity_id) if not template_line: raise UserError( - _("Chosen activity %s is not valid for budgeting") - % activity.display_name + _("Chosen activity %s is not valid in template") % activity.display_name ) raise UserError( _( From 2ecdd9040305194522449e81fa55bb366c613228 Mon Sep 17 00:00:00 2001 From: Saran440 Date: Mon, 27 Mar 2023 14:48:31 +0700 Subject: [PATCH 06/13] [FIX] required activity in adjust budget and constraint amount > 0 --- budget_activity/models/budget_move_adjustment.py | 8 ++++++++ budget_activity/views/budget_move_adjustment_view.xml | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/budget_activity/models/budget_move_adjustment.py b/budget_activity/models/budget_move_adjustment.py index 75e9d7e4..3101acf2 100644 --- a/budget_activity/models/budget_move_adjustment.py +++ b/budget_activity/models/budget_move_adjustment.py @@ -13,6 +13,14 @@ class BudgetMoveAdjustmentItem(models.Model): index=True, ) + _sql_constraints = [ + ( + "amount_positive", + "CHECK(amount >= 0)", + "The adjusted budget must be positive.", + ) + ] + @api.onchange("activity_id") def _onchange_activity_id(self): if self.activity_id: diff --git a/budget_activity/views/budget_move_adjustment_view.xml b/budget_activity/views/budget_move_adjustment_view.xml index 0301b7b2..4a50f9fb 100644 --- a/budget_activity/views/budget_move_adjustment_view.xml +++ b/budget_activity/views/budget_move_adjustment_view.xml @@ -12,7 +12,11 @@ expr="//page[@name='adjust_item_ids']//tree/field[@name='account_id']" position="before" > - + Date: Fri, 31 Mar 2023 11:50:48 +0700 Subject: [PATCH 07/13] [IMP] do not allow edit account in activity after used it in commit --- budget_activity/models/budget_activity.py | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/budget_activity/models/budget_activity.py b/budget_activity/models/budget_activity.py index 16873b82..fcc4c297 100644 --- a/budget_activity/models/budget_activity.py +++ b/budget_activity/models/budget_activity.py @@ -1,7 +1,8 @@ # Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import UserError class BudgetActivity(models.Model): @@ -58,6 +59,27 @@ def name_search(self, name, args=None, operator="ilike", limit=100): activitys = self.search(domain + args, limit=limit) return activitys.name_get() + @api.onchange("account_id") + def onchange_account_id(self): + """Not allow edit account after use it in commit budgeting""" + BudgetPeriod = self.env["budget.period"] + MonitorReport = self.env["budget.monitor.report"] + query = BudgetPeriod._budget_info_query() + + domain = [("activity", "=", self._origin.name)] + dataset_all = MonitorReport.read_group( + domain=domain, + fields=query["fields"], + groupby=query["groupby"], + lazy=False, + ) + if dataset_all: + raise UserError( + _( + "You cannot change the account because it is already used in a commit." + ) + ) + class BudgetActivityKeyword(models.Model): _name = "budget.activity.keyword" From 732a14873272ff42891709a130e888511cc89fc0 Mon Sep 17 00:00:00 2001 From: Saran440 Date: Wed, 17 May 2023 13:55:29 +0700 Subject: [PATCH 08/13] [FIX] budget_activity: required account activity --- budget_activity/models/budget_activity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/budget_activity/models/budget_activity.py b/budget_activity/models/budget_activity.py index fcc4c297..77f294f0 100644 --- a/budget_activity/models/budget_activity.py +++ b/budget_activity/models/budget_activity.py @@ -23,7 +23,7 @@ class BudgetActivity(models.Model): domain=[("deprecated", "=", False)], readonly=False, index=True, - required=False, + required=True, ) company_id = fields.Many2one( comodel_name="res.company", @@ -73,7 +73,7 @@ def onchange_account_id(self): groupby=query["groupby"], lazy=False, ) - if dataset_all: + if dataset_all and self.account_id: raise UserError( _( "You cannot change the account because it is already used in a commit." From c633956de67a10ff24d6f98d87fad40bb6e7d842 Mon Sep 17 00:00:00 2001 From: ps-tubtim Date: Mon, 22 May 2023 09:12:22 +0700 Subject: [PATCH 09/13] [FIX] budget_activity: activity not require account --- budget_activity/models/budget_activity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/budget_activity/models/budget_activity.py b/budget_activity/models/budget_activity.py index 77f294f0..4850d48a 100644 --- a/budget_activity/models/budget_activity.py +++ b/budget_activity/models/budget_activity.py @@ -23,7 +23,6 @@ class BudgetActivity(models.Model): domain=[("deprecated", "=", False)], readonly=False, index=True, - required=True, ) company_id = fields.Many2one( comodel_name="res.company", From 2e73b2500dedaaa34f2b8c5486a9eb89489d3785 Mon Sep 17 00:00:00 2001 From: Saran440 Date: Fri, 11 Aug 2023 17:45:44 +0700 Subject: [PATCH 10/13] [FIX] activity_id not required when move is not affect budget --- budget_activity/views/account_move_views.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/budget_activity/views/account_move_views.xml b/budget_activity/views/account_move_views.xml index 603acd95..d244d025 100644 --- a/budget_activity/views/account_move_views.xml +++ b/budget_activity/views/account_move_views.xml @@ -11,16 +11,16 @@ > From b7f19c716a702322f3da89e2a32bc0137a51af8b Mon Sep 17 00:00:00 2001 From: Saran440 Date: Tue, 17 Oct 2023 14:04:02 +0700 Subject: [PATCH 11/13] [UPD] pre-commit --- budget_activity/README.rst | 47 ++++++--------- budget_activity/__manifest__.py | 2 +- budget_activity/static/description/index.html | 59 +++++++++---------- 3 files changed, 46 insertions(+), 62 deletions(-) diff --git a/budget_activity/README.rst b/budget_activity/README.rst index 732c2da4..9ec835e1 100644 --- a/budget_activity/README.rst +++ b/budget_activity/README.rst @@ -2,10 +2,13 @@ Budget Activity =============== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a5a8909d4b508145ac8b4f7811bc684d06e2e7e126e227c8e349ee255d4c9c7b + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status @@ -13,17 +16,11 @@ Budget Activity .. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 -.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--budgeting-lightgray.png?logo=github - :target: https://github.com/OCA/account-budgeting/tree/15.0/budget_activity - :alt: OCA/account-budgeting -.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/account-budgeting-15-0/account-budgeting-15-0-budget_activity - :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/88/15.0 - :alt: Try me on Runbot - -|badge1| |badge2| |badge3| |badge4| |badge5| +.. |badge3| image:: https://img.shields.io/badge/github-ecosoft--odoo%2Fbudgeting-lightgray.png?logo=github + :target: https://github.com/ecosoft-odoo/budgeting/tree/15.0/budget_activity + :alt: ecosoft-odoo/budgeting + +|badge1| |badge2| |badge3| This module helps the system accurately record the account code for budget recording. It includes an "Activity" linking the two, where the activity serves as a match between what the user understands and the account code. @@ -50,8 +47,8 @@ To used this module you have to configued Budget Activity first. #. Create new activity, select a KPI (if you want to group it) and match the activity to an account. #. Add a `Keyword` if you need to search for other words to show this activity. For example, the activity name is `Activity1` and the keywords are `Ticket` and `Transportation`. In the Activity field, the user can search for the name `Ticket` to see the activity `Activity1`. #. Go to Budgeting > Configurations > Budget Template -#. Create new template or use old template (if you have) -#. When you select a KPI, it will automatically select the corresponding activity and account. If you haven't set up an activity in the KPI, you can select the activity and it will automatically select the corresponding account +#. Create new template or use old template (if you have). +#. When you select a KPI, it will automatically select the corresponding activity and account. If you haven't set up an activity in the KPI, you can select the activity and it will automatically select the corresponding account. In the usage window, you will see a new field called "Activity" where the user can select from a list of options. @@ -60,10 +57,10 @@ The system will then match the selected activity to the corresponding account th Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -87,24 +84,14 @@ Contributors Maintainers ~~~~~~~~~~~ -This module is maintained by the OCA. - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -OCA, or the Odoo Community Association, is a nonprofit organization whose -mission is to support the collaborative development of Odoo features and -promote its widespread use. - .. |maintainer-kittiu| image:: https://github.com/kittiu.png?size=40px :target: https://github.com/kittiu :alt: kittiu -Current `maintainer `__: +Current maintainer: |maintainer-kittiu| -This module is part of the `OCA/account-budgeting `_ project on GitHub. +This module is part of the `ecosoft-odoo/budgeting `_ project on GitHub. -You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. +You are welcome to contribute. diff --git a/budget_activity/__manifest__.py b/budget_activity/__manifest__.py index 93b139ea..bed48afd 100644 --- a/budget_activity/__manifest__.py +++ b/budget_activity/__manifest__.py @@ -7,7 +7,7 @@ "category": "Accounting", "license": "AGPL-3", "author": "Ecosoft, Odoo Community Association (OCA)", - "website": "https://github.com/OCA/account-analytic", + "website": "https://github.com/ecosoft-odoo/budgeting", "depends": ["budget_control"], "data": [ "security/budget_activity_security.xml", diff --git a/budget_activity/static/description/index.html b/budget_activity/static/description/index.html index 53b43307..c00e78f8 100644 --- a/budget_activity/static/description/index.html +++ b/budget_activity/static/description/index.html @@ -1,20 +1,20 @@ - + - + Budget Activity