[FIX] web: FieldMany2ManyBinaryMultiFiles must extend ReinitializeFieldMixin
[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 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', size=64, 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'),('degressive','Degressive')], '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 account_asset_category()
74
75 class account_asset_asset(osv.osv):
76     _name = 'account.asset.asset'
77     _description = 'Asset'
78
79     def unlink(self, cr, uid, ids, context=None):
80         for asset in self.browse(cr, uid, ids, context=context):
81             if asset.account_move_line_ids: 
82                 raise osv.except_osv(_('Error!'), _('You cannot delete an asset that contains posted depreciation lines.'))
83         return super(account_asset_asset, self).unlink(cr, uid, ids, context=context)
84
85     def _get_period(self, cr, uid, context=None):
86         ctx = dict(context or {}, account_period_prefer_normal=True)
87         periods = self.pool.get('account.period').find(cr, uid, context=ctx)
88         if periods:
89             return periods[0]
90         else:
91             return False
92
93     def _get_last_depreciation_date(self, cr, uid, ids, context=None):
94         """
95         @param id: ids of a account.asset.asset objects
96         @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
97         """
98         cr.execute("""
99             SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
100             FROM account_asset_asset a
101             LEFT JOIN account_move_line l ON (l.asset_id = a.id)
102             WHERE a.id IN %s
103             GROUP BY a.id, a.purchase_date """, (tuple(ids),))
104         return dict(cr.fetchall())
105
106     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):
107         #by default amount = 0
108         amount = 0
109         if i == undone_dotation_number:
110             amount = residual_amount
111         else:
112             if asset.method == 'linear':
113                 amount = amount_to_depr / (undone_dotation_number - len(posted_depreciation_line_ids))
114                 if asset.prorata:
115                     amount = amount_to_depr / asset.method_number
116                     days = total_days - float(depreciation_date.strftime('%j'))
117                     if i == 1:
118                         amount = (amount_to_depr / asset.method_number) / total_days * days
119                     elif i == undone_dotation_number:
120                         amount = (amount_to_depr / asset.method_number) / total_days * (total_days - days)
121             elif asset.method == 'degressive':
122                 amount = residual_amount * asset.method_progress_factor
123                 if asset.prorata:
124                     days = total_days - float(depreciation_date.strftime('%j'))
125                     if i == 1:
126                         amount = (residual_amount * asset.method_progress_factor) / total_days * days
127                     elif i == undone_dotation_number:
128                         amount = (residual_amount * asset.method_progress_factor) / total_days * (total_days - days)
129         return amount
130
131     def _compute_board_undone_dotation_nb(self, cr, uid, asset, depreciation_date, total_days, context=None):
132         undone_dotation_number = asset.method_number
133         if asset.method_time == 'end':
134             end_date = datetime.strptime(asset.method_end, '%Y-%m-%d')
135             undone_dotation_number = 0
136             while depreciation_date <= end_date:
137                 depreciation_date = (datetime(depreciation_date.year, depreciation_date.month, depreciation_date.day) + relativedelta(months=+asset.method_period))
138                 undone_dotation_number += 1
139         if asset.prorata:
140             undone_dotation_number += 1
141         return undone_dotation_number
142
143     def compute_depreciation_board(self, cr, uid, ids, context=None):
144         depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
145         currency_obj = self.pool.get('res.currency')
146         for asset in self.browse(cr, uid, ids, context=context):
147             if asset.value_residual == 0.0:
148                 continue
149             posted_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_check', '=', True)],order='depreciation_date desc')
150             old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
151             if old_depreciation_line_ids:
152                 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
153
154             amount_to_depr = residual_amount = asset.value_residual
155             if asset.prorata:
156                 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
157             else:
158                 # depreciation_date = 1st January of purchase year
159                 purchase_date = datetime.strptime(asset.purchase_date, '%Y-%m-%d')
160                 #if we already have some previous validated entries, starting date isn't 1st January but last entry + method period
161                 if (len(posted_depreciation_line_ids)>0):
162                     last_depreciation_date = datetime.strptime(depreciation_lin_obj.browse(cr,uid,posted_depreciation_line_ids[0],context=context).depreciation_date, '%Y-%m-%d')
163                     depreciation_date = (last_depreciation_date+relativedelta(months=+asset.method_period))
164                 else:
165                     depreciation_date = datetime(purchase_date.year, 1, 1)
166             day = depreciation_date.day
167             month = depreciation_date.month
168             year = depreciation_date.year
169             total_days = (year % 4) and 365 or 366
170
171             undone_dotation_number = self._compute_board_undone_dotation_nb(cr, uid, asset, depreciation_date, total_days, context=context)
172             for x in range(len(posted_depreciation_line_ids), undone_dotation_number):
173                 i = x + 1
174                 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)
175                 company_currency = asset.company_id.currency_id.id
176                 current_currency = asset.currency_id.id
177                 # compute amount into company currency
178                 amount = currency_obj.compute(cr, uid, current_currency, company_currency, amount, context=context)
179                 residual_amount -= amount
180                 vals = {
181                      'amount': amount,
182                      'asset_id': asset.id,
183                      'sequence': i,
184                      'name': str(asset.id) +'/' + str(i),
185                      'remaining_value': residual_amount,
186                      'depreciated_value': (asset.purchase_value - asset.salvage_value) - (residual_amount + amount),
187                      'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
188                 }
189                 depreciation_lin_obj.create(cr, uid, vals, context=context)
190                 # Considering Depr. Period as months
191                 depreciation_date = (datetime(year, month, day) + relativedelta(months=+asset.method_period))
192                 day = depreciation_date.day
193                 month = depreciation_date.month
194                 year = depreciation_date.year
195         return True
196
197     def validate(self, cr, uid, ids, context=None):
198         if context is None:
199             context = {}
200         return self.write(cr, uid, ids, {
201             'state':'open'
202         }, context)
203
204     def set_to_close(self, cr, uid, ids, context=None):
205         return self.write(cr, uid, ids, {'state': 'close'}, context=context)
206
207     def set_to_draft(self, cr, uid, ids, context=None):
208         return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
209
210     def _amount_residual(self, cr, uid, ids, name, args, context=None):
211         cr.execute("""SELECT
212                 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
213             FROM
214                 account_move_line l
215             WHERE
216                 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
217         res=dict(cr.fetchall())
218         for asset in self.browse(cr, uid, ids, context):
219             res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0) - asset.salvage_value
220         for id in ids:
221             res.setdefault(id, 0.0)
222         return res
223
224     def onchange_company_id(self, cr, uid, ids, company_id=False, context=None):
225         val = {}
226         if company_id:
227             company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
228             if company.currency_id.company_id and company.currency_id.company_id.id != company_id:
229                 val['currency_id'] = False
230             else:
231                 val['currency_id'] = company.currency_id.id
232         return {'value': val}
233     
234     def onchange_purchase_salvage_value(self, cr, uid, ids, purchase_value, salvage_value, context=None):
235         val = {}
236         for asset in self.browse(cr, uid, ids, context=context):
237             if purchase_value:
238                 val['value_residual'] = purchase_value - salvage_value
239             if salvage_value:
240                 val['value_residual'] = purchase_value - salvage_value
241         return {'value': val}    
242
243     _columns = {
244         'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
245         'name': fields.char('Asset Name', size=64, required=True, readonly=True, states={'draft':[('readonly',False)]}),
246         'code': fields.char('Reference', size=32, readonly=True, states={'draft':[('readonly',False)]}),
247         'purchase_value': fields.float('Gross Value', required=True, readonly=True, states={'draft':[('readonly',False)]}),
248         'currency_id': fields.many2one('res.currency','Currency',required=True, readonly=True, states={'draft':[('readonly',False)]}),
249         'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft':[('readonly',False)]}),
250         'note': fields.text('Note'),
251         'category_id': fields.many2one('account.asset.category', 'Asset Category', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
252         'parent_id': fields.many2one('account.asset.asset', 'Parent Asset', readonly=True, states={'draft':[('readonly',False)]}),
253         'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
254         'purchase_date': fields.date('Purchase Date', required=True, readonly=True, states={'draft':[('readonly',False)]}),
255         'state': fields.selection([('draft','Draft'),('open','Running'),('close','Close')], 'Status', required=True,
256                                   help="When an asset is created, the status is 'Draft'.\n" \
257                                        "If the asset is confirmed, the status goes in 'Running' and the depreciation lines can be posted in the accounting.\n" \
258                                        "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."),
259         'active': fields.boolean('Active'),
260         'partner_id': fields.many2one('res.partner', 'Partner', readonly=True, states={'draft':[('readonly',False)]}),
261         'method': fields.selection([('linear','Linear'),('degressive','Degressive')], 'Computation Method', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="Choose the method to use to compute the amount of depreciation lines.\n"\
262             "  * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n" \
263             "  * Degressive: Calculated on basis of: Residual Value * Degressive Factor"),
264         'method_number': fields.integer('Number of Depreciations', readonly=True, states={'draft':[('readonly',False)]}, help="The number of depreciations needed to depreciate your asset"),
265         '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"),
266         'method_end': fields.date('Ending Date', readonly=True, states={'draft':[('readonly',False)]}),
267         'method_progress_factor': fields.float('Degressive Factor', readonly=True, states={'draft':[('readonly',False)]}),
268         'value_residual': fields.function(_amount_residual, method=True, digits_compute=dp.get_precision('Account'), string='Residual Value'),
269         'method_time': fields.selection([('number','Number of Depreciations'),('end','Ending Date')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]},
270                                   help="Choose the method to use to compute the dates and number of depreciation lines.\n"\
271                                        "  * Number of Depreciations: Fix the number of depreciation lines and the time between 2 depreciations.\n" \
272                                        "  * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond."),
273         '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'),
274         'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
275         'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True, states={'draft':[('readonly',False)],'open':[('readonly',False)]}),
276         '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)]}),
277     }
278     _defaults = {
279         'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
280         'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
281         'active': True,
282         'state': 'draft',
283         'method': 'linear',
284         'method_number': 5,
285         'method_time': 'number',
286         'method_period': 12,
287         'method_progress_factor': 0.3,
288         'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
289         'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
290     }
291
292     def _check_recursion(self, cr, uid, ids, context=None, parent=None):
293         return super(account_asset_asset, self)._check_recursion(cr, uid, ids, context=context, parent=parent)
294
295     def _check_prorata(self, cr, uid, ids, context=None):
296         for asset in self.browse(cr, uid, ids, context=context):
297             if asset.prorata and asset.method_time != 'number':
298                 return False
299         return True
300
301     _constraints = [
302         (_check_recursion, 'Error ! You cannot create recursive assets.', ['parent_id']),
303         (_check_prorata, 'Prorata temporis can be applied only for time method "number of depreciations".', ['prorata']),
304     ]
305
306     def onchange_category_id(self, cr, uid, ids, category_id, context=None):
307         res = {'value':{}}
308         asset_categ_obj = self.pool.get('account.asset.category')
309         if category_id:
310             category_obj = asset_categ_obj.browse(cr, uid, category_id, context=context)
311             res['value'] = {
312                             'method': category_obj.method,
313                             'method_number': category_obj.method_number,
314                             'method_time': category_obj.method_time,
315                             'method_period': category_obj.method_period,
316                             'method_progress_factor': category_obj.method_progress_factor,
317                             'method_end': category_obj.method_end,
318                             'prorata': category_obj.prorata,
319             }
320         return res
321
322     def onchange_method_time(self, cr, uid, ids, method_time='number', context=None):
323         res = {'value': {}}
324         if method_time != 'number':
325             res['value'] = {'prorata': False}
326         return res
327
328     def copy(self, cr, uid, id, default=None, context=None):
329         if default is None:
330             default = {}
331         if context is None:
332             context = {}
333         default.update({'depreciation_line_ids': [], 'account_move_line_ids': [], 'history_ids': [], 'state': 'draft'})
334         return super(account_asset_asset, self).copy(cr, uid, id, default, context=context)
335
336     def _compute_entries(self, cr, uid, ids, period_id, context=None):
337         result = []
338         period_obj = self.pool.get('account.period')
339         depreciation_obj = self.pool.get('account.asset.depreciation.line')
340         period = period_obj.browse(cr, uid, period_id, context=context)
341         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)
342         if context is None:
343             context = {}
344         context.update({'depreciation_date':period.date_stop})
345         return depreciation_obj.create_move(cr, uid, depreciation_ids, context=context)
346
347     def create(self, cr, uid, vals, context=None):
348         asset_id = super(account_asset_asset, self).create(cr, uid, vals, context=context)
349         self.compute_depreciation_board(cr, uid, [asset_id], context=context)
350         return asset_id
351     
352     def open_entries(self, cr, uid, ids, context=None):
353         if context is None:
354             context = {}
355         context.update({'search_default_asset_id': ids, 'default_asset_id': ids})
356         return {
357             'view_type': 'form',
358             'view_mode': 'tree,form',
359             'res_model': 'account.move.line',
360             'view_id': False,
361             'type': 'ir.actions.act_window',
362             'context': context,
363         }
364
365 account_asset_asset()
366
367 class account_asset_depreciation_line(osv.osv):
368     _name = 'account.asset.depreciation.line'
369     _description = 'Asset depreciation line'
370
371     def _get_move_check(self, cr, uid, ids, name, args, context=None):
372         res = {}
373         for line in self.browse(cr, uid, ids, context=context):
374             res[line.id] = bool(line.move_id)
375         return res
376
377     _columns = {
378         'name': fields.char('Depreciation Name', size=64, required=True, select=1),
379         'sequence': fields.integer('Sequence', required=True),
380         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, ondelete='cascade'),
381         'parent_state': fields.related('asset_id', 'state', type='char', string='State of Asset'),
382         'amount': fields.float('Current Depreciation', digits_compute=dp.get_precision('Account'), required=True),
383         'remaining_value': fields.float('Next Period Depreciation', digits_compute=dp.get_precision('Account'),required=True),
384         'depreciated_value': fields.float('Amount Already Depreciated', required=True),
385         'depreciation_date': fields.date('Depreciation Date', select=1),
386         'move_id': fields.many2one('account.move', 'Depreciation Entry'),
387         'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Posted', store=True)
388     }
389
390     def create_move(self, cr, uid, ids, context=None):
391         can_close = False
392         if context is None:
393             context = {}
394         asset_obj = self.pool.get('account.asset.asset')
395         period_obj = self.pool.get('account.period')
396         move_obj = self.pool.get('account.move')
397         move_line_obj = self.pool.get('account.move.line')
398         currency_obj = self.pool.get('res.currency')
399         created_move_ids = []
400         asset_ids = []
401         for line in self.browse(cr, uid, ids, context=context):
402             depreciation_date = context.get('depreciation_date') or line.depreciation_date or time.strftime('%Y-%m-%d')
403             ctx = dict(context, account_period_prefer_normal=True)
404             period_ids = period_obj.find(cr, uid, depreciation_date, context=ctx)
405             company_currency = line.asset_id.company_id.currency_id.id
406             current_currency = line.asset_id.currency_id.id
407             context.update({'date': depreciation_date})
408             amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
409             sign = (line.asset_id.category_id.journal_id.type == 'purchase' and 1) or -1
410             asset_name = line.asset_id.name
411             reference = line.name
412             move_vals = {
413                 'name': asset_name,
414                 'date': depreciation_date,
415                 'ref': reference,
416                 'period_id': period_ids and period_ids[0] or False,
417                 'journal_id': line.asset_id.category_id.journal_id.id,
418                 }
419             move_id = move_obj.create(cr, uid, move_vals, context=context)
420             journal_id = line.asset_id.category_id.journal_id.id
421             partner_id = line.asset_id.partner_id.id
422             move_line_obj.create(cr, uid, {
423                 'name': asset_name,
424                 'ref': reference,
425                 'move_id': move_id,
426                 'account_id': line.asset_id.category_id.account_depreciation_id.id,
427                 'debit': 0.0,
428                 'credit': amount,
429                 'period_id': period_ids and period_ids[0] or False,
430                 'journal_id': journal_id,
431                 'partner_id': partner_id,
432                 'currency_id': company_currency != current_currency and  current_currency or False,
433                 'amount_currency': company_currency != current_currency and - sign * line.amount or 0.0,
434                 'date': depreciation_date,
435             })
436             move_line_obj.create(cr, uid, {
437                 'name': asset_name,
438                 'ref': reference,
439                 'move_id': move_id,
440                 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
441                 'credit': 0.0,
442                 'debit': amount,
443                 'period_id': period_ids and period_ids[0] or False,
444                 'journal_id': journal_id,
445                 'partner_id': partner_id,
446                 'currency_id': company_currency != current_currency and  current_currency or False,
447                 'amount_currency': company_currency != current_currency and sign * line.amount or 0.0,
448                 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
449                 'date': depreciation_date,
450                 'asset_id': line.asset_id.id
451             })
452             self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
453             created_move_ids.append(move_id)
454             asset_ids.append(line.asset_id.id)
455         # we re-evaluate the assets to determine whether we can close them
456         for asset in asset_obj.browse(cr, uid, list(set(asset_ids)), context=context):
457             if currency_obj.is_zero(cr, uid, asset.currency_id, asset.value_residual):
458                 asset.write({'state': 'close'})
459         return created_move_ids
460
461 account_asset_depreciation_line()
462
463 class account_move_line(osv.osv):
464     _inherit = 'account.move.line'
465     _columns = {
466         'asset_id': fields.many2one('account.asset.asset', 'Asset', ondelete="restrict"),
467         'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
468
469     }
470 account_move_line()
471
472 class account_asset_history(osv.osv):
473     _name = 'account.asset.history'
474     _description = 'Asset history'
475     _columns = {
476         'name': fields.char('History name', size=64, select=1),
477         'user_id': fields.many2one('res.users', 'User', required=True),
478         'date': fields.date('Date', required=True),
479         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
480         'method_time': fields.selection([('number','Number of Depreciations'),('end','Ending Date')], 'Time Method', required=True,
481                                   help="The method to use to compute the dates and number of depreciation lines.\n"\
482                                        "Number of Depreciations: Fix the number of depreciation lines and the time between 2 depreciations.\n" \
483                                        "Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond."),
484         'method_number': fields.integer('Number of Depreciations', help="The number of depreciations needed to depreciate your asset"),
485         'method_period': fields.integer('Period Length', help="Time in month between two depreciations"),
486         'method_end': fields.date('Ending date'),
487         'note': fields.text('Note'),
488     }
489     _order = 'date desc'
490     _defaults = {
491         'date': lambda *args: time.strftime('%Y-%m-%d'),
492         'user_id': lambda self, cr, uid, ctx: uid
493     }
494
495 account_asset_history()
496
497 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: