diff --git a/account_asset_management/__init__.py b/account_asset_management/__init__.py index 9b429614..7660e7bf 100644 --- a/account_asset_management/__init__.py +++ b/account_asset_management/__init__.py @@ -1,2 +1,3 @@ from . import models +from . import report from . import wizard diff --git a/account_asset_management/__manifest__.py b/account_asset_management/__manifest__.py index 6fd97918..f6777545 100644 --- a/account_asset_management/__manifest__.py +++ b/account_asset_management/__manifest__.py @@ -1,4 +1,4 @@ -# Copyright 2009-2018 Noviat +# Copyright 2009-2019 Noviat # Copyright 2019 Tecnativa - Pedro M. Baeza # Copyright 2021 Tecnativa - João Marques # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -7,7 +7,7 @@ "name": "Assets Management", "version": "14.0.1.0.4", "license": "AGPL-3", - "depends": ["account"], + "depends": ["account", "report_xlsx_helper"], "excludes": ["account_asset"], "external_dependencies": {"python": ["python-dateutil"]}, "author": "Noviat, Odoo Community Association (OCA)", @@ -26,5 +26,6 @@ "views/account_move_line.xml", "views/menuitem.xml", "data/cron.xml", + "wizard/wiz_account_asset_report.xml", ], } diff --git a/account_asset_management/models/account_asset.py b/account_asset_management/models/account_asset.py index 95ebfa09..ef283512 100644 --- a/account_asset_management/models/account_asset.py +++ b/account_asset_management/models/account_asset.py @@ -1202,3 +1202,85 @@ class AccountAsset(models.Model): triggers.sudo().write(recompute_vals) return (result, error_log) + + @api.model + def _xls_acquisition_fields(self): + """ + Update list in custom module to add/drop columns or change order + """ + return [ + "account", + "name", + "code", + "date_start", + "depreciation_base", + "salvage_value", + ] + + @api.model + def _xls_active_fields(self): + """ + Update list in custom module to add/drop columns or change order + """ + return [ + "account", + "name", + "code", + "date_start", + "depreciation_base", + "salvage_value", + "period_start_value", + "period_depr", + "period_end_value", + "period_end_depr", + "method", + "method_number", + "prorata", + "state", + ] + + @api.model + def _xls_removal_fields(self): + """ + Update list in custom module to add/drop columns or change order + """ + return [ + "account", + "name", + "code", + "date_remove", + "depreciation_base", + "salvage_value", + ] + + @api.model + def _xls_asset_template(self): + """ + Template updates + + """ + return {} + + @api.model + def _xls_acquisition_template(self): + """ + Template updates + + """ + return {} + + @api.model + def _xls_active_template(self): + """ + Template updates + + """ + return {} + + @api.model + def _xls_removal_template(self): + """ + Template updates + + """ + return {} diff --git a/account_asset_management/models/account_asset_group.py b/account_asset_management/models/account_asset_group.py index c7d1598a..77b1d134 100644 --- a/account_asset_management/models/account_asset_group.py +++ b/account_asset_management/models/account_asset_group.py @@ -1,14 +1,15 @@ -# Copyright 2009-2018 Noviat +# Copyright 2009-2020 Noviat # Copyright 2019 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models +from odoo.osv import expression class AccountAssetGroup(models.Model): _name = "account.asset.group" _description = "Asset Group" - _order = "name" + _order = "code, name" _parent_store = True name = fields.Char(string="Name", size=64, required=True, index=True) @@ -25,7 +26,53 @@ class AccountAssetGroup(models.Model): string="Parent Asset Group", ondelete="restrict", ) + child_ids = fields.One2many( + comodel_name="account.asset.group", + inverse_name="parent_id", + string="Child Asset Groups", + ) @api.model def _default_company_id(self): return self.env.company + + def name_get(self): + result = [] + params = self.env.context.get("params") + list_view = params and params.get("view_type") == "list" + short_name_len = 16 + for rec in self: + if rec.code: + full_name = rec.code + " " + rec.name + short_name = rec.code + else: + full_name = rec.name + if len(full_name) > short_name_len: + short_name = full_name[:16] + "..." + else: + short_name = full_name + if list_view: + name = short_name + else: + name = full_name + result.append((rec.id, name)) + return result + + @api.model + def _name_search( + self, name, args=None, operator="ilike", limit=100, name_get_uid=None + ): + args = args or [] + domain = [] + if name: + domain = [ + "|", + ("code", "=ilike", name.split(" ")[0] + "%"), + ("name", operator, name), + ] + if operator in expression.NEGATIVE_TERM_OPERATORS: + domain = ["&", "!"] + domain[1:] + rec_ids = self._search( + expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid + ) + return self.browse(rec_ids).name_get() diff --git a/account_asset_management/readme/DESCRIPTION.rst b/account_asset_management/readme/DESCRIPTION.rst index fc2e688f..1875b4a7 100644 --- a/account_asset_management/readme/DESCRIPTION.rst +++ b/account_asset_management/readme/DESCRIPTION.rst @@ -15,7 +15,5 @@ or automatically by two ways: These options are compatibles each other. -Excel based reporting is available via the 'account_asset_management_xls' module. - The module contains a large number of functional enhancements compared to the standard account_asset module from Odoo. diff --git a/account_asset_management/report/__init__.py b/account_asset_management/report/__init__.py new file mode 100644 index 00000000..2a46ed58 --- /dev/null +++ b/account_asset_management/report/__init__.py @@ -0,0 +1 @@ +from . import account_asset_report_xls diff --git a/account_asset_management/report/account_asset_report_xls.py b/account_asset_management/report/account_asset_report_xls.py new file mode 100644 index 00000000..37ef06cb --- /dev/null +++ b/account_asset_management/report/account_asset_report_xls.py @@ -0,0 +1,684 @@ +# Copyright 2009-2019 Noviat +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import _, models +from odoo.exceptions import UserError +from odoo.tools.translate import translate + +_logger = logging.getLogger(__name__) + + +IR_TRANSLATION_NAME = "account.asset.report" + + +class AssetReportXlsx(models.AbstractModel): + _name = "report.account_asset_management.asset_report_xls" + _description = "Dynamic XLS asset report generator" + _inherit = "report.report_xlsx.abstract" + + def _(self, src): + lang = self.env.context.get("lang", "en_US") + val = translate(self.env.cr, IR_TRANSLATION_NAME, "report", lang, src) or src + return val + + def _get_ws_params(self, wb, data, wiz): + self._grouped_assets = self._get_assets(wiz) + s1 = self._get_acquisition_ws_params(wb, data, wiz) + s2 = self._get_active_ws_params(wb, data, wiz) + s3 = self._get_removal_ws_params(wb, data, wiz) + return [s1, s2, s3] + + def _get_asset_template(self): + + asset_template = { + "account": { + "header": {"type": "string", "value": self._("Account")}, + "asset": { + "type": "string", + "value": self._render("asset.profile_id.account_asset_id.code"), + }, + "totals": {"type": "string", "value": self._("Totals")}, + "width": 20, + }, + "name": { + "header": {"type": "string", "value": self._("Name")}, + "asset_group": { + "type": "string", + "value": self._render("group.name or ''"), + }, + "asset": {"type": "string", "value": self._render("asset.name")}, + "width": 40, + }, + "code": { + "header": {"type": "string", "value": self._("Reference")}, + "asset_group": { + "type": "string", + "value": self._render("group.code or ''"), + }, + "asset": {"type": "string", "value": self._render("asset.code or ''")}, + "width": 20, + }, + "date_start": { + "header": {"type": "string", "value": self._("Asset Start Date")}, + "asset": { + "value": self._render("asset.date_start"), + "format": self.format_tcell_date_left, + }, + "width": 20, + }, + "date_remove": { + "header": {"type": "string", "value": self._("Asset Removal Date")}, + "asset": { + "value": self._render("asset.date_remove"), + "format": self.format_tcell_date_left, + }, + "width": 20, + }, + "depreciation_base": { + "header": { + "type": "string", + "value": self._("Depreciation Base"), + "format": self.format_theader_yellow_right, + }, + "asset_group": { + "type": "number", + "value": self._render("group._depreciation_base"), + "format": self.format_theader_blue_amount_right, + }, + "asset": { + "type": "number", + "value": self._render("asset.depreciation_base"), + "format": self.format_tcell_amount_right, + }, + "totals": { + "type": "formula", + "value": self._render("asset_total_formula"), + "format": self.format_theader_yellow_amount_right, + }, + "width": 18, + }, + "salvage_value": { + "header": { + "type": "string", + "value": self._("Salvage Value"), + "format": self.format_theader_yellow_right, + }, + "asset_group": { + "type": "number", + "value": self._render("group._salvage_value"), + "format": self.format_theader_blue_amount_right, + }, + "asset": { + "type": "number", + "value": self._render("asset.salvage_value"), + "format": self.format_tcell_amount_right, + }, + "totals": { + "type": "formula", + "value": self._render("salvage_total_formula"), + "format": self.format_theader_yellow_amount_right, + }, + "width": 18, + }, + "period_start_value": { + "header": { + "type": "string", + "value": self._("Period Start Value"), + "format": self.format_theader_yellow_right, + }, + "asset_group": { + "type": "number", + "value": self._render("group._period_start_value"), + "format": self.format_theader_blue_amount_right, + }, + "asset": { + "type": "number", + "value": self._render("asset._period_start_value"), + "format": self.format_tcell_amount_right, + }, + "totals": { + "type": "formula", + "value": self._render("period_start_total_formula"), + "format": self.format_theader_yellow_amount_right, + }, + "width": 18, + }, + "period_depr": { + "header": { + "type": "string", + "value": self._("Period Depreciation"), + "format": self.format_theader_yellow_right, + }, + "asset_group": { + "type": "formula", + "value": self._render("period_diff_formula"), + "format": self.format_theader_blue_amount_right, + }, + "asset": { + "type": "formula", + "value": self._render("period_diff_formula"), + "format": self.format_tcell_amount_right, + }, + "totals": { + "type": "formula", + "value": self._render("period_diff_formula"), + "format": self.format_theader_yellow_amount_right, + }, + "width": 18, + }, + "period_end_value": { + "header": { + "type": "string", + "value": self._("Period End Value"), + "format": self.format_theader_yellow_right, + }, + "asset_group": { + "type": "number", + "value": self._render("group._period_end_value"), + "format": self.format_theader_blue_amount_right, + }, + "asset": { + "type": "number", + "value": self._render("asset._period_end_value"), + "format": self.format_tcell_amount_right, + }, + "totals": { + "type": "formula", + "value": self._render("period_end_total_formula"), + "format": self.format_theader_yellow_amount_right, + }, + "width": 18, + }, + "period_end_depr": { + "header": { + "type": "string", + "value": self._("Tot. Depreciation"), + "format": self.format_theader_yellow_right, + }, + "asset_group": { + "type": "formula", + "value": self._render("total_depr_formula"), + "format": self.format_theader_blue_amount_right, + }, + "asset": { + "type": "formula", + "value": self._render("total_depr_formula"), + "format": self.format_tcell_amount_right, + }, + "totals": { + "type": "formula", + "value": self._render("total_depr_formula"), + "format": self.format_theader_yellow_amount_right, + }, + "width": 18, + }, + "method": { + "header": { + "type": "string", + "value": self._("Comput. Method"), + "format": self.format_theader_yellow_center, + }, + "asset": { + "type": "string", + "value": self._render("asset.method or ''"), + "format": self.format_tcell_center, + }, + "width": 20, + }, + "method_number": { + "header": { + "type": "string", + "value": self._("Number of Years"), + "format": self.format_theader_yellow_center, + }, + "asset": { + "type": "number", + "value": self._render("asset.method_number"), + "format": self.format_tcell_integer_center, + }, + "width": 20, + }, + "prorata": { + "header": { + "type": "string", + "value": self._("Prorata Temporis"), + "format": self.format_theader_yellow_center, + }, + "asset": { + "type": "boolean", + "value": self._render("asset.prorata"), + "format": self.format_tcell_center, + }, + "width": 20, + }, + "state": { + "header": { + "type": "string", + "value": self._("Status"), + "format": self.format_theader_yellow_center, + }, + "asset": { + "type": "string", + "value": self._render("asset.state"), + "format": self.format_tcell_center, + }, + "width": 8, + }, + } + asset_template.update(self.env["account.asset"]._xls_asset_template()) + + return asset_template + + def _get_acquisition_ws_params(self, wb, data, wiz): + + acquisition_template = self._get_asset_template() + acquisition_template.update( + self.env["account.asset"]._xls_acquisition_template() + ) + wl_acq = self.env["account.asset"]._xls_acquisition_fields() + title = self._get_title(wiz, "acquisition", format="normal") + title_short = self._get_title(wiz, "acquisition", format="short") + sheet_name = title_short[:31].replace("/", "-") + + return { + "ws_name": sheet_name, + "generate_ws_method": "_asset_report", + "title": title, + "wanted_list": wl_acq, + "col_specs": acquisition_template, + "report_type": "acquisition", + } + + def _get_active_ws_params(self, wb, data, wiz): + + active_template = self._get_asset_template() + active_template.update(self.env["account.asset"]._xls_active_template()) + wl_act = self.env["account.asset"]._xls_active_fields() + title = self._get_title(wiz, "active", format="normal") + title_short = self._get_title(wiz, "active", format="short") + sheet_name = title_short[:31].replace("/", "-") + + return { + "ws_name": sheet_name, + "generate_ws_method": "_asset_report", + "title": title, + "wanted_list": wl_act, + "col_specs": active_template, + "report_type": "active", + } + + def _get_removal_ws_params(self, wb, data, wiz): + + removal_template = self._get_asset_template() + removal_template.update(self.env["account.asset"]._xls_removal_template()) + wl_dsp = self.env["account.asset"]._xls_removal_fields() + title = self._get_title(wiz, "removal", format="normal") + title_short = self._get_title(wiz, "removal", format="short") + sheet_name = title_short[:31].replace("/", "-") + + return { + "ws_name": sheet_name, + "generate_ws_method": "_asset_report", + "title": title, + "wanted_list": wl_dsp, + "col_specs": removal_template, + "report_type": "removal", + } + + def _get_title(self, wiz, report, format="normal"): + + prefix = "{} - {}".format(wiz.date_from, wiz.date_to) + if report == "acquisition": + if format == "normal": + title = prefix + " : " + _("New Acquisitions") + else: + title = "ACQ" + elif report == "active": + if format == "normal": + title = prefix + " : " + _("Active Assets") + else: + title = "ACT" + else: + if format == "normal": + title = prefix + " : " + _("Removed Assets") + else: + title = "DSP" + return title + + def _report_title(self, ws, row_pos, ws_params, data, wiz): + return self._write_ws_title(ws, row_pos, ws_params) + + def _empty_report(self, ws, row_pos, ws_params, data, wiz): + report = ws_params["report_type"] + if report == "acquisition": + suffix = _("New Acquisitions") + elif report == "active": + suffix = _("Active Assets") + else: + suffix = _("Removed Assets") + no_entries = _("No") + " " + suffix + ws.write_string(row_pos, 0, no_entries, self.format_left_bold) + + def _get_assets(self, wiz): + + dom = [ + ("date_start", "<=", wiz.date_to), + "|", + ("date_remove", "=", False), + ("date_remove", ">=", wiz.date_from), + ] + + parent_group = wiz.asset_group_id + if parent_group: + + def _child_get(parent): + groups = [parent] + children = parent.child_ids + children = children.sorted(lambda r: r.code or r.name) + for child in children: + if child in groups: + raise UserError( + _( + "Inconsistent reporting structure." + "\nPlease correct Asset Group '%s' (id %s)" + ) + % (child.name, child.id) + ) + groups.extend(_child_get(child)) + return groups + + groups = _child_get(parent_group) + dom.append(("group_ids", "in", [x.id for x in groups])) + + if not wiz.draft: + dom.append(("state", "!=", "draft")) + self._assets = self.env["account.asset"].search(dom) + grouped_assets = {} + self._group_assets(self._assets, parent_group, grouped_assets) + return grouped_assets + + @staticmethod + def acquisition_filter(wiz, asset): + return asset.date_start >= wiz.date_from + + @staticmethod + def active_filter(wiz, asset): + return True + + @staticmethod + def removal_filter(wiz, asset): + return ( + asset.date_remove + and asset.date_remove >= wiz.date_from + and asset.date_remove <= wiz.date_to + ) + + def _group_assets(self, assets, group, grouped_assets): + if group: + group_assets = assets.filtered(lambda r: group in r.group_ids) + else: + group_assets = assets + group_assets = group_assets.sorted(lambda r: (r.date_start or "", r.code)) + grouped_assets[group] = {"assets": group_assets} + for child in group.child_ids: + self._group_assets(assets, child, grouped_assets[group]) + + def _create_report_entries( + self, ws_params, wiz, entries, group, group_val, error_dict + ): + report = ws_params["report_type"] + + def asset_filter(asset): + filter = getattr(self, "{}_filter".format(report)) + return filter(wiz, asset) + + def _has_assets(group, group_val): + assets = group_val.get("assets") + assets = assets.filtered(asset_filter) + if assets: + return True + for child in group.child_ids: + if _has_assets(child, group_val[child]): + return True + return False + + assets = group_val.get("assets") + assets = assets.filtered(asset_filter) + + # remove empty entries + if not _has_assets(group, group_val): + return + + asset_entries = [] + group._depreciation_base = 0.0 + group._salvage_value = 0.0 + group._period_start_value = 0.0 + group._period_end_value = 0.0 + for asset in assets: + group._depreciation_base += asset.depreciation_base + group._salvage_value += asset.salvage_value + dls_all = asset.depreciation_line_ids.filtered( + lambda r: r.type == "depreciate" + ) + dls_all = dls_all.sorted(key=lambda r: r.line_date) + if not dls_all: + error_dict["no_table"] += asset + # period_start_value + dls = dls_all.filtered(lambda r: r.line_date <= wiz.date_from) + if dls: + value_depreciated = dls[-1].depreciated_value + dls[-1].amount + else: + value_depreciated = 0.0 + asset._period_start_value = asset.depreciation_base - value_depreciated + group._period_start_value += asset._period_start_value + # period_end_value + dls = dls_all.filtered(lambda r: r.line_date <= wiz.date_to) + if dls: + value_depreciated = dls[-1].depreciated_value + dls[-1].amount + else: + value_depreciated = 0.0 + asset._period_end_value = asset.depreciation_base - value_depreciated + group._period_end_value += asset._period_end_value + + asset_entries.append({"asset": asset}) + + todos = [] + for g in group.child_ids: + if _has_assets(g, group_val[g]): + todos.append(g) + + entries.append({"group": group}) + entries.extend(asset_entries) + for todo in todos: + self._create_report_entries( + ws_params, wiz, entries, todo, group_val[todo], error_dict + ) + + def _asset_report(self, workbook, ws, ws_params, data, wiz): + report = ws_params["report_type"] + + ws.set_portrait() + ws.fit_to_pages(1, 0) + ws.set_header(self.xls_headers["standard"]) + ws.set_footer(self.xls_footers["standard"]) + + wl = ws_params["wanted_list"] + if "account" not in wl: + raise UserError( + _( + "The 'account' field is a mandatory entry of the " + "'_xls_%s_fields' list !" + ) + % report + ) + + self._set_column_width(ws, ws_params) + + row_pos = 0 + row_pos = self._report_title(ws, row_pos, ws_params, data, wiz) + + def asset_filter(asset): + filter = getattr(self, "{}_filter".format(report)) + return filter(wiz, asset) + + assets = self._assets.filtered(asset_filter) + + if not assets: + return self._empty_report(ws, row_pos, ws_params, data, wiz) + + row_pos = self._write_line( + ws, + row_pos, + ws_params, + col_specs_section="header", + default_format=self.format_theader_yellow_left, + ) + + ws.freeze_panes(row_pos, 0) + + row_pos_start = row_pos + depreciation_base_pos = "depreciation_base" in wl and wl.index( + "depreciation_base" + ) + salvage_value_pos = "salvage_value" in wl and wl.index("salvage_value") + period_start_value_pos = "period_start_value" in wl and wl.index( + "period_start_value" + ) + period_end_value_pos = "period_end_value" in wl and wl.index("period_end_value") + + entries = [] + root = wiz.asset_group_id + root_val = self._grouped_assets[root] + error_dict = { + "no_table": self.env["account.asset"], + "dups": self.env["account.asset"], + } + + self._create_report_entries(ws_params, wiz, entries, root, root_val, error_dict) + + # traverse entries in reverse order to calc totals + for i, entry in enumerate(reversed(entries)): + group = entry.get("group") + if "group" in entry: + parent = group.parent_id + for e in reversed(entries[: -i - 1]): + g = e.get("group") + if g == parent: + g._depreciation_base += group._depreciation_base + g._salvage_value += group._salvage_value + g._period_start_value += group._period_start_value + g._period_end_value += group._period_end_value + continue + + processed = [] + for entry in entries: + + period_start_value_cell = period_start_value_pos and self._rowcol_to_cell( + row_pos, period_start_value_pos + ) + period_end_value_cell = period_end_value_pos and self._rowcol_to_cell( + row_pos, period_end_value_pos + ) + depreciation_base_cell = depreciation_base_pos and self._rowcol_to_cell( + row_pos, depreciation_base_pos + ) + period_diff_formula = period_end_value_cell and ( + period_start_value_cell + "-" + period_end_value_cell + ) + total_depr_formula = period_end_value_cell and ( + depreciation_base_cell + "-" + period_end_value_cell + ) + + if "group" in entry: + row_pos = self._write_line( + ws, + row_pos, + ws_params, + col_specs_section="asset_group", + render_space={ + "group": entry["group"], + "period_diff_formula": period_diff_formula, + "total_depr_formula": total_depr_formula, + }, + default_format=self.format_theader_blue_left, + ) + + else: + asset = entry["asset"] + if asset in processed: + error_dict["dups"] += asset + continue + else: + processed.append(asset) + row_pos = self._write_line( + ws, + row_pos, + ws_params, + col_specs_section="asset", + render_space={ + "asset": asset, + "period_diff_formula": period_diff_formula, + "total_depr_formula": total_depr_formula, + }, + default_format=self.format_tcell_left, + ) + + asset_total_formula = depreciation_base_pos and self._rowcol_to_cell( + row_pos_start, depreciation_base_pos + ) + salvage_total_formula = salvage_value_pos and self._rowcol_to_cell( + row_pos_start, salvage_value_pos + ) + period_start_total_formula = period_start_value_pos and self._rowcol_to_cell( + row_pos_start, period_start_value_pos + ) + period_end_total_formula = period_end_value_pos and self._rowcol_to_cell( + row_pos_start, period_end_value_pos + ) + period_start_value_cell = period_start_value_pos and self._rowcol_to_cell( + row_pos, period_start_value_pos + ) + period_end_value_cell = period_end_value_pos and self._rowcol_to_cell( + row_pos, period_end_value_pos + ) + depreciation_base_cell = depreciation_base_pos and self._rowcol_to_cell( + row_pos, depreciation_base_pos + ) + period_diff_formula = period_end_value_cell and ( + period_start_value_cell + "-" + period_end_value_cell + ) + total_depr_formula = period_end_value_cell and ( + depreciation_base_cell + "-" + period_end_value_cell + ) + + row_pos = self._write_line( + ws, + row_pos, + ws_params, + col_specs_section="totals", + render_space={ + "asset_total_formula": asset_total_formula, + "salvage_total_formula": salvage_total_formula, + "period_start_total_formula": period_start_total_formula, + "period_end_total_formula": period_end_total_formula, + "period_diff_formula": period_diff_formula, + "total_depr_formula": total_depr_formula, + }, + default_format=self.format_theader_yellow_left, + ) + + for k in error_dict: + if error_dict[k]: + if k == "no_table": + reason = _("Missing depreciation table") + elif k == "dups": + reason = _("Duplicate reporting entries") + else: + reason = _("Undetermined error") + row_pos += 1 + err_msg = _("Assets to be corrected") + ": " + err_msg += "%s" % [x[1] for x in error_dict[k].name_get()] + err_msg += " - " + _("Reason") + ": " + reason + ws.write_string(row_pos, 0, err_msg, self.format_left_bold) diff --git a/account_asset_management/security/ir.model.access.csv b/account_asset_management/security/ir.model.access.csv index 2ac1210b..148fa11e 100644 --- a/account_asset_management/security/ir.model.access.csv +++ b/account_asset_management/security/ir.model.access.csv @@ -15,3 +15,4 @@ access_account_asset_group_user,account.asset.group,model_account_asset_group,ac access_account_asset_group_manager,account.asset.group,model_account_asset_group,account.group_account_manager,1,1,1,1 access_account_asset_remove_user,account.asset.remove,model_account_asset_remove,account.group_account_user,1,1,1,1 access_account_asset_compute_user,account.asset.compute,model_account_asset_compute,account.group_account_user,1,1,1,1 +access_wiz_account_asset_report,wiz.account.asset.report,model_wiz_account_asset_report,account.group_account_readonly,1,1,1,0 diff --git a/account_asset_management/tests/__init__.py b/account_asset_management/tests/__init__.py index abadecb7..8187a598 100644 --- a/account_asset_management/tests/__init__.py +++ b/account_asset_management/tests/__init__.py @@ -1 +1,2 @@ from . import test_account_asset_management +from . import test_asset_management_xls diff --git a/account_asset_management/tests/test_asset_management_xls.py b/account_asset_management/tests/test_asset_management_xls.py new file mode 100644 index 00000000..3119203e --- /dev/null +++ b/account_asset_management/tests/test_asset_management_xls.py @@ -0,0 +1,37 @@ +# Copyright 2009-2019 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields +from odoo.tests.common import SavepointCase + + +class TestAssetManagementXls(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestAssetManagementXls, cls).setUpClass() + + module = __name__.split("addons.")[1].split(".")[0] + cls.xls_report_name = "{}.asset_report_xls".format(module) + cls.wiz_model = cls.env["wiz.account.asset.report"] + cls.company = cls.env.ref("base.main_company") + asset_group_id = cls.wiz_model._default_asset_group_id() + fy_dates = cls.company.compute_fiscalyear_dates(fields.date.today()) + + wiz_vals = { + "asset_group_id": asset_group_id, + "date_from": fy_dates["date_from"], + "date_to": fy_dates["date_to"], + } + cls.xls_report = cls.wiz_model.create(wiz_vals) + cls.report_action = cls.xls_report.xls_export() + + def test_01_action_xls(self): + """ Check report XLS action """ + self.assertGreaterEqual( + self.report_action.items(), + { + "type": "ir.actions.report", + "report_type": "xlsx", + "report_name": self.xls_report_name, + }.items(), + ) diff --git a/account_asset_management/views/account_asset.xml b/account_asset_management/views/account_asset.xml index 47d28990..0ad77c68 100644 --- a/account_asset_management/views/account_asset.xml +++ b/account_asset_management/views/account_asset.xml @@ -315,6 +315,7 @@ + @@ -353,6 +354,7 @@ + + + + Financial Assets report + wiz.account.asset.report + +
+ + + + + + + +
+
+
+
+
+ + Financial Assets report + ir.actions.act_window + wiz.account.asset.report + form + + new + + + +