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