diff --git a/CHANGELOG b/CHANGELOG index 9f91dd6c2d..496c2c87ac 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Add depreciated amount + Version 5.2.0 - 2019-05-06 * Bug fixes (see mercurial logs for details) * Add asset on account type diff --git a/asset.py b/asset.py index fa599e0c12..0459560338 100644 --- a/asset.py +++ b/asset.py @@ -101,12 +101,33 @@ class Asset(Workflow, ModelSQL, ModelView): 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, depends=['currency_digits', 'state'], - required=True) - residual_value = fields.Numeric('Residual Value', + required=True, + help="The value of the asset when purchased.") + depreciated_amount = fields.Numeric("Depreciated Amount", + digits=(16, Eval('currency_digits', 2)), + domain=[ + ('depreciated_amount', '<=', Eval('value')), + ], states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, - depends=['currency_digits', 'state'], + depends=['currency_digits', 'value', 'state'], + required=True, + help="The amount already depreciated at the start date.") + depreciating_value = fields.Function(fields.Numeric( + "Depreciating Value", + digits=(16, Eval('currency_digits', 2)), + depends=['currency_digits'], + help="The value of the asset at the start date."), + 'on_change_with_depreciating_value') + residual_value = fields.Numeric('Residual Value', + domain=[ + ('residual_value', '<=', Eval('depreciating_value')), + ], + states={ + 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), + }, + depends=['currency_digits', 'depreciating_value', 'state'], required=True, digits=(16, Eval('currency_digits', 2))) purchase_date = fields.Date('Purchase Date', states={ @@ -227,6 +248,14 @@ class Asset(Workflow, ModelSQL, ModelView): def default_depreciation_method(): return 'linear' + @classmethod + def default_depreciated_amount(cls): + return Decimal(0) + + @classmethod + def default_residual_value(cls): + return Decimal(0) + @staticmethod def default_start_date(): return Pool().get('ir.date').today() @@ -245,6 +274,13 @@ class Asset(Workflow, ModelSQL, ModelView): return journals[0].id return None + @fields.depends('value', 'depreciated_amount') + def on_change_with_depreciating_value(self, name=None): + if self.value is not None and self.depreciated_amount is not None: + return self.value - self.depreciated_amount + else: + return Decimal(0) + @fields.depends('company') def on_change_with_currency_digits(self, name=None): if self.company: @@ -320,9 +356,9 @@ class Asset(Workflow, ModelSQL, ModelView): return result def get_depreciated_amount(self): - lines = [line.accumulated_depreciation for line in self.lines + lines = [line.depreciation for line in self.lines if line.move and line.move.state == 'posted'] - return max(lines) if lines else 0 + return sum(lines, Decimal(0)) def compute_move_dates(self): """ @@ -352,12 +388,10 @@ class Asset(Workflow, ModelSQL, ModelView): dates.append(self.end_date) return dates - def compute_depreciation(self, date, dates): + def compute_depreciation(self, amount, date, dates): """ Returns the depreciation amount for an asset on a certain date. """ - amount = (self.value - self.get_depreciated_amount() - - self.residual_value) if self.depreciation_method == 'linear': start_date = max([self.start_date - relativedelta.relativedelta(days=1)] @@ -392,14 +426,17 @@ class Asset(Workflow, ModelSQL, ModelView): Line = Pool().get('account.asset.line') amounts = {} dates = self.compute_move_dates() - amount = (self.value - self.get_depreciated_amount() + depreciated_amount = self.get_depreciated_amount() + amount = (self.depreciating_value + - depreciated_amount - self.residual_value) if amount <= 0: return amounts - residual_value, acc_depreciation = amount, Decimal(0) + residual_value, acc_depreciation = ( + amount, depreciated_amount + self.depreciated_amount) asset_line = None for date in dates: - depreciation = self.compute_depreciation(date, dates) + depreciation = self.compute_depreciation(amount, date, dates) amounts[date] = asset_line = Line( acquired_value=self.value, depreciable_basis=amount, @@ -407,22 +444,20 @@ class Asset(Workflow, ModelSQL, ModelView): if depreciation > residual_value: asset_line.depreciation = residual_value asset_line.accumulated_depreciation = ( - self.get_depreciated_amount() - + acc_depreciation + residual_value) + acc_depreciation + residual_value) break else: residual_value -= depreciation acc_depreciation += depreciation asset_line.depreciation = depreciation - asset_line.accumulated_depreciation = ( - self.get_depreciated_amount() + acc_depreciation) + asset_line.accumulated_depreciation = acc_depreciation else: if residual_value > 0 and asset_line is not None: asset_line.depreciation += residual_value asset_line.accumulated_depreciation += residual_value for asset_line in amounts.values(): - asset_line.actual_value = (self.value - - asset_line.accumulated_depreciation) + asset_line.actual_value = (self.value + - asset_line.accumulated_depreciation) return amounts @classmethod @@ -536,7 +571,7 @@ class Asset(Workflow, ModelSQL, ModelView): account=account_asset, ) depreciation_line = MoveLine( - debit=self.get_depreciated_amount(), + debit=self.get_depreciated_amount() + self.depreciated_amount, credit=0, account=self.product.account_depreciation_used, ) diff --git a/doc/index.rst b/doc/index.rst index e6ac3952ae..a8c9ed03b0 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,7 +10,7 @@ An Asset defines how an asset is depreciated. It is mainly defined by: - Product (of type "Assets"). - Journal. -- Value and Residual Value. +- Value, Depreciated Amount and Residual Value. - Start and End Date. - Depreciation Method: - Linear diff --git a/tests/scenario_account_asset_depreciated.rst b/tests/scenario_account_asset_depreciated.rst new file mode 100644 index 0000000000..41e1d7b7bd --- /dev/null +++ b/tests/scenario_account_asset_depreciated.rst @@ -0,0 +1,143 @@ +================================== +Account Asset Depreciated Scenario +================================== + +Imports:: + + >>> import datetime + >>> from dateutil.relativedelta import relativedelta + >>> from decimal import Decimal + >>> from proteus import Model, Wizard + >>> from trytond.tests.tools import activate_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 account_asset:: + + >>> config = activate_modules('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 account category:: + + >>> ProductCategory = Model.get('product.category') + >>> account_category = ProductCategory(name="Account Category") + >>> account_category.accounting = True + >>> account_category.account_expense = expense + >>> account_category.account_revenue = revenue + >>> account_category.account_asset = asset_account + >>> account_category.account_depreciation = depreciation_account + >>> account_category.save() + +Create an asset:: + + >>> ProductUom = Model.get('product.uom') + >>> unit, = ProductUom.find([('name', '=', 'Unit')]) + >>> ProductTemplate = Model.get('product.template') + >>> asset_template = ProductTemplate() + >>> asset_template.name = 'Asset' + >>> asset_template.type = 'assets' + >>> asset_template.default_uom = unit + >>> asset_template.list_price = Decimal('1000') + >>> asset_template.account_category = account_category + >>> asset_template.depreciable = True + >>> asset_template.depreciation_duration = 24 + >>> asset_template.save() + >>> asset_product, = asset_template.products + +Depreciate the asset:: + + >>> Asset = Model.get('account.asset') + >>> asset = Asset() + >>> asset.product = asset_product + >>> asset.value = Decimal('1500.00') + >>> asset.depreciated_amount = Decimal('500.00') + >>> asset.start_date = today + relativedelta(day=1, month=1) + >>> asset.purchase_date = asset.start_date + >>> asset.end_date = (asset.start_date + + ... relativedelta(years=2, days=-1)) + >>> asset.quantity = 1 + >>> asset.residual_value = Decimal('100') + >>> asset.click('create_lines') + >>> len(asset.lines) + 24 + >>> [l.depreciation for l in asset.lines] == [Decimal('37.5')] * 24 + True + >>> [l.acquired_value for l in asset.lines] == [Decimal('1500.00')] * 24 + True + >>> [l.depreciable_basis for l in asset.lines] == [Decimal('900.00')] * 24 + True + >>> asset.lines[0].actual_value + Decimal('962.50') + >>> asset.lines[0].accumulated_depreciation + Decimal('537.50') + >>> asset.lines[11].actual_value + Decimal('550.00') + >>> asset.lines[11].accumulated_depreciation + Decimal('950.00') + >>> asset.lines[-1].actual_value + Decimal('100.00') + >>> asset.lines[-1].accumulated_depreciation + Decimal('1400.00') + >>> asset.click('run') + +Create Moves for 3 months:: + + >>> create_moves = Wizard('account.asset.create_moves') + >>> create_moves.form.date = (asset.start_date + ... + relativedelta(months=3)) + >>> create_moves.execute('create_moves') + >>> depreciation_account.reload() + >>> depreciation_account.debit + Decimal('0.00') + >>> depreciation_account.credit + Decimal('112.50') + >>> expense.reload() + >>> expense.debit + Decimal('112.50') + >>> expense.credit + Decimal('0.00') + +Close the asset:: + + >>> asset.click('close') + >>> asset_account.reload() + >>> asset_account.debit + Decimal('0.00') + >>> asset_account.credit + Decimal('1500.00') + >>> depreciation_account.reload() + >>> depreciation_account.debit + Decimal('612.50') + >>> depreciation_account.credit + Decimal('112.50') + >>> revenue.reload() + >>> revenue.debit + Decimal('887.50') + >>> revenue.credit + Decimal('0.00') diff --git a/tests/test_account_asset.py b/tests/test_account_asset.py index f81e7d1e39..97ea9f9d5e 100644 --- a/tests/test_account_asset.py +++ b/tests/test_account_asset.py @@ -22,6 +22,11 @@ def suite(): tearDown=doctest_teardown, encoding='utf-8', checker=doctest_checker, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) + suite.addTests(doctest.DocFileSuite( + 'scenario_account_asset_depreciated.rst', + tearDown=doctest_teardown, encoding='utf-8', + checker=doctest_checker, + optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) suite.addTests(doctest.DocFileSuite('scenario_purchase_asset.rst', tearDown=doctest_teardown, encoding='utf-8', checker=doctest_checker, diff --git a/view/asset_form.xml b/view/asset_form.xml index 3c29612dd9..b14b2b68d8 100644 --- a/view/asset_form.xml +++ b/view/asset_form.xml @@ -4,14 +4,14 @@