Merge branch 'master' of https://github.com/odoo/odoo
[odoo/odoo.git] / addons / account_asset / account_asset.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import time
23 from datetime import datetime
24 from dateutil.relativedelta import relativedelta
25
26 from openerp.osv import fields, osv
27 import openerp.addons.decimal_precision as dp
28 from openerp.tools.translate import _
29
30 class account_asset_category(osv.osv):
31     _name = 'account.asset.category'
32     _description = 'Asset category'
33
34     _columns = {
35         'name': fields.char('Name', required=True, select=1),
36         'note': fields.text('Note'),
37         'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
38         'account_asset_id': fields.many2one('account.account', 'Asset Account', required=True, domain=[('type','=','other')]),
39         'account_depreciation_id': fields.many2one('account.account', 'Depreciation Account', required=True, domain=[('type','=','other')]),
40         'account_expense_depreciation_id': fields.many2one('account.account', 'Depr. Expense Account', required=True, domain=[('type','=','other')]),
41         'journal_id': fields.many2one('account.journal', 'Journal', required=True),
42         'company_id': fields.many2one('res.company', 'Company', required=True),
43         'method': fields.selection([('linear','Linear: Computed on basis of Gross Value / Number of Depreciations'),('degressive','Degressive: Computed on basis of Residual Value * Degressive Factor')], 'Computation Method', required=True, help="Choose the method to use to compute the amount of depreciation lines.\n"\
44             "  * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n" \
45             "  * Degressive: Calculated on basis of: Residual Value * Degressive Factor"),
46         'method_number': fields.integer('Number of Depreciations', help="The number of depreciations needed to depreciate your asset"),
47         'method_period': fields.integer('Period Length', help="State here the time between 2 depreciations, in months", required=True),
48         'method_progress_factor': fields.float('Degressive Factor'),
49         'method_time': fields.selection([('number','Number of Depreciations'),('end','Ending Date')], 'Time Method', required=True,
50                                   help="Choose the method to use to compute the dates and number of depreciation lines.\n"\
51                                        "  * Number of Depreciations: Fix the number of depreciation lines and the time between 2 depreciations.\n" \
52                                        "  * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond."),
53         'method_end': fields.date('Ending date'),
54         'prorata':fields.boolean('Prorata Temporis', help='Indicates that the first depreciation entry for this asset have to be done from the purchase date instead of the first January'),
55         'open_asset': fields.boolean('Skip Draft State', help="Check this if you want to automatically confirm the assets of this category when created by invoices."),
56     }
57
58     _defaults = {
59         'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.category', context=context),
60         'method': 'linear',
61         'method_number': 5,
62         'method_time': 'number',
63         'method_period': 12,
64         'method_progress_factor': 0.3,
65     }
66
67     def onchange_account_asset(self, cr, uid, ids, account_asset_id, context=None):
68         res = {'value':{}}
69         if account_asset_id:
70            res['value'] = {'account_depreciation_id': account_asset_id}
71         return res
72
73
74 class account_asset_asset(osv.osv):
75     _name = 'account.asset.asset'
76     _description = 'Asset'
77
78     def unlink(self, cr, uid, ids, context=None):
79         for asset in self.browse(cr, uid, ids, context=context):
80             if asset.account_move_line_ids: 
81                 raise osv.except_osv(_('Error!'), _('You cannot delete an asset that contains posted depreciation lines.'))
82         return super(account_asset_asset, self).unlink(cr, uid, ids, context=context)
83
84     def _get_period(self, cr, uid, context=None):
85         periods = self.pool.get('account.period').find(cr, uid, context=context)
86         if periods:
87             return periods[0]
88         else:
89             return False
90
91     def _get_last_depreciation_date(self, cr, uid, ids, context=None):
92         """
93         @param id: ids of a account.asset.asset objects
94         @return: Returns a dictionary of the effective dates of the last depreciation entry made for given asset ids. If there isn't any, return the purchase date of this asset
95         """
96         cr.execute("""
97             SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
98             FROM account_asset_asset a
99             LEFT JOIN account_move_line l ON (l.asset_id = a.id)
100             WHERE a.id IN %s
101             GROUP BY a.id, a.purchase_date """, (tuple(ids),))
102         return dict(cr.fetchall())
103
104     def _compute_board_amount(self, cr, uid, asset, i, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date, context=None):
105         #by default amount = 0
106         amount = 0
107         if i == undone_dotation_number:
108             amount = residual_amount
109         else:
110             if asset.method == 'linear':
111                 amount = amount_to_depr / (undone_dotation_number - len(posted_depreciation_line_ids))
112                 if asset.prorata:
113                     amount = amount_to_depr / asset.method_number
114                     days = total_days - float(depreciation_date.strftime('%j'))
115                     if i == 1:
116                         amount = (amount_to_depr / asset.method_number) / total_days * days
117                     elif i == undone_dotation_number:
118                         amount = (amount_to_depr / asset.method_number) / total_days * (total_days - days)
119             elif asset.method == 'degressive':
120                 amount = residual_amount * asset.method_progress_factor
121                 if asset.prorata:
122                     days = total_days - float(depreciation_date.strftime('%j'))
123                     if i == 1:
124                         amount = (residual_amount * asset.method_progress_factor) / total_days * days
125                     elif i == undone_dotation_number:
126                         amount = (residual_amount * asset.method_progress_factor) / total_days * (total_days - days)
127         return amount
128
129     def _compute_board_undone_dotation_nb(self, cr, uid, asset, depreciation_date, total_days, context=None):
130         undone_dotation_number = asset.method_number
131         if asset.method_time == 'end':
132             end_date = datetime.strptime(asset.method_end, '%Y-%m-%d')
133             undone_dotation_number = 0
134             while depreciation_date <= end_date:
135                 depreciation_date = (datetime(depreciation_date.year, depreciation_date.month, depreciation_date.day) + relativedelta(months=+asset.method_period))
136                 undone_dotation_number += 1
137         if asset.prorata:
138             undone_dotation_number += 1
139         return undone_dotation_number
140
141     def compute_depreciation_board(self, cr, uid, ids, context=None):
142         depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
143         currency_obj = self.pool.get('res.currency')
144         for asset in self.browse(cr, uid, ids, context=context):
145             if asset.value_residual == 0.0:
146                 continue
147             posted_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_check', '=', True)],order='depreciation_date desc')
148             old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
149             if old_depreciation_line_ids:
150                 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
151
152             amount_to_depr = residual_amount = asset.value_residual
153             if asset.prorata:
154                 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
155             else:
156                 # depreciation_date = 1st January of purchase year
157                 purchase_date = datetime.strptime(asset.purchase_date, '%Y-%m-%d')
158                 #if we already have some previous validated entries, starting date isn't 1st January but last entry + method period
159                 if (len(posted_depreciation_line_ids)>0):
160                     last_depreciation_date = datetime.strptime(depreciation_lin_obj.browse(cr,uid,posted_depreciation_line_ids[0],context=context).depreciation_date, '%Y-%m-%d')
161                     depreciation_date = (last_depreciation_date+relativedelta(months=+asset.method_period))
162                 else:
163                     depreciation_date = datetime(purchase_date.year, 1, 1)
164             day = depreciation_date.day
165             month = depreciation_date.month
166             year = depreciation_date.year
167             total_days = (year % 4) and 365 or 366
168
169             undone_dotation_number = self._compute_board_undone_dotation_nb(cr, uid, asset, depreciation_date, total_days, context=context)
170             for x in range(len(posted_depreciation_line_ids), undone_dotation_number):
171                 i = x + 1
172                 amount = self._compute_board_amount(cr, uid, asset, i, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date, context=context)
173                 company_currency = asset.company_id.currency_id.id
174                 current_currency = asset.currency_id.id
175                 # compute amount into company currency
176                 amount = currency_obj.compute(cr, uid, current_currency, company_currency, amount, context=context)
177                 residual_amount -= amount
178                 vals = {
179                      'amount': amount,
180                      'asset_id': asset.id,
181                      'sequence': i,
182                      'name': str(asset.id) +'/' + str(i),
183                      'remaining_value': residual_amount,
184                      'depreciated_value': (asset.purchase_value - asset.salvage_value) - (residual_amount + amount),
185                      'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
186                 }
187                 depreciation_lin_obj.create(cr, uid, vals, context=context)
188                 # Considering Depr. Period as months
189                 depreciation_date = (datetime(year, month, day) + relativedelta(months=+asset.method_period))
190                 day = depreciation_date.day
191                 month = depreciation_date.month
192                 year = depreciation_date.year
193         return True
194
195     def validate(self, cr, uid, ids, context=None):
196         if context is None:
197             context = {}
198         return self.write(cr, uid, ids, {
199             'state':'open'
200         }, context)
201
202     def set_to_close(self, cr, uid, ids, context=None):
203         return self.write(cr, uid, ids, {'state': 'close'}, context=context)
204
205     def set_to_draft(self, cr, uid, ids, context=None):
206         return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
207
208     def _amount_residual(self, cr, uid, ids, name, args, context=None):
209         cr.execute("""SELECT
210                 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
211             FROM
212                 account_move_line l
213             WHERE
214                 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
215         res=dict(cr.fetchall())
216         for asset in self.browse(cr, uid, ids, context):
217             res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0) - asset.salvage_value
218         for id in ids:
219             res.setdefault(id, 0.0)
220         return res
221
222     def onchange_company_id(self, cr, uid, ids, company_id=False, context=None):
223         val = {}
224         if company_id:
225             company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
226             if company.currency_id.company_id and company.currency_id.company_id.id != company_id:
227                 val['currency_id'] = False
228             else:
229                 val['currency_id'] = company.currency_id.id
230         return {'value': val}
231     
232     def onchange_purchase_salvage_value(self, cr, uid, ids, purchase_value, salvage_value, context=None):
233         val = {}
234         for asset in self.browse(cr, uid, ids, context=context):
235             if purchase_value:
236                 val['value_residual'] = purchase_value - salvage_value
237             if salvage_value:
238                 val['value_residual'] = purchase_value - salvage_value
239         return {'value': val}    
240     def _entry_count(self, cr, uid, ids, field_name, arg, context=None):
241         MoveLine = self.pool('account.move.line')
242         return {
243             asset_id: MoveLine.search_count(cr, uid, [('asset_id', '=', asset_id)], context=context)
244             for asset_id in ids
245         }
246     _columns = {
247         'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
248         'entry_count': fields.function(_entry_count, string='# Asset Entries', type='integer'),
249         'name': fields.char('Asset Name', required=True, readonly=True, states={'draft':[('readonly',False)]}),
250         'code': fields.char('Reference', size=32, readonly=True, states={'draft':[('readonly',False)]}),
251         'purchase_value': fields.float('Gross Value', required=True, readonly=True, states={'draft':[('readonly',False)]}),
252         'currency_id': fields.many2one('res.currency','Currency',required=True, readonly=True, states={'draft':[('readonly',False)]}),
253         'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),
254         'note': fields.text('Note'),
255         'category_id': fields.many2one('account.asset.category', 'Asset Category', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
256         'parent_id': fields.many2one('account.asset.asset', 'Parent Asset', readonly=True, states={'draft':[('readonly',False)]}),
257         'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets', copy=True),
258         'purchase_date': fields.date('Purchase Date', required=True, readonly=True, states={'draft':[('readonly',False)]}),
259         'state': fields.selection([('draft','Draft'),('open','Running'),('close','Close')], 'Status', required=True, copy=False,
260                                   help="When an asset is created, the status is 'Draft'.\n" \
261                                        "If the asset is confirmed, the status goes in 'Running' and the depreciation lines can be posted in the accounting.\n" \
262                                        "You can manually close an asset when the depreciation is over. If the last line of depreciation is posted, the asset automatically goes in that status."),
263         'active': fields.boolean('Active'),
264         'partner_id': fields.many2one('res.partner', 'Partner', readonly=True, states={'draft':[('readonly',False)]}),
265         'method': fields.selection([('linear','Linear: Computed on basis of Gross Value / Number of Depreciations'),('degressive','Degressive: Computed on basis of Residual Value * Degressive Factor')], 'Computation Method', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="Choose the method to use to compute the amount of depreciation lines.\n"\
266             "  * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n" \
267             "  * Degressive: Calculated on basis of: Residual Value * Degressive Factor"),
268         'method_number': fields.integer('Number of Depreciations', readonly=True, states={'draft':[('readonly',False)]}, help="The number of depreciations needed to depreciate your asset"),
269         'method_period': fields.integer('Number of Months in a Period', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The amount of time between two depreciations, in months"),
270         'method_end': fields.date('Ending Date', readonly=True, states={'draft':[('readonly',False)]}),
271         'method_progress_factor': fields.float('Degressive Factor', readonly=True, states={'draft':[('readonly',False)]}),
272         'value_residual': fields.function(_amount_residual, method=True, digits_compute=dp.get_precision('Account'), string='Residual Value'),
273         'method_time': fields.selection([('number','Number of Depreciations'),('end','Ending Date')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]},
274                                   help="Choose the method to use to compute the dates and number of depreciation lines.\n"\
275                                        "  * Number of Depreciations: Fix the number of depreciation lines and the time between 2 depreciations.\n" \
276                                        "  * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond."),
277         'prorata':fields.boolean('Prorata Temporis', readonly=True, states={'draft':[('readonly',False)]}, help='Indicates that the first depreciation entry for this asset have to be done from the purchase date instead of the first January'),
278         'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
279         'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True, states={'draft':[('readonly',False)],'open':[('readonly',False)]}),
280         'salvage_value': fields.float('Salvage Value', digits_compute=dp.get_precision('Account'), help="It is the amount you plan to have that you cannot depreciate.", readonly=True, states={'draft':[('readonly',False)]}),
281     }
282     _defaults = {
283         'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').next_by_code(cr, uid, 'account.asset.code'),
284         'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
285         'active': True,
286         'state': 'draft',
287         'method': 'linear',
288         'method_number': 5,
289         'method_time': 'number',
290         'method_period': 12,
291         'method_progress_factor': 0.3,
292         'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
293         'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
294     }
295
296     def _check_recursion(self, cr, uid, ids, context=None, parent=None):
297         return super(account_asset_asset, self)._check_recursion(cr, uid, ids, context=context, parent=parent)
298
299     def _check_prorata(self, cr, uid, ids, context=None):
300         for asset in self.browse(cr, uid, ids, context=context):
301             if asset.prorata and asset.method_time != 'number':
302                 return False
303         return True
304
305     _constraints = [
306         (_check_recursion, 'Error ! You cannot create recursive assets.', ['parent_id']),
307         (_check_prorata, 'Prorata temporis can be applied only for time method "number of depreciations".', ['prorata']),
308     ]
309
310     def onchange_category_id(self, cr, uid, ids, category_id, context=None):
311         res = {'value':{}}
312         asset_categ_obj = self.pool.get('account.asset.category')
313         if category_id:
314             category_obj = asset_categ_obj.browse(cr, uid, category_id, context=context)
315             res['value'] = {
316                             'method': category_obj.method,
317                             'method_number': category_obj.method_number,
318                             'method_time': category_obj.method_time,
319                             'method_period': category_obj.method_period,
320                             'method_progress_factor': category_obj.method_progress_factor,
321                             'method_end': category_obj.method_end,
322                             'prorata': category_obj.prorata,
323             }
324         return res
325
326     def onchange_method_time(self, cr, uid, ids, method_time='number', context=None):
327         res = {'value': {}}
328         if method_time != 'number':
329             res['value'] = {'prorata': False}
330         return res
331
332     def _compute_entries(self, cr, uid, ids, period_id, context=None):
333         result = []
334         period_obj = self.pool.get('account.period')
335         depreciation_obj = self.pool.get('account.asset.depreciation.line')
336         period = period_obj.browse(cr, uid, period_id, context=context)
337         depreciation_ids = depreciation_obj.search(cr, uid, [('asset_id', 'in', ids), ('depreciation_date', '<=', period.date_stop), ('depreciation_date', '>=', period.date_start), ('move_check', '=', False)], context=context)
338         context = dict(context or {}, depreciation_date=period.date_stop)
339         return depreciation_obj.create_move(cr, uid, depreciation_ids, context=context)
340
341     def create(self, cr, uid, vals, context=None):
342         asset_id = super(account_asset_asset, self).create(cr, uid, vals, context=context)
343         self.compute_depreciation_board(cr, uid, [asset_id], context=context)
344         return asset_id
345     
346     def open_entries(self, cr, uid, ids, context=None):
347         context = dict(context or {}, search_default_asset_id=ids, default_asset_id=ids)
348         return {
349             'name': _('Journal Items'),
350             'view_type': 'form',
351             'view_mode': 'tree,form',
352             'res_model': 'account.move.line',
353             'view_id': False,
354             'type': 'ir.actions.act_window',
355             'context': context,
356         }
357
358
359 class account_asset_depreciation_line(osv.osv):
360     _name = 'account.asset.depreciation.line'
361     _description = 'Asset depreciation line'
362
363     def _get_move_check(self, cr, uid, ids, name, args, context=None):
364         res = {}
365         for line in self.browse(cr, uid, ids, context=context):
366             res[line.id] = bool(line.move_id)
367         return res
368
369     _columns = {
370         'name': fields.char('Depreciation Name', required=True, select=1),
371         'sequence': fields.integer('Sequence', required=True),
372         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, ondelete='cascade'),
373         'parent_state': fields.related('asset_id', 'state', type='char', string='State of Asset'),
374         'amount': fields.float('Current Depreciation', digits_compute=dp.get_precision('Account'), required=True),
375         'remaining_value': fields.float('Next Period Depreciation', digits_compute=dp.get_precision('Account'),required=True),
376         'depreciated_value': fields.float('Amount Already Depreciated', required=True),
377         'depreciation_date': fields.date('Depreciation Date', select=1),
378         'move_id': fields.many2one('account.move', 'Depreciation Entry'),
379         'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Posted', store=True)
380     }
381
382     def create_move(self, cr, uid, ids, context=None):
383         context = dict(context or {})
384         can_close = False
385         asset_obj = self.pool.get('account.asset.asset')
386         period_obj = self.pool.get('account.period')
387         move_obj = self.pool.get('account.move')
388         move_line_obj = self.pool.get('account.move.line')
389         currency_obj = self.pool.get('res.currency')
390         created_move_ids = []
391         asset_ids = []
392         for line in self.browse(cr, uid, ids, context=context):
393             depreciation_date = context.get('depreciation_date') or line.depreciation_date or time.strftime('%Y-%m-%d')
394             period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
395             company_currency = line.asset_id.company_id.currency_id.id
396             current_currency = line.asset_id.currency_id.id
397             context.update({'date': depreciation_date})
398             amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
399             sign = (line.asset_id.category_id.journal_id.type == 'purchase' and 1) or -1
400             asset_name = line.asset_id.name
401             reference = line.name
402             move_vals = {
403                 'name': asset_name,
404                 'date': depreciation_date,
405                 'ref': reference,
406                 'period_id': period_ids and period_ids[0] or False,
407                 'journal_id': line.asset_id.category_id.journal_id.id,
408                 }
409             move_id = move_obj.create(cr, uid, move_vals, context=context)
410             journal_id = line.asset_id.category_id.journal_id.id
411             partner_id = line.asset_id.partner_id.id
412             move_line_obj.create(cr, uid, {
413                 'name': asset_name,
414                 'ref': reference,
415                 'move_id': move_id,
416                 'account_id': line.asset_id.category_id.account_depreciation_id.id,
417                 'debit': 0.0,
418                 'credit': amount,
419                 'period_id': period_ids and period_ids[0] or False,
420                 'journal_id': journal_id,
421                 'partner_id': partner_id,
422                 'currency_id': company_currency != current_currency and  current_currency or False,
423                 'amount_currency': company_currency != current_currency and - sign * line.amount or 0.0,
424                 'date': depreciation_date,
425             })
426             move_line_obj.create(cr, uid, {
427                 'name': asset_name,
428                 'ref': reference,
429                 'move_id': move_id,
430                 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
431                 'credit': 0.0,
432                 'debit': amount,
433                 'period_id': period_ids and period_ids[0] or False,
434                 'journal_id': journal_id,
435                 'partner_id': partner_id,
436                 'currency_id': company_currency != current_currency and  current_currency or False,
437                 'amount_currency': company_currency != current_currency and sign * line.amount or 0.0,
438                 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
439                 'date': depreciation_date,
440                 'asset_id': line.asset_id.id
441             })
442             self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
443             created_move_ids.append(move_id)
444             asset_ids.append(line.asset_id.id)
445         # we re-evaluate the assets to determine whether we can close them
446         for asset in asset_obj.browse(cr, uid, list(set(asset_ids)), context=context):
447             if currency_obj.is_zero(cr, uid, asset.currency_id, asset.value_residual):
448                 asset.write({'state': 'close'})
449         return created_move_ids
450
451
452 class account_move_line(osv.osv):
453     _inherit = 'account.move.line'
454     _columns = {
455         'asset_id': fields.many2one('account.asset.asset', 'Asset', ondelete="restrict"),
456     }
457
458 class account_asset_history(osv.osv):
459     _name = 'account.asset.history'
460     _description = 'Asset history'
461     _columns = {
462         'name': fields.char('History name', select=1),
463         'user_id': fields.many2one('res.users', 'User', required=True),
464         'date': fields.date('Date', required=True),
465         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
466         'method_time': fields.selection([('number','Number of Depreciations'),('end','Ending Date')], 'Time Method', required=True,
467                                   help="The method to use to compute the dates and number of depreciation lines.\n"\
468                                        "Number of Depreciations: Fix the number of depreciation lines and the time between 2 depreciations.\n" \
469                                        "Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond."),
470         'method_number': fields.integer('Number of Depreciations', help="The number of depreciations needed to depreciate your asset"),
471         'method_period': fields.integer('Period Length', help="Time in month between two depreciations"),
472         'method_end': fields.date('Ending date'),
473         'note': fields.text('Note'),
474     }
475     _order = 'date desc'
476     _defaults = {
477         'date': lambda *args: time.strftime('%Y-%m-%d'),
478         'user_id': lambda self, cr, uid, ctx: uid
479     }
480
481
482 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: