Add support for depreciable assets
The analytic entries should not be created when posting the invoice for depreciable assets. Instead entries must be created when the assets are depreciated. issue5909 review26731002
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
* Add support for depreciable assets
|
||||
|
||||
Version 4.0.0 - 2016-05-02
|
||||
* Bug fixes (see mercurial logs for details)
|
||||
* Add Python3 support
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
from trytond.pool import Pool
|
||||
from .invoice import *
|
||||
from . import asset
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
InvoiceLine,
|
||||
AnalyticAccountEntry,
|
||||
asset.Asset,
|
||||
module='analytic_invoice', type_='model')
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.model import fields
|
||||
|
||||
from trytond.modules.analytic_account import AnalyticMixin
|
||||
|
||||
__all__ = ['Asset']
|
||||
|
||||
|
||||
class Asset(AnalyticMixin):
|
||||
__metaclass__ = PoolMeta
|
||||
__name__ = 'account.asset'
|
||||
|
||||
@fields.depends('supplier_invoice_line', 'analytic_accounts')
|
||||
def on_change_supplier_invoice_line(self):
|
||||
pool = Pool()
|
||||
Entry = pool.get('analytic.account.entry')
|
||||
|
||||
super(Asset, self).on_change_supplier_invoice_line()
|
||||
if self.supplier_invoice_line:
|
||||
entries = []
|
||||
for entry in self.supplier_invoice_line.analytic_accounts:
|
||||
new_entry = Entry()
|
||||
for field in Entry._fields:
|
||||
if field in {'origin', 'id'}:
|
||||
continue
|
||||
setattr(new_entry, field, getattr(entry, field))
|
||||
entries.append(new_entry)
|
||||
self.analytic_accounts = entries
|
||||
|
||||
def get_move(self, line):
|
||||
move = super(Asset, self).get_move(line)
|
||||
self.set_analytic_lines(move)
|
||||
return move
|
||||
|
||||
def get_closing_move(self, account):
|
||||
move = super(Asset, self).get_closing_move(account)
|
||||
self.set_analytic_lines(move)
|
||||
return move
|
||||
|
||||
def set_analytic_lines(self, move):
|
||||
"Fill analytic lines on lines with expense account"
|
||||
if self.analytic_accounts:
|
||||
for line in move.lines:
|
||||
if line.account != self.product.account_expense_used:
|
||||
continue
|
||||
analytic_lines = []
|
||||
for entry in self.analytic_accounts:
|
||||
analytic_lines.extend(
|
||||
entry.get_analytic_lines(line, move.date))
|
||||
line.analytic_lines = analytic_lines
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data depends="account_asset">
|
||||
<record model="ir.ui.view" id="asset_view_form">
|
||||
<field name="model">account.asset</field>
|
||||
<field name="inherit" ref="account_asset.asset_view_form"/>
|
||||
<field name="name">asset_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
+32
-4
@@ -10,6 +10,7 @@ __all__ = ['InvoiceLine', 'AnalyticAccountEntry']
|
||||
|
||||
|
||||
class InvoiceLine(AnalyticMixin):
|
||||
__metaclass__ = PoolMeta
|
||||
__name__ = 'account.invoice.line'
|
||||
|
||||
@classmethod
|
||||
@@ -37,7 +38,14 @@ class InvoiceLine(AnalyticMixin):
|
||||
|
||||
def get_move_lines(self):
|
||||
lines = super(InvoiceLine, self).get_move_lines()
|
||||
if self.analytic_accounts:
|
||||
if self.invoice and self.invoice.type:
|
||||
type_ = self.invoice.type
|
||||
else:
|
||||
type_ = self.invoice_type
|
||||
asset_depreciable = (self.product and type_ == 'in'
|
||||
and self.product.type == 'assets'
|
||||
and getattr(self.product, 'depreciable', False))
|
||||
if self.analytic_accounts and not asset_depreciable:
|
||||
date = self.invoice.accounting_date or self.invoice.invoice_date
|
||||
for line in lines:
|
||||
analytic_lines = []
|
||||
@@ -54,24 +62,44 @@ class AnalyticAccountEntry:
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
pool = Pool()
|
||||
origins = super(AnalyticAccountEntry, cls)._get_origin()
|
||||
return origins + ['account.invoice.line']
|
||||
origins.append('account.invoice.line')
|
||||
try:
|
||||
pool.get('account.asset')
|
||||
origins.append('account.asset')
|
||||
except KeyError:
|
||||
pass
|
||||
return origins
|
||||
|
||||
@fields.depends('origin')
|
||||
def on_change_with_company(self, name=None):
|
||||
pool = Pool()
|
||||
InvoiceLine = pool.get('account.invoice.line')
|
||||
try:
|
||||
Asset = pool.get('account.asset')
|
||||
except KeyError:
|
||||
Asset = None
|
||||
company = super(AnalyticAccountEntry, self).on_change_with_company(
|
||||
name)
|
||||
if isinstance(self.origin, InvoiceLine):
|
||||
if (isinstance(self.origin, InvoiceLine)
|
||||
or (Asset and isinstance(self.origin, Asset))):
|
||||
company = self.origin.company.id
|
||||
return company
|
||||
|
||||
@classmethod
|
||||
def search_company(cls, name, clause):
|
||||
pool = Pool()
|
||||
domain = super(AnalyticAccountEntry, cls).search_company(name, clause),
|
||||
return ['OR',
|
||||
domain = ['OR',
|
||||
domain,
|
||||
(('origin.company',) + tuple(clause[1:]) +
|
||||
('account.invoice.line',)),
|
||||
]
|
||||
try:
|
||||
pool.get('account.asset')
|
||||
domain.append(
|
||||
(('origin.company',) + tuple(clause[1:]) + ('account.asset',)))
|
||||
except KeyError:
|
||||
pass
|
||||
return domain
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
=====================================
|
||||
Analytic Invoice with Assets Scenario
|
||||
=====================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime
|
||||
>>> from dateutil.relativedelta import relativedelta
|
||||
>>> from decimal import Decimal
|
||||
>>> from operator import attrgetter
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.tests.tools import install_modules
|
||||
>>> from trytond.modules.company.tests.tools import create_company, \
|
||||
... get_company
|
||||
>>> from trytond.modules.account.tests.tools import create_fiscalyear, \
|
||||
... create_chart, get_accounts
|
||||
>>> from trytond.modules.account_invoice.tests.tools import \
|
||||
... set_fiscalyear_invoice_sequences, create_payment_term
|
||||
>>> from trytond.modules.account_asset.tests.tools \
|
||||
... import add_asset_accounts
|
||||
>>> today = datetime.date.today()
|
||||
|
||||
Install analytic_invoice and account_asset::
|
||||
|
||||
>>> config = install_modules(['analytic_invoice', 'account_asset'])
|
||||
|
||||
Create company::
|
||||
|
||||
>>> _ = create_company()
|
||||
>>> company = get_company()
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(company))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Create chart of accounts::
|
||||
|
||||
>>> _ = create_chart(company)
|
||||
>>> accounts = add_asset_accounts(get_accounts(company), company)
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> asset_account = accounts['asset']
|
||||
>>> expense = accounts['expense']
|
||||
>>> depreciation_account = accounts['depreciation']
|
||||
|
||||
Create analytic accounts::
|
||||
|
||||
>>> AnalyticAccount = Model.get('analytic_account.account')
|
||||
>>> root = AnalyticAccount(type='root', name='Root')
|
||||
>>> root.save()
|
||||
>>> analytic_account = AnalyticAccount(root=root, parent=root,
|
||||
... name='Analytic')
|
||||
>>> analytic_account.save()
|
||||
|
||||
Create supplier::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
Create an asset::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Product = Model.get('product.product')
|
||||
>>> asset_product = Product()
|
||||
>>> asset_template = ProductTemplate()
|
||||
>>> asset_template.name = 'Asset'
|
||||
>>> asset_template.type = 'assets'
|
||||
>>> asset_template.default_uom = unit
|
||||
>>> asset_template.list_price = Decimal('1000')
|
||||
>>> asset_template.cost_price = Decimal('1000')
|
||||
>>> asset_template.depreciable = True
|
||||
>>> asset_template.account_expense = expense
|
||||
>>> asset_template.account_revenue = revenue
|
||||
>>> asset_template.account_asset = asset_account
|
||||
>>> asset_template.account_depreciation = depreciation_account
|
||||
>>> asset_template.depreciation_duration = Decimal(10)
|
||||
>>> asset_template.save()
|
||||
>>> asset_product.template = asset_template
|
||||
>>> asset_product.save()
|
||||
|
||||
Buy an asset::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> InvoiceLine = Model.get('account.invoice.line')
|
||||
>>> supplier_invoice = Invoice(type='in')
|
||||
>>> supplier_invoice.party = supplier
|
||||
>>> invoice_line = supplier_invoice.lines.new()
|
||||
>>> invoice_line.product = asset_product
|
||||
>>> invoice_line.quantity = 1
|
||||
>>> invoice_line.unit_price = Decimal('1000')
|
||||
>>> entry, = invoice_line.analytic_accounts
|
||||
>>> entry.account = analytic_account
|
||||
>>> supplier_invoice.invoice_date = today + relativedelta(day=1, month=1)
|
||||
>>> supplier_invoice.click('post')
|
||||
>>> supplier_invoice.state
|
||||
u'posted'
|
||||
>>> invoice_line, = supplier_invoice.lines
|
||||
>>> analytic_account.debit
|
||||
Decimal('0.00')
|
||||
>>> analytic_account.credit
|
||||
Decimal('0.00')
|
||||
|
||||
Depreciate the asset::
|
||||
|
||||
>>> Asset = Model.get('account.asset')
|
||||
>>> asset = Asset()
|
||||
>>> asset.product = asset_product
|
||||
>>> asset.supplier_invoice_line = invoice_line
|
||||
>>> asset.residual_value = Decimal(0)
|
||||
>>> asset.click('create_lines')
|
||||
>>> asset.click('run')
|
||||
|
||||
Create Moves for 1 month::
|
||||
|
||||
>>> create_moves = Wizard('account.asset.create_moves')
|
||||
>>> create_moves.form.date = (supplier_invoice.invoice_date
|
||||
... + relativedelta(months=1))
|
||||
>>> create_moves.execute('create_moves')
|
||||
>>> analytic_account.reload()
|
||||
>>> analytic_account.debit
|
||||
Decimal('100.00')
|
||||
>>> analytic_account.credit
|
||||
Decimal('0.00')
|
||||
@@ -21,4 +21,8 @@ def suite():
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
suite.addTests(doctest.DocFileSuite('scenario_analytic_invoice_asset.rst',
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
return suite
|
||||
|
||||
@@ -3,5 +3,7 @@ version=4.1.0
|
||||
depends:
|
||||
account_invoice
|
||||
analytic_account
|
||||
extras_depend:
|
||||
account_asset
|
||||
xml:
|
||||
invoice.xml
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook/page[@id='lines']" position="after">
|
||||
<page name="analytic_accounts" col="1">
|
||||
<field name="analytic_accounts"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
Reference in New Issue
Block a user