[IMP] account_asset: Improved field name.
[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-2009 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 from osv import osv, fields
23 import time
24 from datetime import datetime
25
26 class account_asset_category(osv.osv):
27     _name = 'account.asset.category'
28     _description = 'Asset category'
29
30     _columns = {
31         'name': fields.char('Name', size=64, required=True, select=1),
32         'note': fields.text('Note'),
33         'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
34         'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
35         'account_asset_id': fields.many2one('account.account', 'Asset Account', required=True),
36         'account_depreciation_id': fields.many2one('account.account', 'Depreciation Account', required=True),
37         'account_expense_depreciation_id': fields.many2one('account.account', 'Depr. Expense Account', required=True),
38         'journal_id': fields.many2one('account.journal', 'Journal', required=True),
39         'company_id': fields.many2one('res.company', 'Company', required=True),
40         'on_change_state': fields.boolean('Skip Draft State', help="Check this if you want to skip draft state for asset when it is created by invoice."),
41     }
42
43     _defaults = {
44         'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.category', context=context),
45     }
46
47 account_asset_category()
48
49 #class one2many_mod_asset(fields.one2many):
50 #
51 #    def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
52 #        prinasset_property_id        if context is None:
53 #            context = {}
54 #        if not values:
55 #            values = {}
56 #        res = {}
57 #        for id in ids:
58 #            res[id] = []
59 #        #compute depreciation board
60 #        depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
61 #        for key, value in depreciation_line_ids.items():
62 #            #write values on asset
63 #            obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
64 #        return depreciation_line_ids
65
66 class account_asset_asset(osv.osv):
67     _name = 'account.asset.asset'
68     _description = 'Asset'
69
70     def _get_period(self, cr, uid, context={}):
71         periods = self.pool.get('account.period').find(cr, uid)
72         if periods:
73             return periods[0]
74         else:
75             return False
76
77     def _get_last_depreciation_date(self, cr, uid, ids, context=None):
78         """
79         @param id: ids of a account.asset.asset objects
80         @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
81         """
82         cr.execute("""
83             SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
84             FROM account_asset_asset a
85             LEFT JOIN account_move_line l ON (l.asset_id = a.id)
86             WHERE a.id IN %s
87             GROUP BY a.id, a.purchase_date """, (tuple(ids),))
88         return dict(cr.fetchall())
89
90     def compute_depreciation_board(self, cr, uid,ids, context=None):
91         depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
92         for asset in self.browse(cr, uid, ids, context=context):
93             old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
94             if old_depreciation_line_ids:
95                 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
96
97             undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
98             residual_amount = asset.value_residual
99             depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
100             day = depreciation_date.day
101             month = depreciation_date.month
102             year = depreciation_date.year
103             for i in range(1,undone_dotation_number+1):
104                 if i == undone_dotation_number + 1:
105                     amount = residual_amount
106                 else:
107                     if asset.method == 'linear':
108                         amount = asset.purchase_value / undone_dotation_number
109                     else:
110                         amount = residual_amount * asset.method_progress_factor
111                 residual_amount -= amount
112                 vals = {
113                      'amount': amount,
114                      'asset_id': asset.id,
115                      'sequence':i,
116                      'name': str(asset.id) +'/'+ str(i),
117                      'remaining_value': residual_amount,
118                      'depreciated_value': asset.purchase_value - residual_amount,
119                      'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
120                 }
121                 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
122                 month += asset.method_period
123                 depreciation_date = datetime(year + (month / 12), month % 12, day)
124         return True
125
126     def validate(self, cr, uid, ids, context={}):
127         return self.write(cr, uid, ids, {
128             'state':'normal'
129         }, context)
130
131     def _amount_total(self, cr, uid, ids, name, args, context={}):
132         #FIXME: function not working²
133         id_set=",".join(map(str,ids))
134         cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
135                 account_move_line l
136             WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
137         res=dict(cr.fetchall())
138         for id in ids:
139             res.setdefault(id, 0.0)
140         return res
141
142     def _amount_residual(self, cr, uid, ids, name, args, context={}):
143         cr.execute("""SELECT
144                 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
145             FROM
146                 account_move_line l
147             WHERE
148                 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
149         res=dict(cr.fetchall())
150         for asset in self.browse(cr, uid, ids, context):
151             res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
152         for id in ids:
153             res.setdefault(id, 0.0)
154         return res
155
156     _columns = {
157         'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
158         'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
159
160         'name': fields.char('Asset', size=64, required=True, select=1),
161         'code': fields.char('Reference ', size=16, select=1),
162         'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
163         'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
164         'company_id': fields.many2one('res.company', 'Company', required=True),
165         'note': fields.text('Note'),
166         'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
167         'localisation': fields.char('Localisation', size=32, select=2),
168         'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
169         'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
170         'purchase_date': fields.date('Purchase Date', required=True),
171         'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
172         'active': fields.boolean('Active', select=2),
173         'partner_id': fields.many2one('res.partner', 'Partner'),
174
175         'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
176         'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
177         'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
178         'method_end': fields.date('Ending date'),
179         'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
180         'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
181         'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
182         'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
183         'prorata':fields.boolean('Prorata Temporis', Readonly="True", help='Indicates that the accounting entries for this asset have to be done from the purchase date instead of the first January'),
184         'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
185         'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
186     }
187     _defaults = {
188         'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
189         'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
190         'active': lambda obj, cr, uid, context: True,
191         'state': lambda obj, cr, uid, context: 'draft',
192         'period_id': _get_period,
193         'method': lambda obj, cr, uid, context: 'linear',
194         'method_delay': lambda obj, cr, uid, context: 5,
195         'method_time': lambda obj, cr, uid, context: 'delay',
196         'method_period': lambda obj, cr, uid, context: 12,
197         'method_progress_factor': lambda obj, cr, uid, context: 0.3,
198         'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
199         'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
200     }
201
202
203     def _compute_period(self, cr, uid, property, context={}):
204         if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
205             return False
206         if len(property.entry_asset_ids):
207             cp = property.entry_asset_ids[-1].period_id
208             cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
209             current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
210         else:
211             current_period = property.asset_id.period_id
212         return current_period
213
214     def _compute_move(self, cr, uid, property, period, context={}):
215         #FIXME: fucntion not working OK
216         result = []
217         total = 0.0
218         for move in property.asset_id.entry_ids:
219             total += move.debit-move.credit
220         for move in property.entry_asset_ids:
221             if move.account_id == property.account_asset_ids:
222                 total += move.debit
223                 total += -move.credit
224         periods = (len(property.entry_asset_ids)/2) - property.method_delay
225
226         if periods==1:
227             amount = total
228         else:
229             if property.method == 'linear':
230                 amount = total / periods
231             else:
232                 amount = total * property.method_progress_factor
233
234         move_id = self.pool.get('account.move').create(cr, uid, {
235             'journal_id': property.journal_id.id,
236             'period_id': period.id,
237             'name': property.name or property.asset_id.name,
238             'ref': property.asset_id.code
239         })
240         result = [move_id]
241         id = self.pool.get('account.move.line').create(cr, uid, {
242             'name': property.name or property.asset_id.name,
243             'move_id': move_id,
244             'account_id': property.account_asset_id.id,
245             'debit': amount>0 and amount or 0.0,
246             'credit': amount<0 and -amount or 0.0,
247             'ref': property.asset_id.code,
248             'period_id': period.id,
249             'journal_id': property.journal_id.id,
250             'partner_id': property.asset_id.partner_id.id,
251             'date': time.strftime('%Y-%m-%d'),
252         })
253         id2 = self.pool.get('account.move.line').create(cr, uid, {
254             'name': property.name or property.asset_id.name,
255             'move_id': move_id,
256             'account_id': property.account_actif_id.id,
257             'credit': amount>0 and amount or 0.0,
258             'debit': amount<0 and -amount or 0.0,
259             'ref': property.asset_id.code,
260             'period_id': period.id,
261             'journal_id': property.journal_id.id,
262             'partner_id': property.asset_id.partner_id.id,
263             'date': time.strftime('%Y-%m-%d'),
264         })
265     #
266         self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
267             'entry_asset_ids': [(4, id2, False),(4,id,False)]
268         })
269         if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
270             #self.pool.get('account.asset.property')._close(cr, uid, property, context)
271             return result
272         return result
273
274     def _compute_entries(self, cr, uid, asset, period_id, context={}):
275         #FIXME: function not working CHECK all res
276         result = []
277         date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
278         for property in asset.property_ids:
279             if property.state=='open':
280                 period = self._compute_period(cr, uid, property, context)
281                 if period and (period.date_start<=date_start):
282                     result += self._compute_move(cr, uid, property, period, context)
283         return result
284 account_asset_asset()
285
286 class account_asset_depreciation_line(osv.osv):
287     _name = 'account.asset.depreciation.line'
288     _description = 'Asset depreciation line'
289
290     def _get_move_check(self, cr, uid, ids, name, args, context=None):
291         res = {}
292         for line in self.browse(cr, uid, ids, context=context):
293             res[line.id] = bool(line.move_id)
294         return res
295
296     _columns = {
297         'name': fields.char('Depreciation Name', size=64, required=True, select=1),
298         'sequence': fields.integer('Sequence of the depreciation', required=True),
299         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
300         'amount': fields.float('Depreciation Amount', required=True),
301         'remaining_value': fields.float('Amount to Depreciate', required=True),
302         'depreciated_value': fields.float('Amount Already Depreciated', required=True),
303         'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
304         'move_id': fields.many2one('account.move', 'Depreciation Entry'),
305         'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
306     }
307
308     def create_move(self, cr, uid,ids, context=None):
309         if context is None:
310             context = {}
311         asset_obj = self.pool.get('account.asset.asset')
312         period_obj = self.pool.get('account.period')
313         move_obj = self.pool.get('account.move')
314         move_line_obj = self.pool.get('account.move.line')
315         currency_obj = self.pool.get('res.currency')
316         for line in self.browse(cr, uid, ids, context=context):
317             depreciation_date = time.strftime('%Y-%m-%d')
318             period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
319             company_currency = line.asset_id.company_id.currency_id.id
320             current_currency = line.asset_id.currency_id.id
321             context.update({'date': depreciation_date})
322             amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
323             sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
324             move_vals = {
325                 'name': line.name,
326                 'date': depreciation_date,
327                 'ref': line.name,
328                 'period_id': period_ids and period_ids[0] or False,
329                 'journal_id': line.asset_id.category_id.journal_id.id,
330                 }
331             move_id = move_obj.create(cr, uid, move_vals, context=context)
332             move_line_obj.create(cr, uid, {
333                 'name': line.name,
334                 'ref': line.name,
335                 'move_id': move_id,
336                 'account_id': line.asset_id.category_id.account_depreciation_id.id,
337                 'debit': 0.0,
338                 'credit': amount,
339                 'period_id': period_ids and period_ids[0] or False,
340                 'journal_id': line.asset_id.category_id.journal_id.id,
341                 'partner_id': line.asset_id.partner_id.id,
342                 'currency_id': company_currency <> current_currency and  current_currency or False,
343                 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
344                 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
345                 'date': depreciation_date,
346             })
347             move_line_obj.create(cr, uid, {
348                 'name': line.name,
349                 'ref': line.name,
350                 'move_id': move_id,
351                 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
352                 'credit': 0.0,
353                 'debit': amount,
354                 'period_id': period_ids and period_ids[0] or False,
355                 'journal_id': line.asset_id.category_id.journal_id.id,
356                 'partner_id': line.asset_id.partner_id.id,
357                 'currency_id': company_currency <> current_currency and  current_currency or False,
358                 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
359                 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
360                 'date': depreciation_date,
361             })
362             self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
363         return True
364
365 account_asset_depreciation_line()
366
367 #class account_asset_property(osv.osv):
368 #    def _amount_total(self, cr, uid, ids, name, args, context={}):
369 #        id_set=",".join(map(str,ids))
370 #        cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
371 #                account_asset_property p
372 #            left join
373 #                account_move_line l on (p.asset_id=l.asset_id)
374 #            WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
375 #        res=dict(cr.fetchall())
376 #        for id in ids:
377 #            res.setdefault(id, 0.0)
378 #        return res
379 #
380 #    def _close(self, cr, uid, property, context={}):
381 #        if property.state<>'close':
382 #            self.pool.get('account.asset.property').write(cr, uid, [property.id], {
383 #                'state': 'close'
384 #            })
385 #            property.state='close'
386 #        ok = property.asset_id.state=='open'
387 #        for prop in property.asset_id.property_ids:
388 #            ok = ok and prop.state=='close'
389 #        self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
390 #            'state': 'close'
391 #        }, context)
392 #        return True
393 #
394 #    _name = 'account.asset.property'
395 #    _description = 'Asset property'
396 #    _columns = {
397 #        'name': fields.char('Method name', size=64, select=1),
398 #        'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
399 #        'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
400 #        'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
401 #        'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
402 #        'journal_id': fields.many2one('account.journal', 'Journal', required=True),
403 #        'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
404 #        'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
405 #
406 #        'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
407 #        'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
408 #        'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
409 #        'method_end': fields.date('Ending date'),
410 #
411 #        'date': fields.date('Date created'),
412 #    #'test': fields.one2many('account.pre', 'asset_id',  readonly=True, states={'draft':[('readonly',False)]}),
413 #        'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
414 #        'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
415 #
416 #        'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
417 #        'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
418 #        'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
419 ##    'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
420 ##    'partner_id': fields.many2one('res.partner', 'Partner'),
421 ##    'note': fields.text('Note'),
422 #
423 #    }
424 #    _defaults = {
425 #        'type': lambda obj, cr, uid, context: 'direct',
426 #        'state': lambda obj, cr, uid, context: 'draft',
427 #        'method': lambda obj, cr, uid, context: 'linear',
428 #        'method_time': lambda obj, cr, uid, context: 'delay',
429 #        'method_progress_factor': lambda obj, cr, uid, context: 0.3,
430 #        'method_delay': lambda obj, cr, uid, context: 5,
431 #        'method_period': lambda obj, cr, uid, context: 12,
432 #        'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
433 #    }
434 #account_asset_property()
435
436 class account_move_line(osv.osv):
437     _inherit = 'account.move.line'
438     _columns = {
439         'asset_id': fields.many2one('account.asset.asset', 'Asset'),
440         'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
441
442     }
443 account_move_line()
444
445 class account_asset_history(osv.osv):
446     _name = 'account.asset.history'
447     _description = 'Asset history'
448     _columns = {
449         'name': fields.char('History name', size=64, select=1),
450         'user_id': fields.many2one('res.users', 'User', required=True),
451         'date': fields.date('Date', required=True),
452         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
453         'method_delay': fields.integer('Number of interval'),
454         'method_period': fields.integer('Period per interval'),
455         'method_end': fields.date('Ending date'),
456         'note': fields.text('Note'),
457     }
458     _defaults = {
459         'date': lambda *args: time.strftime('%Y-%m-%d'),
460         'user_id': lambda self,cr, uid,ctx: uid
461     }
462 account_asset_history()
463
464 class account_asset_board(osv.osv):
465     _name = 'account.asset.board'
466     _description = 'Asset board'
467     _columns = {
468         'name': fields.char('Asset name', size=64, required=True, select=1),
469         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
470         'value_gross': fields.float('Gross value', required=True, select=1),
471         'value_asset': fields.float('Asset Value', required=True, select=1),
472         'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
473         'value_net': fields.float('Net value', required=True, select=1),
474
475     }
476     _auto = False
477     def init(self, cr):
478         cr.execute("""
479             create or replace view account_asset_board as (
480                 select
481                     min(l.id) as id,
482                     min(l.id) as asset_id,
483                     0.0 as value_gross,
484                     0.0 as value_asset,
485                     0.0 as value_asset_cumul,
486                     0.0 as value_net
487                 from
488                     account_move_line l
489                 where
490                     l.state <> 'draft' and
491                     l.asset_id=3
492             )""")
493 account_asset_board()
494
495 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: