[IMP] account_asset_management: Add reporting
This commit is contained in:
parent
0d332ba006
commit
e4796d3bd5
@ -1,2 +1,3 @@
|
||||
from . import models
|
||||
from . import report
|
||||
from . import wizard
|
||||
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
1
account_asset_management/report/__init__.py
Normal file
1
account_asset_management/report/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import account_asset_report_xls
|
684
account_asset_management/report/account_asset_report_xls.py
Normal file
684
account_asset_management/report/account_asset_report_xls.py
Normal file
@ -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)
|
@ -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
|
||||
|
|
@ -1 +1,2 @@
|
||||
from . import test_account_asset_management
|
||||
from . import test_asset_management_xls
|
||||
|
37
account_asset_management/tests/test_asset_management_xls.py
Normal file
37
account_asset_management/tests/test_asset_management_xls.py
Normal file
@ -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(),
|
||||
)
|
@ -315,6 +315,7 @@
|
||||
<field name="date_remove" />
|
||||
<field name="profile_id" />
|
||||
<field name="state" />
|
||||
<field name="group_ids" widget="many2many_tags" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
</tree>
|
||||
</field>
|
||||
@ -353,6 +354,7 @@
|
||||
<field name="code" />
|
||||
<field name="date_start" />
|
||||
<field name="profile_id" />
|
||||
<field name="group_ids" />
|
||||
<field
|
||||
name="partner_id"
|
||||
filter_domain="[('partner_id', 'child_of', self)]"
|
||||
|
@ -1,2 +1,3 @@
|
||||
from . import account_asset_compute
|
||||
from . import account_asset_remove
|
||||
from . import wiz_account_asset_report
|
||||
|
73
account_asset_management/wizard/wiz_account_asset_report.py
Normal file
73
account_asset_management/wizard/wiz_account_asset_report.py
Normal file
@ -0,0 +1,73 @@
|
||||
# Copyright 2009-2019 Noviat
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import unicodedata
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class WizAccountAssetReport(models.TransientModel):
|
||||
_name = "wiz.account.asset.report"
|
||||
_description = "Financial Assets report"
|
||||
|
||||
asset_group_id = fields.Many2one(
|
||||
comodel_name="account.asset.group",
|
||||
string="Asset Group",
|
||||
default=lambda self: self._default_asset_group_id(),
|
||||
)
|
||||
date_from = fields.Date(string="Start Date", required=True)
|
||||
date_to = fields.Date(string="End Date", required=True)
|
||||
draft = fields.Boolean(string="Include draft assets")
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self._default_company_id(),
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _default_asset_group_id(self):
|
||||
return (
|
||||
self.env["account.asset.group"]
|
||||
.search([("parent_id", "=", False)], limit=1)
|
||||
.id
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _default_company_id(self):
|
||||
return self.env.user.company_id
|
||||
|
||||
@api.onchange("company_id")
|
||||
def _onchange_company_id(self):
|
||||
fy_dates = self.company_id.compute_fiscalyear_dates(fields.date.today())
|
||||
self.date_from = fy_dates["date_from"]
|
||||
self.date_to = fy_dates["date_to"]
|
||||
|
||||
@api.constrains("date_from", "date_to")
|
||||
def _check_dates(self):
|
||||
for wiz in self:
|
||||
if wiz.date_to <= wiz.date_from:
|
||||
raise UserError(_("The Start Date must precede the Ending Date."))
|
||||
|
||||
def xls_export(self):
|
||||
self.ensure_one()
|
||||
report_name = "account_asset_management.asset_report_xls"
|
||||
if self.asset_group_id:
|
||||
prefix = (
|
||||
unicodedata.normalize("NFKD", self.asset_group_id.name)
|
||||
.encode("ascii", "ignore")
|
||||
.decode("ascii")
|
||||
)
|
||||
prefix = "".join(x for x in prefix if x.isalnum())
|
||||
report_file = "{}_asset_report".format(prefix)
|
||||
else:
|
||||
report_file = "asset_report"
|
||||
report = {
|
||||
"type": "ir.actions.report",
|
||||
"report_type": "xlsx",
|
||||
"report_name": report_name,
|
||||
"context": dict(self.env.context, report_file=report_file),
|
||||
"data": {"dynamic_report": True},
|
||||
}
|
||||
return report
|
52
account_asset_management/wizard/wiz_account_asset_report.xml
Normal file
52
account_asset_management/wizard/wiz_account_asset_report.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="wiz_account_asset_report_view_form" model="ir.ui.view">
|
||||
<field name="name">Financial Assets report</field>
|
||||
<field name="model">wiz.account.asset.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Financial Assets report">
|
||||
<group col="2" colspan="4">
|
||||
<field
|
||||
name="asset_group_id"
|
||||
options="{'no_create_edit': True, 'no_create': True}"
|
||||
/>
|
||||
<field name="date_from" />
|
||||
<field name="date_to" />
|
||||
<field name="draft" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
</group>
|
||||
<footer>
|
||||
<button
|
||||
name="xls_export"
|
||||
string="Generate Report"
|
||||
type="object"
|
||||
default_focus="1"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="wiz_account_asset_report_action" model="ir.actions.act_window">
|
||||
<field name="name">Financial Assets report</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">wiz.account.asset.report</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="wiz_account_asset_report_view_form" />
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
<menuitem
|
||||
id="account_asset_report_menu"
|
||||
name="Financial Assets"
|
||||
parent="account.menu_finance_reports"
|
||||
/>
|
||||
<menuitem
|
||||
id="wiz_account_asset_report_menu"
|
||||
name="Financial Assets report"
|
||||
parent="account_asset_report_menu"
|
||||
action="wiz_account_asset_report_action"
|
||||
sequence="200"
|
||||
/>
|
||||
</odoo>
|
Loading…
Reference in New Issue
Block a user