mirror of
https://github.com/bringout/oca-financial.git
synced 2026-04-25 04:22:08 +02:00
Initial commit: OCA Financial packages (186 packages)
This commit is contained in:
commit
3e0e8473fb
8757 changed files with 947473 additions and 0 deletions
|
|
@ -0,0 +1,45 @@
|
|||
# Assets Management
|
||||
|
||||
Odoo addon: account_asset_management
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install odoo-bringout-oca-account-financial-tools-account_asset_management
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
This addon depends on:
|
||||
- account
|
||||
- report_xlsx_helper
|
||||
|
||||
## Manifest Information
|
||||
|
||||
- **Name**: Assets Management
|
||||
- **Version**: 16.0.1.2.8
|
||||
- **Category**: Accounting & Finance
|
||||
- **License**: AGPL-3
|
||||
- **Installable**: False
|
||||
|
||||
## Source
|
||||
|
||||
Based on [OCA/account-financial-tools](https://github.com/OCA/account-financial-tools) branch 16.0, addon `account_asset_management`.
|
||||
|
||||
## License
|
||||
|
||||
This package maintains the original AGPL-3 license from the upstream Odoo project.
|
||||
|
||||
## Documentation
|
||||
|
||||
- Overview: doc/OVERVIEW.md
|
||||
- Architecture: doc/ARCHITECTURE.md
|
||||
- Models: doc/MODELS.md
|
||||
- Controllers: doc/CONTROLLERS.md
|
||||
- Wizards: doc/WIZARDS.md
|
||||
- Install: doc/INSTALL.md
|
||||
- Usage: doc/USAGE.md
|
||||
- Configuration: doc/CONFIGURATION.md
|
||||
- Dependencies: doc/DEPENDENCIES.md
|
||||
- Troubleshooting: doc/TROUBLESHOOTING.md
|
||||
- FAQ: doc/FAQ.md
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
=================
|
||||
Assets Management
|
||||
=================
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:52c019d93c48d81202b947b0b886cbfebb2a30a2a3dd1155d530bcc61c8620db
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Mature
|
||||
.. |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--financial--tools-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/account-financial-tools/tree/16.0/account_asset_management
|
||||
:alt: OCA/account-financial-tools
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/account-financial-tools-16-0/account-financial-tools-16-0-account_asset_management
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&target_branch=16.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
This Module manages the assets owned by a company. It will keep
|
||||
track of depreciation's occurred on those assets. And it allows to create
|
||||
accounting entries from the depreciation lines.
|
||||
|
||||
The full asset life-cycle is managed (from asset creation to asset removal).
|
||||
|
||||
Assets can be created manually as well as automatically
|
||||
(via the creation of an accounting entry on the asset account).
|
||||
|
||||
Depreciation Journal Entries can be created manually in the "Deprecation Board" tab,
|
||||
or automatically by two ways:
|
||||
|
||||
* Using the "Invoicing/Assets/Compute Assets" wizard.
|
||||
* Activating the "Asset Management: Generate assets" cron.
|
||||
|
||||
These options are compatibles each other.
|
||||
|
||||
The module contains a large number of functional enhancements compared to
|
||||
the standard account_asset module from Odoo.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
The module in NOT compatible with the standard account_asset module.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
14.0.1.0.0 (2021-01-08)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [BREAKING] Removed all functionality associated with `account.fiscal.year`
|
||||
|
||||
13.0.3.0.0 (2021-07-06)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Allow to reverse the posting of a depreciation line instead of deleting the
|
||||
journal entry.
|
||||
|
||||
13.0.2.0.0 (2021-02-19)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Add support for multi-company
|
||||
|
||||
13.0.1.0.0 (2019-10-21)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Python code and views were adapted to be compatible with v13.
|
||||
* When assets are created through accounting journal items,
|
||||
they are created when the journal items is posted.
|
||||
* When a Bill Invoice is created or modified, at the time it is saved,
|
||||
for each line that has an Asset profile and Quantity 'N'
|
||||
greater than 1, it will be replaced by 'N' lines identical to it but
|
||||
with quantity 1. This was done to maintain the same behavior as in
|
||||
the previous version, in which for each asset created there is a
|
||||
Journal Item. In addition, this solution does not change the data
|
||||
model which does not cause migration scripts.
|
||||
* The configuration option was removed so the only function of that is to
|
||||
allow the module to be uninstalled by unchecking that configuration option.
|
||||
* Tests were adapted.
|
||||
|
||||
12.0.2.1.0 (2019-10-21)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [IMP] Add option to calculate depreciation table by days
|
||||
|
||||
12.0.1.0.0 (2019-01-13)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [BREAKING] account.asset: parent_path has replaced parent_left & parent_right (TODO: migration script)
|
||||
* [BREAKING] account.asset.recompute.trigger: depends on date_range.py (TODO: re-implement in account_fiscal_year.py)
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-financial-tools/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_asset_management%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* Noviat
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* OpenERP SA
|
||||
* Luc De Meyer (Noviat)
|
||||
* Frédéric Clementi (camptocamp)
|
||||
* Florian Dacosta (Akretion)
|
||||
* Stéphane Bidoul (Acsone)
|
||||
* Adrien Peiffer (Acsone)
|
||||
* Akim Juillerat <akim.juillerat@camptocamp.com>
|
||||
* Henrik Norlin (Apps2GROW)
|
||||
* Maxence Groine <mgroine@fiefmanage.ch>
|
||||
* Kitti Upariphutthiphong <kittiu@ecosoft.co.th>
|
||||
* Saran Lim. <saranl@ecosoft.co.th>
|
||||
* `Tecnativa <https://www.tecnativa.com>`_:
|
||||
|
||||
* Ernesto Tejeda
|
||||
* Pedro M. Baeza
|
||||
* João Marques
|
||||
* Víctor Martínez
|
||||
|
||||
* `ForgeFlow <https://www.forgeflow.com>`_:
|
||||
|
||||
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
||||
* Miquel Raïch <miquel.raich@forgeflow.com>
|
||||
|
||||
* `Sygel <https://www.sygel.es>`_:
|
||||
|
||||
* Manuel Regidor <manuel.regidor@sygel.es>
|
||||
|
||||
* `Moduon <https://www.moduon.team>`_:
|
||||
|
||||
* Eduardo de Miguel
|
||||
|
||||
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.
|
||||
|
||||
This module is part of the `OCA/account-financial-tools <https://github.com/OCA/account-financial-tools/tree/16.0/account_asset_management>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from . import models
|
||||
from . import report
|
||||
from . import wizard
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# 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).
|
||||
|
||||
{
|
||||
"name": "Assets Management",
|
||||
"version": "16.0.1.2.8",
|
||||
"license": "AGPL-3",
|
||||
"depends": ["account", "report_xlsx_helper"],
|
||||
"excludes": ["account_asset"],
|
||||
"development_status": "Mature",
|
||||
"external_dependencies": {"python": ["python-dateutil"]},
|
||||
"author": "Noviat, Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/account-financial-tools",
|
||||
"category": "Accounting & Finance",
|
||||
"data": [
|
||||
"security/account_asset_security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"wizard/account_asset_compute.xml",
|
||||
"wizard/account_asset_remove.xml",
|
||||
"views/account_account.xml",
|
||||
"views/account_asset.xml",
|
||||
"views/account_asset_group.xml",
|
||||
"views/account_asset_profile.xml",
|
||||
"views/account_move.xml",
|
||||
"views/account_move_line.xml",
|
||||
"views/menuitem.xml",
|
||||
"data/cron.xml",
|
||||
"wizard/wiz_account_asset_report.xml",
|
||||
"wizard/wiz_asset_move_reverse.xml",
|
||||
],
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
<record forcecreate="True" id="ir_cron_assets_generator" model="ir.cron">
|
||||
<field name="name">Asset Management: Generate assets</field>
|
||||
<field name="model_id" ref="model_account_asset_compute" />
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.create({}).asset_compute()</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="active" eval="False" />
|
||||
<field name="doall" eval="False" />
|
||||
</record>
|
||||
</odoo>
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,51 @@
|
|||
# Copyright 2024 Moduon Team S.L. <info@moduon.team>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from openupgradelib import openupgrade
|
||||
|
||||
|
||||
@openupgrade.migrate()
|
||||
def migrate(env, version):
|
||||
"""Migrate Analytic Accounts to Analytic Distributions on Assets and Profiles"""
|
||||
openupgrade.add_fields(
|
||||
env,
|
||||
[
|
||||
(
|
||||
"analytic_distribution",
|
||||
"account.asset",
|
||||
"account_asset",
|
||||
"json",
|
||||
"jsonb",
|
||||
"account_asset_management",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"analytic_distribution",
|
||||
"account.asset.profile",
|
||||
"account_asset_profile",
|
||||
"json",
|
||||
"jsonb",
|
||||
"account_asset_management",
|
||||
None,
|
||||
),
|
||||
],
|
||||
)
|
||||
openupgrade.logged_query(
|
||||
env.cr,
|
||||
"""
|
||||
UPDATE account_asset
|
||||
SET analytic_distribution = jsonb_build_object(
|
||||
account_analytic_id::text, 100.0
|
||||
)
|
||||
WHERE account_analytic_id IS NOT NULL
|
||||
""",
|
||||
)
|
||||
openupgrade.logged_query(
|
||||
env.cr,
|
||||
"""
|
||||
UPDATE account_asset_profile
|
||||
SET analytic_distribution = jsonb_build_object(
|
||||
account_analytic_id::text, 100.0
|
||||
)
|
||||
WHERE account_analytic_id IS NOT NULL
|
||||
""",
|
||||
)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from . import account_account
|
||||
from . import account_asset
|
||||
from . import account_asset_group
|
||||
from . import account_asset_profile
|
||||
from . import account_asset_line
|
||||
from . import account_asset_recompute_trigger
|
||||
from . import account_move
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright 2009-2017 Noviat
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class AccountAccount(models.Model):
|
||||
_inherit = "account.account"
|
||||
|
||||
asset_profile_id = fields.Many2one(
|
||||
comodel_name="account.asset.profile",
|
||||
string="Asset Profile",
|
||||
check_company=True,
|
||||
help="Default Asset Profile when creating invoice lines with this account.",
|
||||
)
|
||||
|
||||
@api.constrains("asset_profile_id")
|
||||
def _check_asset_profile(self):
|
||||
for account in self:
|
||||
if (
|
||||
account.asset_profile_id
|
||||
and account.asset_profile_id.account_asset_id != account
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The Asset Account defined in the Asset Profile "
|
||||
"must be equal to the account."
|
||||
)
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,81 @@
|
|||
# Copyright 2009-2020 Noviat
|
||||
# Copyright 2019 Tecnativa - Pedro M. Baeza
|
||||
# Copyright 2021 Tecnativa - Víctor Martínez
|
||||
# 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 = "code, name"
|
||||
_parent_store = True
|
||||
_check_company_auto = True
|
||||
|
||||
name = fields.Char(size=64, required=True, index=True)
|
||||
code = fields.Char(index=True)
|
||||
parent_path = fields.Char(index=True, unaccent=False)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self._default_company_id(),
|
||||
)
|
||||
parent_id = fields.Many2one(
|
||||
comodel_name="account.asset.group",
|
||||
string="Parent Asset Group",
|
||||
ondelete="restrict",
|
||||
check_company=True,
|
||||
)
|
||||
child_ids = fields.One2many(
|
||||
comodel_name="account.asset.group",
|
||||
inverse_name="parent_id",
|
||||
string="Child Asset Groups",
|
||||
check_company=True,
|
||||
)
|
||||
|
||||
@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:]
|
||||
return self._search(
|
||||
expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid
|
||||
)
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
# Copyright 2009-2018 Noviat
|
||||
# Copyright 2021 Tecnativa - João Marques
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountAssetLine(models.Model):
|
||||
_name = "account.asset.line"
|
||||
_description = "Asset depreciation table line"
|
||||
_order = "type, line_date"
|
||||
_check_company_auto = True
|
||||
|
||||
name = fields.Char(string="Depreciation Name", size=64, readonly=True)
|
||||
asset_id = fields.Many2one(
|
||||
comodel_name="account.asset",
|
||||
string="Asset",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
check_company=True,
|
||||
index=True,
|
||||
)
|
||||
previous_id = fields.Many2one(
|
||||
comodel_name="account.asset.line",
|
||||
string="Previous Depreciation Line",
|
||||
readonly=True,
|
||||
)
|
||||
parent_state = fields.Selection(
|
||||
related="asset_id.state",
|
||||
string="State of Asset",
|
||||
)
|
||||
depreciation_base = fields.Monetary(
|
||||
related="asset_id.depreciation_base",
|
||||
string="Depreciation Base",
|
||||
)
|
||||
amount = fields.Monetary(required=True)
|
||||
remaining_value = fields.Monetary(
|
||||
compute="_compute_values",
|
||||
string="Next Period Depreciation",
|
||||
store=True,
|
||||
)
|
||||
depreciated_value = fields.Monetary(
|
||||
compute="_compute_values",
|
||||
string="Amount Already Depreciated",
|
||||
store=True,
|
||||
)
|
||||
line_date = fields.Date(string="Date", required=True)
|
||||
line_days = fields.Integer(string="Days", readonly=True)
|
||||
move_id = fields.Many2one(
|
||||
comodel_name="account.move",
|
||||
string="Depreciation Entry",
|
||||
readonly=True,
|
||||
check_company=True,
|
||||
)
|
||||
move_check = fields.Boolean(
|
||||
compute="_compute_move_check", string="Posted", store=True
|
||||
)
|
||||
type = fields.Selection(
|
||||
selection=[
|
||||
("create", "Depreciation Base"),
|
||||
("depreciate", "Depreciation"),
|
||||
("remove", "Asset Removal"),
|
||||
],
|
||||
readonly=True,
|
||||
default="depreciate",
|
||||
)
|
||||
init_entry = fields.Boolean(
|
||||
string="Initial Balance Entry",
|
||||
help="Set this flag for entries of previous fiscal years "
|
||||
"for which Odoo has not generated accounting entries.",
|
||||
)
|
||||
company_id = fields.Many2one(related="asset_id.company_id", store=True)
|
||||
currency_id = fields.Many2one(
|
||||
related="asset_id.company_id.currency_id", store=True, string="Company Currency"
|
||||
)
|
||||
|
||||
@api.depends("amount", "previous_id", "type")
|
||||
def _compute_values(self):
|
||||
self.depreciated_value = 0.0
|
||||
self.remaining_value = 0.0
|
||||
dlines = self
|
||||
if self.env.context.get("no_compute_asset_line_ids"):
|
||||
# skip compute for lines in unlink
|
||||
exclude_ids = self.env.context["no_compute_asset_line_ids"]
|
||||
dlines = self.filtered(lambda l: l.id not in exclude_ids)
|
||||
dlines = dlines.filtered(lambda l: l.type == "depreciate")
|
||||
dlines = dlines.sorted(key=lambda l: l.line_date)
|
||||
# Give value 0 to the lines that are not going to be calculated
|
||||
# to avoid cache miss error
|
||||
all_excluded_lines = self - dlines
|
||||
all_excluded_lines.depreciated_value = 0
|
||||
all_excluded_lines.remaining_value = 0
|
||||
# Group depreciation lines per asset
|
||||
asset_ids = dlines.mapped("asset_id")
|
||||
grouped_dlines = []
|
||||
for asset in asset_ids:
|
||||
grouped_dlines.append(dlines.filtered(lambda l: l.asset_id.id == asset.id))
|
||||
for dlines in grouped_dlines:
|
||||
for i, dl in enumerate(dlines):
|
||||
if i == 0:
|
||||
depreciation_base = dl.depreciation_base
|
||||
tmp = depreciation_base - dl.previous_id.remaining_value
|
||||
depreciated_value = dl.previous_id and tmp or 0.0
|
||||
remaining_value = depreciation_base - depreciated_value - dl.amount
|
||||
else:
|
||||
depreciated_value += dl.previous_id.amount
|
||||
remaining_value -= dl.amount
|
||||
dl.depreciated_value = depreciated_value
|
||||
dl.remaining_value = remaining_value
|
||||
|
||||
@api.depends("move_id")
|
||||
def _compute_move_check(self):
|
||||
for line in self:
|
||||
line.move_check = bool(line.move_id)
|
||||
|
||||
@api.onchange("amount")
|
||||
def _onchange_amount(self):
|
||||
if self.type == "depreciate":
|
||||
self.remaining_value = (
|
||||
self.depreciation_base - self.depreciated_value - self.amount
|
||||
)
|
||||
|
||||
def write(self, vals):
|
||||
for dl in self:
|
||||
line_date = vals.get("line_date") or dl.line_date
|
||||
asset_lines = dl.asset_id.depreciation_line_ids
|
||||
if list(vals.keys()) == ["move_id"] and not vals["move_id"]:
|
||||
# allow to remove an accounting entry via the
|
||||
# 'Delete Move' button on the depreciation lines.
|
||||
if not self.env.context.get("unlink_from_asset"):
|
||||
raise UserError(
|
||||
_(
|
||||
"You are not allowed to remove an accounting entry "
|
||||
"linked to an asset."
|
||||
"\nYou should remove such entries from the asset."
|
||||
)
|
||||
)
|
||||
elif list(vals.keys()) == ["asset_id"]:
|
||||
continue
|
||||
elif (
|
||||
dl.move_id
|
||||
and not self.env.context.get("allow_asset_line_update")
|
||||
and dl.type != "create"
|
||||
):
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot change a depreciation line "
|
||||
"with an associated accounting entry."
|
||||
)
|
||||
)
|
||||
elif vals.get("init_entry"):
|
||||
check = asset_lines.filtered(
|
||||
lambda l: l.move_check
|
||||
and l.type == "depreciate"
|
||||
and l.line_date <= line_date
|
||||
)
|
||||
if check:
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot set the 'Initial Balance Entry' flag "
|
||||
"on a depreciation line "
|
||||
"with prior posted entries."
|
||||
)
|
||||
)
|
||||
elif vals.get("line_date"):
|
||||
if dl.type == "create":
|
||||
check = asset_lines.filtered(
|
||||
lambda l: l.type != "create"
|
||||
and (l.init_entry or l.move_check)
|
||||
and l.line_date < fields.Date.to_date(vals["line_date"])
|
||||
)
|
||||
if check:
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot set the Asset Start Date "
|
||||
"after already posted entries."
|
||||
)
|
||||
)
|
||||
else:
|
||||
check = asset_lines.filtered(
|
||||
lambda al: al != dl
|
||||
and (al.init_entry or al.move_check)
|
||||
and al.line_date > fields.Date.to_date(vals["line_date"])
|
||||
)
|
||||
if check:
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot set the date on a depreciation line "
|
||||
"prior to already posted entries."
|
||||
)
|
||||
)
|
||||
return super().write(vals)
|
||||
|
||||
def unlink(self):
|
||||
for dl in self:
|
||||
if dl.type == "create" and dl.amount:
|
||||
raise UserError(
|
||||
_("You cannot remove an asset line " "of type 'Depreciation Base'.")
|
||||
)
|
||||
elif dl.move_id:
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot delete a depreciation line with "
|
||||
"an associated accounting entry."
|
||||
)
|
||||
)
|
||||
previous = dl.previous_id
|
||||
next_line = dl.asset_id.depreciation_line_ids.filtered(
|
||||
lambda l: l.previous_id == dl and l not in self
|
||||
)
|
||||
if next_line:
|
||||
next_line.previous_id = previous
|
||||
return super(
|
||||
AccountAssetLine, self.with_context(no_compute_asset_line_ids=self.ids)
|
||||
).unlink()
|
||||
|
||||
def _setup_move_data(self, depreciation_date):
|
||||
asset = self.asset_id
|
||||
move_data = {
|
||||
"date": depreciation_date,
|
||||
"ref": "{} - {}".format(asset.name, self.name),
|
||||
"journal_id": asset.profile_id.journal_id.id,
|
||||
}
|
||||
return move_data
|
||||
|
||||
def _setup_move_line_data(self, depreciation_date, account, ml_type, move):
|
||||
"""Prepare data to be propagated to account.move.line"""
|
||||
asset = self.asset_id
|
||||
currency = asset.company_id.currency_id
|
||||
amount = self.amount
|
||||
amount_comp = currency.compare_amounts(amount, 0)
|
||||
analytic_distribution = False
|
||||
if ml_type == "depreciation":
|
||||
debit = amount_comp < 0 and -amount or 0.0
|
||||
credit = amount_comp > 0 and amount or 0.0
|
||||
elif ml_type == "expense":
|
||||
debit = amount_comp > 0 and amount or 0.0
|
||||
credit = amount_comp < 0 and -amount or 0.0
|
||||
analytic_distribution = asset.analytic_distribution
|
||||
move_line_data = {
|
||||
"name": asset.name,
|
||||
"ref": self.name,
|
||||
"move_id": move.id,
|
||||
"account_id": account.id,
|
||||
"credit": credit,
|
||||
"debit": debit,
|
||||
"journal_id": asset.profile_id.journal_id.id,
|
||||
"partner_id": asset.partner_id.id,
|
||||
"analytic_distribution": analytic_distribution,
|
||||
"date": depreciation_date,
|
||||
"asset_id": asset.id,
|
||||
}
|
||||
return move_line_data
|
||||
|
||||
def create_move(self):
|
||||
created_move_ids = []
|
||||
asset_ids = set()
|
||||
ctx = dict(self.env.context, allow_asset=True, check_move_validity=False)
|
||||
for line in self:
|
||||
asset = line.asset_id
|
||||
depreciation_date = line.line_date
|
||||
am_vals = line._setup_move_data(depreciation_date)
|
||||
move = self.env["account.move"].with_context(**ctx).create(am_vals)
|
||||
depr_acc = asset.profile_id.account_depreciation_id
|
||||
exp_acc = asset.profile_id.account_expense_depreciation_id
|
||||
aml_d_vals = line._setup_move_line_data(
|
||||
depreciation_date, depr_acc, "depreciation", move
|
||||
)
|
||||
self.env["account.move.line"].with_context(**ctx).create(aml_d_vals)
|
||||
aml_e_vals = line._setup_move_line_data(
|
||||
depreciation_date, exp_acc, "expense", move
|
||||
)
|
||||
self.env["account.move.line"].with_context(**ctx).create(aml_e_vals)
|
||||
move.action_post()
|
||||
line.with_context(allow_asset_line_update=True).write({"move_id": move.id})
|
||||
created_move_ids.append(move.id)
|
||||
asset_ids.add(asset.id)
|
||||
# we re-evaluate the assets to determine if we can close them
|
||||
for asset in self.env["account.asset"].browse(list(asset_ids)):
|
||||
if asset.currency_id.is_zero(asset.value_residual):
|
||||
asset.state = "close"
|
||||
return created_move_ids
|
||||
|
||||
def open_move(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
"name": _("Journal Entry"),
|
||||
"view_mode": "form",
|
||||
"res_id": self.move_id.id,
|
||||
"res_model": "account.move",
|
||||
"view_id": False,
|
||||
"type": "ir.actions.act_window",
|
||||
"context": self.env.context,
|
||||
}
|
||||
|
||||
def update_asset_line_after_unlink_move(self):
|
||||
self.write({"move_id": False})
|
||||
if self.parent_state == "close":
|
||||
self.asset_id.write({"state": "open"})
|
||||
elif self.parent_state == "removed" and self.type == "remove":
|
||||
self.asset_id.write({"state": "close", "date_remove": False})
|
||||
self.unlink()
|
||||
|
||||
def unlink_move(self):
|
||||
for line in self:
|
||||
if line.asset_id.profile_id.allow_reversal:
|
||||
context = dict(self._context or {})
|
||||
context.update(
|
||||
{
|
||||
"active_model": self._name,
|
||||
"active_ids": line.ids,
|
||||
"active_id": line.id,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"name": _("Reverse Move"),
|
||||
"view_mode": "form",
|
||||
"res_model": "wiz.asset.move.reverse",
|
||||
"target": "new",
|
||||
"type": "ir.actions.act_window",
|
||||
"context": context,
|
||||
}
|
||||
else:
|
||||
move = line.move_id
|
||||
move.button_draft()
|
||||
move.with_context(force_delete=True, unlink_from_asset=True).unlink()
|
||||
line.with_context(
|
||||
unlink_from_asset=True
|
||||
).update_asset_line_after_unlink_move()
|
||||
return True
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
# Copyright 2009-2018 Noviat
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountAssetProfile(models.Model):
|
||||
_name = "account.asset.profile"
|
||||
_inherit = "analytic.mixin"
|
||||
_check_company_auto = True
|
||||
_description = "Asset profile"
|
||||
_order = "name"
|
||||
|
||||
name = fields.Char(size=64, required=True, index=True)
|
||||
note = fields.Text()
|
||||
account_asset_id = fields.Many2one(
|
||||
comodel_name="account.account",
|
||||
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]",
|
||||
string="Asset Account",
|
||||
check_company=True,
|
||||
required=True,
|
||||
)
|
||||
account_depreciation_id = fields.Many2one(
|
||||
comodel_name="account.account",
|
||||
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]",
|
||||
string="Depreciation Account",
|
||||
check_company=True,
|
||||
required=True,
|
||||
)
|
||||
account_expense_depreciation_id = fields.Many2one(
|
||||
comodel_name="account.account",
|
||||
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]",
|
||||
string="Depr. Expense Account",
|
||||
check_company=True,
|
||||
required=True,
|
||||
)
|
||||
account_plus_value_id = fields.Many2one(
|
||||
comodel_name="account.account",
|
||||
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]",
|
||||
check_company=True,
|
||||
string="Plus-Value Account",
|
||||
)
|
||||
account_min_value_id = fields.Many2one(
|
||||
comodel_name="account.account",
|
||||
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]",
|
||||
check_company=True,
|
||||
string="Min-Value Account",
|
||||
)
|
||||
account_residual_value_id = fields.Many2one(
|
||||
comodel_name="account.account",
|
||||
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]",
|
||||
check_company=True,
|
||||
string="Residual Value Account",
|
||||
)
|
||||
journal_id = fields.Many2one(
|
||||
comodel_name="account.journal",
|
||||
domain="[('type', '=', 'general'), ('company_id', '=', company_id)]",
|
||||
string="Journal",
|
||||
check_company=True,
|
||||
required=True,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self._default_company_id(),
|
||||
)
|
||||
group_ids = fields.Many2many(
|
||||
comodel_name="account.asset.group",
|
||||
relation="account_asset_profile_group_rel",
|
||||
column1="profile_id",
|
||||
column2="group_id",
|
||||
check_company=True,
|
||||
string="Asset Groups",
|
||||
)
|
||||
salvage_value = fields.Float(
|
||||
digits="Account",
|
||||
help="The estimated value that an asset will realize upon "
|
||||
"its sale at the end of its useful life.\n"
|
||||
"This value is used to determine the depreciation amounts.",
|
||||
)
|
||||
salvage_type = fields.Selection(
|
||||
selection=[("fixed", "Fixed"), ("percent", "Percentage of Price")]
|
||||
)
|
||||
method = fields.Selection(
|
||||
selection=lambda self: self._selection_method(),
|
||||
string="Computation Method",
|
||||
required=True,
|
||||
help="Choose the method to use to compute the depreciation lines.\n"
|
||||
" * Linear: Calculated on basis of: "
|
||||
"Depreciation Base / Number of Depreciations. "
|
||||
"Depreciation Base = Purchase Value - Salvage Value.\n"
|
||||
" * Linear-Limit: Linear up to Salvage Value. "
|
||||
"Depreciation Base = Purchase Value.\n"
|
||||
" * Degressive: Calculated on basis of: "
|
||||
"Residual Value * Degressive Factor.\n"
|
||||
" * Degressive-Linear (only for Time Method = Year): "
|
||||
"Degressive becomes linear when the annual linear "
|
||||
"depreciation exceeds the annual degressive depreciation.\n"
|
||||
" * Degressive-Limit: Degressive up to Salvage Value. "
|
||||
"The Depreciation Base is equal to the asset value.",
|
||||
default="linear",
|
||||
)
|
||||
method_number = fields.Integer(
|
||||
string="Number of Years",
|
||||
help="The number of years needed to depreciate your asset",
|
||||
default=5,
|
||||
)
|
||||
method_period = fields.Selection(
|
||||
selection=lambda self: self._selection_method_period(),
|
||||
string="Period Length",
|
||||
required=True,
|
||||
default="year",
|
||||
help="Period length for the depreciation accounting entries",
|
||||
)
|
||||
method_progress_factor = fields.Float(string="Degressive Factor", default=0.3)
|
||||
method_time = fields.Selection(
|
||||
selection=lambda self: self._selection_method_time(),
|
||||
string="Time Method",
|
||||
required=True,
|
||||
default="year",
|
||||
help="Choose the method to use to compute the dates and "
|
||||
"number of depreciation lines.\n"
|
||||
" * Number of Years: Specify the number of years "
|
||||
"for the depreciation.\n"
|
||||
" * Number of Depreciations: Fix the number of "
|
||||
"depreciation lines and the time between 2 depreciations.\n",
|
||||
)
|
||||
days_calc = fields.Boolean(
|
||||
string="Calculate by days",
|
||||
default=False,
|
||||
help="Use number of days to calculate depreciation amount",
|
||||
)
|
||||
use_leap_years = fields.Boolean(
|
||||
default=False,
|
||||
help="If not set, the system will distribute evenly the amount to "
|
||||
"amortize across the years, based on the number of years. "
|
||||
"So the amount per year will be the "
|
||||
"depreciation base / number of years.\n "
|
||||
"If set, the system will consider if the current year "
|
||||
"is a leap year. The amount to depreciate per year will be "
|
||||
"calculated as depreciation base / (depreciation end date - "
|
||||
"start date + 1) * days in the current year.",
|
||||
)
|
||||
prorata = fields.Boolean(
|
||||
string="Prorata Temporis",
|
||||
compute="_compute_prorrata",
|
||||
readonly=False,
|
||||
store=True,
|
||||
help="Indicates that the first depreciation entry for this asset "
|
||||
"has to be done from the depreciation start date instead of "
|
||||
"the first day of the fiscal year.",
|
||||
)
|
||||
open_asset = fields.Boolean(
|
||||
string="Skip Draft State",
|
||||
help="Check this if you want to automatically confirm the assets "
|
||||
"of this profile when created by invoices.",
|
||||
)
|
||||
asset_product_item = fields.Boolean(
|
||||
string="Create an asset by product item",
|
||||
help="By default during the validation of an invoice, an asset "
|
||||
"is created by invoice line as long as an accounting entry is "
|
||||
"created by invoice line. "
|
||||
"With this setting, an accounting entry will be created by "
|
||||
"product item. So, there will be an asset by product item.",
|
||||
)
|
||||
active = fields.Boolean(default=True)
|
||||
allow_reversal = fields.Boolean(
|
||||
"Allow Reversal of journal entries",
|
||||
help="If set, when pressing the Delete/Reverse Move button in a "
|
||||
"posted depreciation line will prompt the option to reverse the "
|
||||
"journal entry, instead of deleting them.",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _default_company_id(self):
|
||||
return self.env.company
|
||||
|
||||
@api.model
|
||||
def _selection_method(self):
|
||||
return [
|
||||
("linear", _("Linear")),
|
||||
("linear-limit", _("Linear up to Salvage Value")),
|
||||
("degressive", _("Degressive")),
|
||||
("degr-linear", _("Degressive-Linear")),
|
||||
("degr-limit", _("Degressive up to Salvage Value")),
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _selection_method_period(self):
|
||||
return [("month", _("Month")), ("quarter", _("Quarter")), ("year", _("Year"))]
|
||||
|
||||
@api.model
|
||||
def _selection_method_time(self):
|
||||
return [
|
||||
("year", _("Number of Years or end date")),
|
||||
("number", _("Number of Depreciations")),
|
||||
]
|
||||
|
||||
@api.constrains("method", "method_time")
|
||||
def _check_method(self):
|
||||
if any(a.method == "degr-linear" and a.method_time != "year" for a in self):
|
||||
raise UserError(
|
||||
_("Degressive-Linear is only supported for Time Method = Year.")
|
||||
)
|
||||
|
||||
@api.depends("method_time")
|
||||
def _compute_prorrata(self):
|
||||
for profile in self:
|
||||
if profile.method_time != "year":
|
||||
profile.prorata = True
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get("method_time") != "year" and not vals.get("prorata"):
|
||||
vals["prorata"] = True
|
||||
profile_ids = super().create(vals_list)
|
||||
account_dict = {}
|
||||
for profile_id in profile_ids.filtered(
|
||||
lambda x: not x.account_asset_id.asset_profile_id
|
||||
):
|
||||
account_dict.setdefault(profile_id.account_asset_id, []).append(
|
||||
profile_id.id
|
||||
)
|
||||
for account, profile_list in account_dict.items():
|
||||
account.write({"asset_profile_id": profile_list[-1]})
|
||||
return profile_ids
|
||||
|
||||
def write(self, vals):
|
||||
if vals.get("method_time"):
|
||||
if vals["method_time"] != "year" and not vals.get("prorata"):
|
||||
vals["prorata"] = True
|
||||
res = super().write(vals)
|
||||
# TODO last profile in self is defined as default on the related
|
||||
# account. must be improved.
|
||||
account = self.env["account.account"].browse(vals.get("account_asset_id"))
|
||||
if self and account and not account.asset_profile_id:
|
||||
account.write({"asset_profile_id": self[-1].id})
|
||||
return res
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright 2009-2018 Noviat
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountAssetRecomputeTrigger(models.Model):
|
||||
_name = "account.asset.recompute.trigger"
|
||||
_description = "Asset table recompute triggers"
|
||||
|
||||
reason = fields.Char(required=True)
|
||||
company_id = fields.Many2one("res.company", string="Company", required=True)
|
||||
date_trigger = fields.Datetime(
|
||||
"Trigger Date",
|
||||
readonly=True,
|
||||
help="Date of the event triggering the need to recompute the Asset Tables.",
|
||||
)
|
||||
date_completed = fields.Datetime("Completion Date", readonly=True)
|
||||
state = fields.Selection(
|
||||
selection=[("open", "Open"), ("done", "Done")],
|
||||
default="open",
|
||||
readonly=True,
|
||||
)
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
# Copyright 2009-2018 Noviat
|
||||
# Copyright 2021 Tecnativa - João Marques
|
||||
# Copyright 2021 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import Form
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# List of move's fields that can't be modified if move is linked
|
||||
# with a depreciation line
|
||||
FIELDS_AFFECTS_ASSET_MOVE = {"journal_id", "date"}
|
||||
# List of move line's fields that can't be modified if move is linked
|
||||
# with a depreciation line
|
||||
FIELDS_AFFECTS_ASSET_MOVE_LINE = {
|
||||
"credit",
|
||||
"debit",
|
||||
"account_id",
|
||||
"journal_id",
|
||||
"date",
|
||||
"asset_profile_id",
|
||||
"asset_id",
|
||||
}
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
asset_count = fields.Integer(compute="_compute_asset_count")
|
||||
|
||||
def _compute_asset_count(self):
|
||||
rg_res = self.env["account.asset.line"].read_group(
|
||||
[("move_id", "in", self.ids)], ["move_id"], ["move_id"]
|
||||
)
|
||||
mapped_data = {x["move_id"][0]: x["move_id_count"] for x in rg_res}
|
||||
for move in self:
|
||||
move.asset_count = mapped_data.get(move.id, 0)
|
||||
|
||||
def unlink(self):
|
||||
# for move in self:
|
||||
deprs = (
|
||||
self.env["account.asset.line"]
|
||||
.sudo()
|
||||
.search(
|
||||
[("move_id", "in", self.ids), ("type", "in", ["depreciate", "remove"])]
|
||||
)
|
||||
)
|
||||
if deprs and not self.env.context.get("unlink_from_asset"):
|
||||
raise UserError(
|
||||
_(
|
||||
"You are not allowed to remove an accounting entry "
|
||||
"linked to an asset."
|
||||
"\nYou should remove such entries from the asset."
|
||||
)
|
||||
)
|
||||
# trigger store function
|
||||
deprs.write({"move_id": False})
|
||||
return super().unlink()
|
||||
|
||||
def write(self, vals):
|
||||
if set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE):
|
||||
deprs = (
|
||||
self.env["account.asset.line"]
|
||||
.sudo()
|
||||
.search([("move_id", "in", self.ids), ("type", "=", "depreciate")])
|
||||
)
|
||||
if deprs:
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot change an accounting entry "
|
||||
"linked to an asset depreciation line."
|
||||
)
|
||||
)
|
||||
return super().write(vals)
|
||||
|
||||
def _prepare_asset_vals(self, aml):
|
||||
depreciation_base = aml.balance
|
||||
return {
|
||||
"name": aml.name,
|
||||
"code": self.name,
|
||||
"profile_id": aml.asset_profile_id,
|
||||
"purchase_value": depreciation_base,
|
||||
"partner_id": aml.partner_id,
|
||||
"date_start": self.date,
|
||||
}
|
||||
|
||||
def _post(self, soft=True):
|
||||
ret_val = super()._post(soft=soft)
|
||||
for move in self:
|
||||
for aml in move.line_ids.filtered(
|
||||
lambda line: line.asset_profile_id and not line.tax_line_id
|
||||
):
|
||||
vals = move._prepare_asset_vals(aml)
|
||||
if not aml.name:
|
||||
raise UserError(
|
||||
_("Asset name must be set in the label of the line.")
|
||||
)
|
||||
if aml.asset_id:
|
||||
continue
|
||||
asset_form = Form(
|
||||
self.env["account.asset"]
|
||||
.with_company(move.company_id)
|
||||
.with_context(create_asset_from_move_line=True, move_id=move.id)
|
||||
)
|
||||
for key, val in vals.items():
|
||||
setattr(asset_form, key, val)
|
||||
asset = asset_form.save()
|
||||
asset.analytic_distribution = aml.analytic_distribution
|
||||
aml.with_context(
|
||||
allow_asset=True, allow_asset_removal=True
|
||||
).asset_id = asset.id
|
||||
refs = [
|
||||
"<a href=# data-oe-model=account.asset data-oe-id=%s>%s</a>"
|
||||
% tuple(name_get)
|
||||
for name_get in move.line_ids.filtered(
|
||||
"asset_profile_id"
|
||||
).asset_id.name_get()
|
||||
]
|
||||
if refs:
|
||||
message = _("This invoice created the asset(s): %s") % ", ".join(refs)
|
||||
move.message_post(body=message)
|
||||
return ret_val
|
||||
|
||||
def button_draft(self):
|
||||
invoices = self.filtered(lambda r: r.is_purchase_document())
|
||||
if invoices:
|
||||
invoices.line_ids.asset_id.unlink()
|
||||
return super().button_draft()
|
||||
|
||||
def _reverse_move_vals(self, default_values, cancel=True):
|
||||
move_vals = super()._reverse_move_vals(default_values, cancel)
|
||||
if move_vals["move_type"] not in ("out_invoice", "out_refund"):
|
||||
for line_command in move_vals.get("line_ids", []):
|
||||
line_vals = line_command[2] # (0, 0, {...})
|
||||
asset = self.env["account.asset"].browse(line_vals["asset_id"])
|
||||
# We remove the asset if we recognize that we are reversing
|
||||
# the asset creation
|
||||
if asset:
|
||||
asset_line = self.env["account.asset.line"].search(
|
||||
[("asset_id", "=", asset.id), ("type", "=", "create")], limit=1
|
||||
)
|
||||
if asset_line and asset_line.move_id == self:
|
||||
asset.unlink()
|
||||
line_vals.update(asset_profile_id=False, asset_id=False)
|
||||
return move_vals
|
||||
|
||||
def action_view_assets(self):
|
||||
assets = (
|
||||
self.env["account.asset.line"]
|
||||
.search([("move_id", "=", self.id)])
|
||||
.mapped("asset_id")
|
||||
)
|
||||
action = self.env.ref("account_asset_management.account_asset_action")
|
||||
action_dict = action.sudo().read()[0]
|
||||
if len(assets) == 1:
|
||||
res = self.env.ref(
|
||||
"account_asset_management.account_asset_view_form", False
|
||||
)
|
||||
action_dict["views"] = [(res and res.id or False, "form")]
|
||||
action_dict["res_id"] = assets.id
|
||||
elif assets:
|
||||
action_dict["domain"] = [("id", "in", assets.ids)]
|
||||
else:
|
||||
action_dict = {"type": "ir.actions.act_window_close"}
|
||||
return action_dict
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
asset_profile_id = fields.Many2one(
|
||||
comodel_name="account.asset.profile",
|
||||
string="Asset Profile",
|
||||
compute="_compute_asset_profile",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
asset_id = fields.Many2one(
|
||||
comodel_name="account.asset",
|
||||
string="Asset",
|
||||
ondelete="restrict",
|
||||
check_company=True,
|
||||
copy=False,
|
||||
)
|
||||
|
||||
@api.depends("account_id", "asset_id")
|
||||
def _compute_asset_profile(self):
|
||||
for rec in self:
|
||||
if rec.account_id.asset_profile_id and not rec.asset_id:
|
||||
rec.asset_profile_id = rec.account_id.asset_profile_id
|
||||
elif rec.asset_id:
|
||||
rec.asset_profile_id = rec.asset_id.profile_id
|
||||
|
||||
@api.onchange("asset_profile_id")
|
||||
def _onchange_asset_profile_id(self):
|
||||
if self.asset_profile_id.account_asset_id:
|
||||
self.account_id = self.asset_profile_id.account_asset_id
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
move = self.env["account.move"].browse(vals.get("move_id"))
|
||||
if not move.is_sale_document():
|
||||
if vals.get("asset_id") and not self.env.context.get("allow_asset"):
|
||||
raise UserError(
|
||||
_(
|
||||
"You are not allowed to link "
|
||||
"an accounting entry to an asset."
|
||||
"\nYou should generate such entries from the asset."
|
||||
)
|
||||
)
|
||||
records = super().create(vals_list)
|
||||
for record in records:
|
||||
record._expand_asset_line()
|
||||
return records
|
||||
|
||||
def write(self, vals):
|
||||
if set(vals).intersection(FIELDS_AFFECTS_ASSET_MOVE_LINE) and not (
|
||||
self.env.context.get("allow_asset_removal")
|
||||
and list(vals.keys()) == ["asset_id"]
|
||||
):
|
||||
# Check if at least one asset is linked to a move
|
||||
linked_asset = False
|
||||
for move_line in self.filtered(lambda r: not r.move_id.is_sale_document()):
|
||||
linked_asset = move_line.asset_id
|
||||
if linked_asset:
|
||||
raise UserError(
|
||||
_(
|
||||
"You cannot change an accounting item "
|
||||
"linked to an asset depreciation line."
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
self.filtered(lambda r: not r.move_id.is_sale_document())
|
||||
and vals.get("asset_id")
|
||||
and not self.env.context.get("allow_asset")
|
||||
):
|
||||
raise UserError(
|
||||
_(
|
||||
"You are not allowed to link "
|
||||
"an accounting entry to an asset."
|
||||
"\nYou should generate such entries from the asset."
|
||||
)
|
||||
)
|
||||
super().write(vals)
|
||||
if "quantity" in vals or "asset_profile_id" in vals:
|
||||
for record in self:
|
||||
record._expand_asset_line()
|
||||
return True
|
||||
|
||||
def _expand_asset_line(self):
|
||||
self.ensure_one()
|
||||
if self.quantity > 1.0 and self.asset_profile_id.asset_product_item:
|
||||
aml = self.with_context(check_move_validity=False)
|
||||
qty = self.quantity
|
||||
name = self.name
|
||||
aml.write(
|
||||
{
|
||||
"quantity": 1,
|
||||
"name": "{} {}".format(name, 1),
|
||||
# Make sure the price is not changed, like with account_invoice_pricelist
|
||||
"price_unit": self.price_unit,
|
||||
}
|
||||
)
|
||||
for i in range(1, int(qty)):
|
||||
aml.copy({"name": "{} {}".format(name, i + 1)})
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
* OpenERP SA
|
||||
* Luc De Meyer (Noviat)
|
||||
* Frédéric Clementi (camptocamp)
|
||||
* Florian Dacosta (Akretion)
|
||||
* Stéphane Bidoul (Acsone)
|
||||
* Adrien Peiffer (Acsone)
|
||||
* Akim Juillerat <akim.juillerat@camptocamp.com>
|
||||
* Henrik Norlin (Apps2GROW)
|
||||
* Maxence Groine <mgroine@fiefmanage.ch>
|
||||
* Kitti Upariphutthiphong <kittiu@ecosoft.co.th>
|
||||
* Saran Lim. <saranl@ecosoft.co.th>
|
||||
* `Tecnativa <https://www.tecnativa.com>`_:
|
||||
|
||||
* Ernesto Tejeda
|
||||
* Pedro M. Baeza
|
||||
* João Marques
|
||||
* Víctor Martínez
|
||||
|
||||
* `ForgeFlow <https://www.forgeflow.com>`_:
|
||||
|
||||
* Jordi Ballester <jordi.ballester@forgeflow.com>
|
||||
* Miquel Raïch <miquel.raich@forgeflow.com>
|
||||
|
||||
* `Sygel <https://www.sygel.es>`_:
|
||||
|
||||
* Manuel Regidor <manuel.regidor@sygel.es>
|
||||
|
||||
* `Moduon <https://www.moduon.team>`_:
|
||||
|
||||
* Eduardo de Miguel
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
This Module manages the assets owned by a company. It will keep
|
||||
track of depreciation's occurred on those assets. And it allows to create
|
||||
accounting entries from the depreciation lines.
|
||||
|
||||
The full asset life-cycle is managed (from asset creation to asset removal).
|
||||
|
||||
Assets can be created manually as well as automatically
|
||||
(via the creation of an accounting entry on the asset account).
|
||||
|
||||
Depreciation Journal Entries can be created manually in the "Deprecation Board" tab,
|
||||
or automatically by two ways:
|
||||
|
||||
* Using the "Invoicing/Assets/Compute Assets" wizard.
|
||||
* Activating the "Asset Management: Generate assets" cron.
|
||||
|
||||
These options are compatibles each other.
|
||||
|
||||
The module contains a large number of functional enhancements compared to
|
||||
the standard account_asset module from Odoo.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
14.0.1.0.0 (2021-01-08)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [BREAKING] Removed all functionality associated with `account.fiscal.year`
|
||||
|
||||
13.0.3.0.0 (2021-07-06)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Allow to reverse the posting of a depreciation line instead of deleting the
|
||||
journal entry.
|
||||
|
||||
13.0.2.0.0 (2021-02-19)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Add support for multi-company
|
||||
|
||||
13.0.1.0.0 (2019-10-21)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Python code and views were adapted to be compatible with v13.
|
||||
* When assets are created through accounting journal items,
|
||||
they are created when the journal items is posted.
|
||||
* When a Bill Invoice is created or modified, at the time it is saved,
|
||||
for each line that has an Asset profile and Quantity 'N'
|
||||
greater than 1, it will be replaced by 'N' lines identical to it but
|
||||
with quantity 1. This was done to maintain the same behavior as in
|
||||
the previous version, in which for each asset created there is a
|
||||
Journal Item. In addition, this solution does not change the data
|
||||
model which does not cause migration scripts.
|
||||
* The configuration option was removed so the only function of that is to
|
||||
allow the module to be uninstalled by unchecking that configuration option.
|
||||
* Tests were adapted.
|
||||
|
||||
12.0.2.1.0 (2019-10-21)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [IMP] Add option to calculate depreciation table by days
|
||||
|
||||
12.0.1.0.0 (2019-01-13)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* [BREAKING] account.asset: parent_path has replaced parent_left & parent_right (TODO: migration script)
|
||||
* [BREAKING] account.asset.recompute.trigger: depends on date_range.py (TODO: re-implement in account_fiscal_year.py)
|
||||
|
|
@ -0,0 +1 @@
|
|||
The module in NOT compatible with the standard account_asset module.
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import account_asset_report_xls
|
||||
|
|
@ -0,0 +1,734 @@
|
|||
# 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.addons.report_xlsx_helper.report.report_xlsx_format import (
|
||||
FORMATS,
|
||||
XLS_HEADERS,
|
||||
)
|
||||
|
||||
_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 _get_ws_params(self, wb, data, wiz):
|
||||
self._get_assets(wiz, data)
|
||||
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": _("Account")},
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"value": self._render(
|
||||
"asset.profile_id.account_asset_id.code or ''"
|
||||
),
|
||||
},
|
||||
"totals": {"type": "string", "value": _("Totals")},
|
||||
"width": 20,
|
||||
},
|
||||
"name": {
|
||||
"header": {"type": "string", "value": _("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": _("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": _("Asset Start Date")},
|
||||
"asset": {
|
||||
"value": self._render("asset.date_start or ''"),
|
||||
"format": FORMATS["format_tcell_date_left"],
|
||||
},
|
||||
"width": 20,
|
||||
},
|
||||
"date_remove": {
|
||||
"header": {"type": "string", "value": _("Asset Removal Date")},
|
||||
"asset": {
|
||||
"value": self._render("asset.date_remove or ''"),
|
||||
"format": FORMATS["format_tcell_date_left"],
|
||||
},
|
||||
"width": 20,
|
||||
},
|
||||
"depreciation_base": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Depreciation Base"),
|
||||
"format": FORMATS["format_theader_yellow_right"],
|
||||
},
|
||||
"asset_group": {
|
||||
"type": "number",
|
||||
"value": self._render('group_entry["_depreciation_base"]'),
|
||||
"format": FORMATS["format_theader_blue_amount_right"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "number",
|
||||
"value": self._render("asset.depreciation_base"),
|
||||
"format": FORMATS["format_tcell_amount_right"],
|
||||
},
|
||||
"totals": {
|
||||
"type": "formula",
|
||||
"value": self._render("asset_total_formula"),
|
||||
"format": FORMATS["format_theader_yellow_amount_right"],
|
||||
},
|
||||
"width": 18,
|
||||
},
|
||||
"salvage_value": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Salvage Value"),
|
||||
"format": FORMATS["format_theader_yellow_right"],
|
||||
},
|
||||
"asset_group": {
|
||||
"type": "number",
|
||||
"value": self._render('group_entry["_salvage_value"]'),
|
||||
"format": FORMATS["format_theader_blue_amount_right"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "number",
|
||||
"value": self._render("asset.salvage_value"),
|
||||
"format": FORMATS["format_tcell_amount_right"],
|
||||
},
|
||||
"totals": {
|
||||
"type": "formula",
|
||||
"value": self._render("salvage_total_formula"),
|
||||
"format": FORMATS["format_theader_yellow_amount_right"],
|
||||
},
|
||||
"width": 18,
|
||||
},
|
||||
"purchase_value": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Purchase Value"),
|
||||
"format": FORMATS["format_theader_yellow_right"],
|
||||
},
|
||||
"asset_group": {
|
||||
"type": "number",
|
||||
"value": self._render('group_entry["_purchase_value"]'),
|
||||
"format": FORMATS["format_theader_blue_amount_right"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "number",
|
||||
"value": self._render("asset.purchase_value"),
|
||||
"format": FORMATS["format_tcell_amount_right"],
|
||||
},
|
||||
"totals": {
|
||||
"type": "formula",
|
||||
"value": self._render("purchase_total_formula"),
|
||||
"format": FORMATS["format_theader_yellow_amount_right"],
|
||||
},
|
||||
"width": 18,
|
||||
},
|
||||
"period_start_value": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Period Start Value"),
|
||||
"format": FORMATS["format_theader_yellow_right"],
|
||||
},
|
||||
"asset_group": {
|
||||
"type": "number",
|
||||
"value": self._render('group_entry["_period_start_value"]'),
|
||||
"format": FORMATS["format_theader_blue_amount_right"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "number",
|
||||
"value": self._render('asset_entry["_period_start_value"]'),
|
||||
"format": FORMATS["format_tcell_amount_right"],
|
||||
},
|
||||
"totals": {
|
||||
"type": "formula",
|
||||
"value": self._render("period_start_total_formula"),
|
||||
"format": FORMATS["format_theader_yellow_amount_right"],
|
||||
},
|
||||
"width": 18,
|
||||
},
|
||||
"period_depr": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Period Depreciation"),
|
||||
"format": FORMATS["format_theader_yellow_right"],
|
||||
},
|
||||
"asset_group": {
|
||||
"type": "formula",
|
||||
"value": self._render("period_diff_formula"),
|
||||
"format": FORMATS["format_theader_blue_amount_right"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "formula",
|
||||
"value": self._render("period_diff_formula"),
|
||||
"format": FORMATS["format_tcell_amount_right"],
|
||||
},
|
||||
"totals": {
|
||||
"type": "formula",
|
||||
"value": self._render("period_diff_formula"),
|
||||
"format": FORMATS["format_theader_yellow_amount_right"],
|
||||
},
|
||||
"width": 18,
|
||||
},
|
||||
"period_end_value": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Period End Value"),
|
||||
"format": FORMATS["format_theader_yellow_right"],
|
||||
},
|
||||
"asset_group": {
|
||||
"type": "number",
|
||||
"value": self._render('group_entry["_period_end_value"]'),
|
||||
"format": FORMATS["format_theader_blue_amount_right"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "number",
|
||||
"value": self._render('asset_entry["_period_end_value"]'),
|
||||
"format": FORMATS["format_tcell_amount_right"],
|
||||
},
|
||||
"totals": {
|
||||
"type": "formula",
|
||||
"value": self._render("period_end_total_formula"),
|
||||
"format": FORMATS["format_theader_yellow_amount_right"],
|
||||
},
|
||||
"width": 18,
|
||||
},
|
||||
"period_end_depr": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Tot. Depreciation"),
|
||||
"format": FORMATS["format_theader_yellow_right"],
|
||||
},
|
||||
"asset_group": {
|
||||
"type": "formula",
|
||||
"value": self._render("total_depr_formula"),
|
||||
"format": FORMATS["format_theader_blue_amount_right"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "formula",
|
||||
"value": self._render("total_depr_formula"),
|
||||
"format": FORMATS["format_tcell_amount_right"],
|
||||
},
|
||||
"totals": {
|
||||
"type": "formula",
|
||||
"value": self._render("total_depr_formula"),
|
||||
"format": FORMATS["format_theader_yellow_amount_right"],
|
||||
},
|
||||
"width": 18,
|
||||
},
|
||||
"method": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Comput. Method"),
|
||||
"format": FORMATS["format_theader_yellow_center"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"value": self._render("asset.method or ''"),
|
||||
"format": FORMATS["format_tcell_center"],
|
||||
},
|
||||
"width": 20,
|
||||
},
|
||||
"method_number": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Number of Years"),
|
||||
"format": FORMATS["format_theader_yellow_center"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "number",
|
||||
"value": self._render("asset.method_number"),
|
||||
"format": FORMATS["format_tcell_integer_center"],
|
||||
},
|
||||
"width": 20,
|
||||
},
|
||||
"prorata": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Prorata Temporis"),
|
||||
"format": FORMATS["format_theader_yellow_center"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "boolean",
|
||||
"value": self._render("asset.prorata"),
|
||||
"format": FORMATS["format_tcell_center"],
|
||||
},
|
||||
"width": 20,
|
||||
},
|
||||
"state": {
|
||||
"header": {
|
||||
"type": "string",
|
||||
"value": _("Status"),
|
||||
"format": FORMATS["format_theader_yellow_center"],
|
||||
},
|
||||
"asset": {
|
||||
"type": "string",
|
||||
"value": self._render("asset.state"),
|
||||
"format": FORMATS["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", frmt="normal")
|
||||
title_short = self._get_title(wiz, "acquisition", frmt="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", frmt="normal")
|
||||
title_short = self._get_title(wiz, "active", frmt="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", frmt="normal")
|
||||
title_short = self._get_title(wiz, "removal", frmt="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, frmt="normal"):
|
||||
|
||||
prefix = "{} - {}".format(wiz.date_from, wiz.date_to)
|
||||
if report == "acquisition":
|
||||
if frmt == "normal":
|
||||
title = prefix + " : " + _("New Acquisitions")
|
||||
else:
|
||||
title = "ACQ"
|
||||
elif report == "active":
|
||||
if frmt == "normal":
|
||||
title = prefix + " : " + _("Active Assets")
|
||||
else:
|
||||
title = "ACT"
|
||||
else:
|
||||
if frmt == "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, FORMATS["format_left_bold"])
|
||||
|
||||
def _get_assets(self, wiz, data):
|
||||
"""Add the selected assets, both grouped and ungrouped, to `data`"""
|
||||
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 '{group}' (id {id})"
|
||||
).format(group=child.name, id=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"))
|
||||
assets = self.env["account.asset"].search(dom)
|
||||
grouped_assets = {}
|
||||
self._group_assets(assets, parent_group, grouped_assets)
|
||||
data.update(
|
||||
{
|
||||
"assets": assets,
|
||||
"grouped_assets": 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 or "", r.name)
|
||||
)
|
||||
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):
|
||||
filt = getattr(self, "{}_filter".format(report))
|
||||
return filt(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_entry = {
|
||||
"_purchase_value": 0.0,
|
||||
"_depreciation_base": 0.0,
|
||||
"_salvage_value": 0.0,
|
||||
"_period_start_value": 0.0,
|
||||
"_period_end_value": 0.0,
|
||||
"group": group,
|
||||
}
|
||||
for asset in assets:
|
||||
asset_entry = {"asset": asset}
|
||||
group_entry["_purchase_value"] += asset.purchase_value
|
||||
group_entry["_depreciation_base"] += asset.depreciation_base
|
||||
group_entry["_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 and asset.method_number:
|
||||
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_entry["_period_start_value"] = (
|
||||
asset.depreciation_base - value_depreciated
|
||||
)
|
||||
group_entry["_period_start_value"] += asset_entry["_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_entry["_period_end_value"] = (
|
||||
asset.depreciation_base - value_depreciated
|
||||
)
|
||||
group_entry["_period_end_value"] += asset_entry["_period_end_value"]
|
||||
|
||||
asset_entries.append(asset_entry)
|
||||
|
||||
todos = []
|
||||
for g in group.child_ids:
|
||||
if _has_assets(g, group_val[g]):
|
||||
todos.append(g)
|
||||
|
||||
entries.append(group_entry)
|
||||
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(XLS_HEADERS["xls_headers"]["standard"])
|
||||
ws.set_footer(XLS_HEADERS["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):
|
||||
filt = getattr(self, "{}_filter".format(report))
|
||||
return filt(wiz, asset)
|
||||
|
||||
assets = data["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=FORMATS["format_theader_yellow_left"],
|
||||
)
|
||||
|
||||
ws.freeze_panes(row_pos, 0)
|
||||
|
||||
row_pos_start = row_pos
|
||||
purchase_value_pos = "purchase_value" in wl and wl.index("purchase_value")
|
||||
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 = data["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)):
|
||||
if "group" in entry:
|
||||
parent = entry["group"].parent_id
|
||||
for parent_entry in reversed(entries[: -i - 1]):
|
||||
if "group" in parent_entry and parent_entry["group"] == parent:
|
||||
parent_entry["_purchase_value"] += entry["_purchase_value"]
|
||||
parent_entry["_depreciation_base"] += entry[
|
||||
"_depreciation_base"
|
||||
]
|
||||
parent_entry["_salvage_value"] += entry["_salvage_value"]
|
||||
parent_entry["_period_start_value"] += entry[
|
||||
"_period_start_value"
|
||||
]
|
||||
parent_entry["_period_end_value"] += entry["_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"],
|
||||
"group_entry": entry,
|
||||
"period_diff_formula": period_diff_formula,
|
||||
"total_depr_formula": total_depr_formula,
|
||||
},
|
||||
default_format=FORMATS["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": entry["asset"],
|
||||
"asset_entry": entry,
|
||||
"period_diff_formula": period_diff_formula,
|
||||
"total_depr_formula": total_depr_formula,
|
||||
},
|
||||
default_format=FORMATS["format_tcell_left"],
|
||||
)
|
||||
|
||||
purchase_total_formula = purchase_value_pos and self._rowcol_to_cell(
|
||||
row_pos_start, purchase_value_pos
|
||||
)
|
||||
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={
|
||||
"purchase_total_formula": purchase_total_formula,
|
||||
"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=FORMATS["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, FORMATS["format_left_bold"])
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
<record id="account_asset_profile_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account Asset Profile multi-company</field>
|
||||
<field ref="model_account_asset_profile" name="model_id" />
|
||||
<field eval="True" name="global" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="account_asset_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account Asset multi-company</field>
|
||||
<field ref="model_account_asset" name="model_id" />
|
||||
<field eval="True" name="global" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="account_asset_group_multi_company_rule" model="ir.rule">
|
||||
<field name="name">Account Asset Group multi-company</field>
|
||||
<field ref="model_account_asset_group" name="model_id" />
|
||||
<field eval="True" name="global" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_account_asset_profile_invoice,account.asset.profile,model_account_asset_profile,account.group_account_invoice,1,0,0,0
|
||||
access_account_asset_profile_user,account.asset.profile,model_account_asset_profile,account.group_account_user,1,0,0,0
|
||||
access_account_asset_profile_manager,account.asset.profile,model_account_asset_profile,account.group_account_manager,1,1,1,1
|
||||
access_account_asset_invoice,account.asset,model_account_asset,account.group_account_invoice,1,1,1,1
|
||||
access_account_asset_user,account.asset,model_account_asset,account.group_account_user,1,1,1,1
|
||||
access_account_asset_manager,account.asset,model_account_asset,account.group_account_manager,1,1,1,1
|
||||
access_account_asset_line_invoice,account.asset.line,model_account_asset_line,account.group_account_invoice,1,1,1,1
|
||||
access_account_asset_line_user,account.asset.line,model_account_asset_line,account.group_account_user,1,1,1,1
|
||||
access_account_asset_line_manager,account.asset.line,model_account_asset_line,account.group_account_manager,1,1,1,1
|
||||
access_account_asset_recompute_trigger_user,account.asset.recompute.trigger,model_account_asset_recompute_trigger,account.group_account_user,1,1,1,1
|
||||
access_account_asset_recompute_trigger_manager,account.asset.recompute.trigger,model_account_asset_recompute_trigger,account.group_account_manager,1,1,1,1
|
||||
access_account_asset_group_invoice,account.asset.group,model_account_asset_group,account.group_account_invoice,1,0,0,0
|
||||
access_account_asset_group_user,account.asset.group,model_account_asset_group,account.group_account_user,1,0,0,0
|
||||
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
|
||||
access_wiz_asset_move_reverse_user,wiz.asset.move.reverse,model_wiz_asset_move_reverse,account.group_account_user,1,1,1,1
|
||||
|
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
|
|
@ -0,0 +1,536 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
|
||||
<title>Assets Management</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
Despite the name, some widely supported CSS2 features are used.
|
||||
|
||||
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: gray; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic, pre.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="assets-management">
|
||||
<h1 class="title">Assets Management</h1>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:52c019d93c48d81202b947b0b886cbfebb2a30a2a3dd1155d530bcc61c8620db
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Mature" src="https://img.shields.io/badge/maturity-Mature-brightgreen.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/account-financial-tools/tree/16.0/account_asset_management"><img alt="OCA/account-financial-tools" src="https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/account-financial-tools-16-0/account-financial-tools-16-0-account_asset_management"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/account-financial-tools&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>This Module manages the assets owned by a company. It will keep
|
||||
track of depreciation’s occurred on those assets. And it allows to create
|
||||
accounting entries from the depreciation lines.</p>
|
||||
<p>The full asset life-cycle is managed (from asset creation to asset removal).</p>
|
||||
<p>Assets can be created manually as well as automatically
|
||||
(via the creation of an accounting entry on the asset account).</p>
|
||||
<p>Depreciation Journal Entries can be created manually in the “Deprecation Board” tab,
|
||||
or automatically by two ways:</p>
|
||||
<ul class="simple">
|
||||
<li>Using the “Invoicing/Assets/Compute Assets” wizard.</li>
|
||||
<li>Activating the “Asset Management: Generate assets” cron.</li>
|
||||
</ul>
|
||||
<p>These options are compatibles each other.</p>
|
||||
<p>The module contains a large number of functional enhancements compared to
|
||||
the standard account_asset module from Odoo.</p>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
<div class="contents local topic" id="contents">
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
|
||||
<li><a class="reference internal" href="#changelog" id="toc-entry-2">Changelog</a><ul>
|
||||
<li><a class="reference internal" href="#section-1" id="toc-entry-3">14.0.1.0.0 (2021-01-08)</a></li>
|
||||
<li><a class="reference internal" href="#section-2" id="toc-entry-4">13.0.3.0.0 (2021-07-06)</a></li>
|
||||
<li><a class="reference internal" href="#section-3" id="toc-entry-5">13.0.2.0.0 (2021-02-19)</a></li>
|
||||
<li><a class="reference internal" href="#section-4" id="toc-entry-6">13.0.1.0.0 (2019-10-21)</a></li>
|
||||
<li><a class="reference internal" href="#section-5" id="toc-entry-7">12.0.2.1.0 (2019-10-21)</a></li>
|
||||
<li><a class="reference internal" href="#section-6" id="toc-entry-8">12.0.1.0.0 (2019-01-13)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-9">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="toc-entry-10">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="toc-entry-11">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="toc-entry-12">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="toc-entry-13">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="usage">
|
||||
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
|
||||
<p>The module in NOT compatible with the standard account_asset module.</p>
|
||||
</div>
|
||||
<div class="section" id="changelog">
|
||||
<h1><a class="toc-backref" href="#toc-entry-2">Changelog</a></h1>
|
||||
<div class="section" id="section-1">
|
||||
<h2><a class="toc-backref" href="#toc-entry-3">14.0.1.0.0 (2021-01-08)</a></h2>
|
||||
<blockquote>
|
||||
<ul class="simple">
|
||||
<li>[BREAKING] Removed all functionality associated with <cite>account.fiscal.year</cite></li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</div>
|
||||
<div class="section" id="section-2">
|
||||
<h2><a class="toc-backref" href="#toc-entry-4">13.0.3.0.0 (2021-07-06)</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Allow to reverse the posting of a depreciation line instead of deleting the
|
||||
journal entry.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-3">
|
||||
<h2><a class="toc-backref" href="#toc-entry-5">13.0.2.0.0 (2021-02-19)</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Add support for multi-company</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-4">
|
||||
<h2><a class="toc-backref" href="#toc-entry-6">13.0.1.0.0 (2019-10-21)</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Python code and views were adapted to be compatible with v13.</li>
|
||||
<li>When assets are created through accounting journal items,
|
||||
they are created when the journal items is posted.</li>
|
||||
<li>When a Bill Invoice is created or modified, at the time it is saved,
|
||||
for each line that has an Asset profile and Quantity ‘N’
|
||||
greater than 1, it will be replaced by ‘N’ lines identical to it but
|
||||
with quantity 1. This was done to maintain the same behavior as in
|
||||
the previous version, in which for each asset created there is a
|
||||
Journal Item. In addition, this solution does not change the data
|
||||
model which does not cause migration scripts.</li>
|
||||
<li>The configuration option was removed so the only function of that is to
|
||||
allow the module to be uninstalled by unchecking that configuration option.</li>
|
||||
<li>Tests were adapted.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-5">
|
||||
<h2><a class="toc-backref" href="#toc-entry-7">12.0.2.1.0 (2019-10-21)</a></h2>
|
||||
<ul class="simple">
|
||||
<li>[IMP] Add option to calculate depreciation table by days</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="section-6">
|
||||
<h2><a class="toc-backref" href="#toc-entry-8">12.0.1.0.0 (2019-01-13)</a></h2>
|
||||
<ul class="simple">
|
||||
<li>[BREAKING] account.asset: parent_path has replaced parent_left & parent_right (TODO: migration script)</li>
|
||||
<li>[BREAKING] account.asset.recompute.trigger: depends on date_range.py (TODO: re-implement in account_fiscal_year.py)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h1><a class="toc-backref" href="#toc-entry-9">Bug Tracker</a></h1>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/account-financial-tools/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_asset_management%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
<h1><a class="toc-backref" href="#toc-entry-10">Credits</a></h1>
|
||||
<div class="section" id="authors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-11">Authors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Noviat</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-12">Contributors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>OpenERP SA</li>
|
||||
<li>Luc De Meyer (Noviat)</li>
|
||||
<li>Frédéric Clementi (camptocamp)</li>
|
||||
<li>Florian Dacosta (Akretion)</li>
|
||||
<li>Stéphane Bidoul (Acsone)</li>
|
||||
<li>Adrien Peiffer (Acsone)</li>
|
||||
<li>Akim Juillerat <<a class="reference external" href="mailto:akim.juillerat@camptocamp.com">akim.juillerat@camptocamp.com</a>></li>
|
||||
<li>Henrik Norlin (Apps2GROW)</li>
|
||||
<li>Maxence Groine <<a class="reference external" href="mailto:mgroine@fiefmanage.ch">mgroine@fiefmanage.ch</a>></li>
|
||||
<li>Kitti Upariphutthiphong <<a class="reference external" href="mailto:kittiu@ecosoft.co.th">kittiu@ecosoft.co.th</a>></li>
|
||||
<li>Saran Lim. <<a class="reference external" href="mailto:saranl@ecosoft.co.th">saranl@ecosoft.co.th</a>></li>
|
||||
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
|
||||
<li>Ernesto Tejeda</li>
|
||||
<li>Pedro M. Baeza</li>
|
||||
<li>João Marques</li>
|
||||
<li>Víctor Martínez</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference external" href="https://www.forgeflow.com">ForgeFlow</a>:<ul>
|
||||
<li>Jordi Ballester <<a class="reference external" href="mailto:jordi.ballester@forgeflow.com">jordi.ballester@forgeflow.com</a>></li>
|
||||
<li>Miquel Raïch <<a class="reference external" href="mailto:miquel.raich@forgeflow.com">miquel.raich@forgeflow.com</a>></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference external" href="https://www.sygel.es">Sygel</a>:<ul>
|
||||
<li>Manuel Regidor <<a class="reference external" href="mailto:manuel.regidor@sygel.es">manuel.regidor@sygel.es</a>></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference external" href="https://www.moduon.team">Moduon</a>:<ul>
|
||||
<li>Eduardo de Miguel</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2><a class="toc-backref" href="#toc-entry-13">Maintainers</a></h2>
|
||||
<p>This module is maintained by the OCA.</p>
|
||||
<a class="reference external image-reference" href="https://odoo-community.org">
|
||||
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
|
||||
</a>
|
||||
<p>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.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/16.0/account_asset_management">OCA/account-financial-tools</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue