[IMP] account_asset: Added company by default
[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('Asset category', size=64, required=True, select=1),
32         'note': fields.text('Note'),
33         'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'), #FIXME:add in the form view  with group = analytic 
34         'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'), #FIXME:add in the form view  with group = analytic
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',),#FIXME: required=True + add in the form view 
38         'journal_id': fields.many2one('account.journal', 'Journal', required=True),
39         'company_id': fields.many2one('res.company', 'Company'),
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_line_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     def _amount_total(self, cr, uid, ids, name, args, context={}):
131         #FIXME: function not working²
132         id_set=",".join(map(str,ids))
133         cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM 
134                 account_move_line l
135             WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
136         res=dict(cr.fetchall())
137         for id in ids:
138             res.setdefault(id, 0.0)
139         return res
140
141     def _amount_residual(self, cr, uid, ids, name, args, context={}):
142         cr.execute("""SELECT 
143                 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
144             FROM
145                 account_move_line l
146             WHERE
147                 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
148         res=dict(cr.fetchall())
149         for asset in self.browse(cr, uid, ids, context):
150             res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
151         for id in ids:
152             res.setdefault(id, 0.0)
153         return res
154
155     _columns = {
156         'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
157         'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
158
159         'name': fields.char('Asset', size=64, required=True, select=1),
160         'code': fields.char('Reference ', size=16, select=1),
161         'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
162         'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
163         'company_id': fields.many2one('res.company', 'Company', required=True),
164         'note': fields.text('Note'),
165         'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
166         'localisation': fields.char('Localisation', size=32, select=2),
167         'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
168         'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
169         'purchase_date': fields.date('Purchase Date', required=True), 
170         'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
171         'active': fields.boolean('Active', select=2),
172         'partner_id': fields.many2one('res.partner', 'Partner'),
173
174         'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
175         'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
176         'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
177         'method_end': fields.date('Ending date'),
178         'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
179         'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
180         'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
181         'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
182         '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'),
183         'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
184         'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
185     }
186     _defaults = {
187         'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
188         'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
189         'active': lambda obj, cr, uid, context: True,
190         'state': lambda obj, cr, uid, context: 'draft',
191         'period_id': _get_period,
192         'method': lambda obj, cr, uid, context: 'linear',
193         'method_delay': lambda obj, cr, uid, context: 5,
194         'method_time': lambda obj, cr, uid, context: 'delay',
195         'method_period': lambda obj, cr, uid, context: 12,
196         'method_progress_factor': lambda obj, cr, uid, context: 0.3,
197         'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
198         'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
199     }
200
201
202     def _compute_period(self, cr, uid, property, context={}):
203         if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
204             return False
205         if len(property.entry_asset_ids):
206             cp = property.entry_asset_ids[-1].period_id
207             cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
208             current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
209         else:
210             current_period = property.asset_id.period_id
211         return current_period
212
213     def _compute_move(self, cr, uid, property, period, context={}):
214         #FIXME: fucntion not working OK
215         result = []
216         total = 0.0
217         for move in property.asset_id.entry_ids:
218             total += move.debit-move.credit
219         for move in property.entry_asset_ids:
220             if move.account_id == property.account_asset_ids:
221                 total += move.debit
222                 total += -move.credit
223         periods = (len(property.entry_asset_ids)/2) - property.method_delay
224
225         if periods==1:
226             amount = total
227         else:
228             if property.method == 'linear':
229                 amount = total / periods
230             else:
231                 amount = total * property.method_progress_factor
232
233         move_id = self.pool.get('account.move').create(cr, uid, {
234             'journal_id': property.journal_id.id,
235             'period_id': period.id,
236             'name': property.name or property.asset_id.name,
237             'ref': property.asset_id.code
238         })
239         result = [move_id]
240         id = self.pool.get('account.move.line').create(cr, uid, {
241             'name': property.name or property.asset_id.name,
242             'move_id': move_id,
243             'account_id': property.account_asset_id.id,
244             'debit': amount>0 and amount or 0.0,
245             'credit': amount<0 and -amount or 0.0,
246             'ref': property.asset_id.code,
247             'period_id': period.id,
248             'journal_id': property.journal_id.id,
249             'partner_id': property.asset_id.partner_id.id,
250             'date': time.strftime('%Y-%m-%d'),
251         })
252         id2 = self.pool.get('account.move.line').create(cr, uid, {
253             'name': property.name or property.asset_id.name,
254             'move_id': move_id,
255             'account_id': property.account_actif_id.id,
256             'credit': amount>0 and amount or 0.0,
257             'debit': amount<0 and -amount or 0.0,
258             'ref': property.asset_id.code,
259             'period_id': period.id,
260             'journal_id': property.journal_id.id,
261             'partner_id': property.asset_id.partner_id.id,
262             'date': time.strftime('%Y-%m-%d'),
263         })
264     #
265         self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
266             'entry_asset_ids': [(4, id2, False),(4,id,False)]
267         })
268         if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
269             #self.pool.get('account.asset.property')._close(cr, uid, property, context)
270             return result
271         return result
272
273     def _compute_entries(self, cr, uid, asset, period_id, context={}):
274         #FIXME: function not working CHECK all res 
275         result = []
276         date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
277         for property in asset.property_ids:
278             if property.state=='open':
279                 period = self._compute_period(cr, uid, property, context)
280                 if period and (period.date_start<=date_start):
281                     result += self._compute_move(cr, uid, property, period, context)
282         return result
283 account_asset_asset()
284
285 class account_asset_depreciation_line(osv.osv):
286     _name = 'account.asset.depreciation.line'
287     _description = 'Asset depreciation line'
288     _columns = {
289         'name': fields.char('Depreciation Name', size=64, required=True, select=1),
290         'sequence': fields.integer('Sequence of the depreciation', required=True),
291         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
292         'amount': fields.float('Depreciation Amount', required=True),
293         'remaining_value': fields.float('Amount to Depreciate', required=True),
294         'depreciated_value': fields.float('Amount Already Depreciated', required=True),
295         'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
296         'move_line_id': fields.many2one('account.move.line', 'Depreciation Entry'),
297     }
298 account_asset_depreciation_line()
299
300 #class account_asset_property(osv.osv):
301 #    def _amount_total(self, cr, uid, ids, name, args, context={}):
302 #        id_set=",".join(map(str,ids))
303 #        cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM 
304 #                account_asset_property p
305 #            left join
306 #                account_move_line l on (p.asset_id=l.asset_id)
307 #            WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
308 #        res=dict(cr.fetchall())
309 #        for id in ids:
310 #            res.setdefault(id, 0.0)
311 #        return res
312 #
313 #    def _close(self, cr, uid, property, context={}):
314 #        if property.state<>'close':
315 #            self.pool.get('account.asset.property').write(cr, uid, [property.id], {
316 #                'state': 'close'
317 #            })
318 #            property.state='close'
319 #        ok = property.asset_id.state=='open'
320 #        for prop in property.asset_id.property_ids:
321 #            ok = ok and prop.state=='close'
322 #        self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
323 #            'state': 'close'
324 #        }, context)
325 #        return True
326 #
327 #    _name = 'account.asset.property'
328 #    _description = 'Asset property'
329 #    _columns = {
330 #        'name': fields.char('Method name', size=64, select=1),
331 #        'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
332 #        'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
333 #        'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
334 #        'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
335 #        'journal_id': fields.many2one('account.journal', 'Journal', required=True),
336 #        'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
337 #        'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
338 #
339 #        'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
340 #        'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
341 #        'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
342 #        'method_end': fields.date('Ending date'),
343 #
344 #        'date': fields.date('Date created'),
345 #    #'test': fields.one2many('account.pre', 'asset_id',  readonly=True, states={'draft':[('readonly',False)]}),
346 #        'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
347 #        'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
348 #
349 #        'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
350 #        'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
351 #        'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
352 ##    'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
353 ##    'partner_id': fields.many2one('res.partner', 'Partner'),
354 ##    'note': fields.text('Note'),
355 #
356 #    }
357 #    _defaults = {
358 #        'type': lambda obj, cr, uid, context: 'direct',
359 #        'state': lambda obj, cr, uid, context: 'draft',
360 #        'method': lambda obj, cr, uid, context: 'linear',
361 #        'method_time': lambda obj, cr, uid, context: 'delay',
362 #        'method_progress_factor': lambda obj, cr, uid, context: 0.3,
363 #        'method_delay': lambda obj, cr, uid, context: 5,
364 #        'method_period': lambda obj, cr, uid, context: 12,
365 #        'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
366 #    }
367 #account_asset_property()
368
369 class account_move_line(osv.osv):
370     _inherit = 'account.move.line'
371     _columns = {
372         'asset_id': fields.many2one('account.asset.asset', 'Asset'),
373         'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),    
374
375     }
376 account_move_line()
377
378 class account_asset_history(osv.osv):
379     _name = 'account.asset.history'
380     _description = 'Asset history'
381     _columns = {
382         'name': fields.char('History name', size=64, select=1),
383         'user_id': fields.many2one('res.users', 'User', required=True),
384         'date': fields.date('Date', required=True),
385         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
386         'method_delay': fields.integer('Number of interval'),
387         'method_period': fields.integer('Period per interval'),
388         'method_end': fields.date('Ending date'),
389         'note': fields.text('Note'),
390     }
391     _defaults = {
392         'date': lambda *args: time.strftime('%Y-%m-%d'),
393         'user_id': lambda self,cr, uid,ctx: uid
394     }
395 account_asset_history()
396
397 class account_asset_board(osv.osv):
398     _name = 'account.asset.board'
399     _description = 'Asset board'
400     _columns = {
401         'name': fields.char('Asset name', size=64, required=True, select=1),
402         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
403         'value_gross': fields.float('Gross value', required=True, select=1),
404         'value_asset': fields.float('Asset Value', required=True, select=1),
405         'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
406         'value_net': fields.float('Net value', required=True, select=1),
407
408     }
409     _auto = False
410     def init(self, cr):
411         cr.execute("""
412             create or replace view account_asset_board as (
413                 select
414                     min(l.id) as id,
415                     min(l.id) as asset_id,
416                     0.0 as value_gross,
417                     0.0 as value_asset,
418                     0.0 as value_asset_cumul,
419                     0.0 as value_net
420                 from
421                     account_move_line l
422                 where
423                     l.state <> 'draft' and
424                     l.asset_id=3
425             )""")
426 account_asset_board()
427
428 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: