diff --git a/CHANGELOG b/CHANGELOG
index 98668f856e..1710c8bd1b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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
diff --git a/__init__.py b/__init__.py
index 9487c80123..644128c681 100644
--- a/__init__.py
+++ b/__init__.py
@@ -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')
diff --git a/asset.py b/asset.py
new file mode 100644
index 0000000000..25a5d47526
--- /dev/null
+++ b/asset.py
@@ -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
diff --git a/asset.xml b/asset.xml
new file mode 100644
index 0000000000..bdbceb6e33
--- /dev/null
+++ b/asset.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ account.asset
+
+ asset_form
+
+
+
diff --git a/invoice.py b/invoice.py
index 1b4eae120a..f75d95315c 100644
--- a/invoice.py
+++ b/invoice.py
@@ -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
diff --git a/tests/scenario_analytic_invoice_asset.rst b/tests/scenario_analytic_invoice_asset.rst
new file mode 100644
index 0000000000..bd4657d775
--- /dev/null
+++ b/tests/scenario_analytic_invoice_asset.rst
@@ -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')
diff --git a/tests/test_analytic_invoice.py b/tests/test_analytic_invoice.py
index 94d7dfc1a4..0cc8f0ecef 100644
--- a/tests/test_analytic_invoice.py
+++ b/tests/test_analytic_invoice.py
@@ -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
diff --git a/tryton.cfg b/tryton.cfg
index 0f4693a17e..01d02a466b 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -3,5 +3,7 @@ version=4.1.0
depends:
account_invoice
analytic_account
+extras_depend:
+ account_asset
xml:
invoice.xml
diff --git a/view/asset_form.xml b/view/asset_form.xml
new file mode 100644
index 0000000000..c19a11b42c
--- /dev/null
+++ b/view/asset_form.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+