[MERGE] merge pso's branch related to default companies for account_asset module
[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'),
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     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
289     def _get_move_check(self, cr, uid, ids, name, args, context=None):
290         res = {}
291         for line in self.browse(cr, uid, ids, context=context):
292             res[line.id] = bool(line.move_id)
293         return res
294
295     _columns = {
296         'name': fields.char('Depreciation Name', size=64, required=True, select=1),
297         'sequence': fields.integer('Sequence of the depreciation', required=True),
298         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
299         'amount': fields.float('Depreciation Amount', required=True),
300         'remaining_value': fields.float('Amount to Depreciate', required=True),
301         'depreciated_value': fields.float('Amount Already Depreciated', required=True),
302         'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
303         'move_id': fields.many2one('account.move', 'Depreciation Entry'),
304         'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
305     }
306
307     def create_move(self, cr, uid,ids, context=None):
308         if context is None:
309             context = {}
310         asset_obj = self.pool.get('account.asset.asset')
311         period_obj = self.pool.get('account.period')
312         move_obj = self.pool.get('account.move')
313         move_line_obj = self.pool.get('account.move.line')
314         currency_obj = self.pool.get('res.currency')
315         for line in self.browse(cr, uid, ids, context=context):
316             depreciation_date = time.strftime('%Y-%m-%d')
317             period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
318             company_currency = line.asset_id.company_id.currency_id.id
319             current_currency = line.asset_id.currency_id.id
320             context.update({'date': depreciation_date})
321             amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
322             sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
323             move_vals = {
324                 'name': line.name,
325                 'date': depreciation_date,
326                 'ref': line.name,
327                 'period_id': period_ids and period_ids[0] or False,
328                 'journal_id': line.asset_id.category_id.journal_id.id,
329                 }
330             move_id = move_obj.create(cr, uid, move_vals, context=context)
331             move_line_obj.create(cr, uid, {
332                 'name': line.name,
333                 'ref': line.name,
334                 'move_id': move_id,
335                 'account_id': line.asset_id.category_id.account_depreciation_id.id,
336                 'debit': 0.0,
337                 'credit': amount,
338                 'period_id': period_ids and period_ids[0] or False,
339                 'journal_id': line.asset_id.category_id.journal_id.id,
340                 'partner_id': line.asset_id.partner_id.id,
341                 'currency_id': company_currency <> current_currency and  current_currency or False,
342                 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
343                 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
344                 'date': depreciation_date,
345             })
346             move_line_obj.create(cr, uid, {
347                 'name': line.name,
348                 'ref': line.name,
349                 'move_id': move_id,
350                 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
351                 'credit': 0.0,
352                 'debit': amount,
353                 'period_id': period_ids and period_ids[0] or False,
354                 'journal_id': line.asset_id.category_id.journal_id.id,
355                 'partner_id': line.asset_id.partner_id.id,
356                 'currency_id': company_currency <> current_currency and  current_currency or False,
357                 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
358                 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
359                 'date': depreciation_date,
360             })
361             self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
362         return True
363
364 account_asset_depreciation_line()
365
366 #class account_asset_property(osv.osv):
367 #    def _amount_total(self, cr, uid, ids, name, args, context={}):
368 #        id_set=",".join(map(str,ids))
369 #        cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
370 #                account_asset_property p
371 #            left join
372 #                account_move_line l on (p.asset_id=l.asset_id)
373 #            WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
374 #        res=dict(cr.fetchall())
375 #        for id in ids:
376 #            res.setdefault(id, 0.0)
377 #        return res
378 #
379 #    def _close(self, cr, uid, property, context={}):
380 #        if property.state<>'close':
381 #            self.pool.get('account.asset.property').write(cr, uid, [property.id], {
382 #                'state': 'close'
383 #            })
384 #            property.state='close'
385 #        ok = property.asset_id.state=='open'
386 #        for prop in property.asset_id.property_ids:
387 #            ok = ok and prop.state=='close'
388 #        self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
389 #            'state': 'close'
390 #        }, context)
391 #        return True
392 #
393 #    _name = 'account.asset.property'
394 #    _description = 'Asset property'
395 #    _columns = {
396 #        'name': fields.char('Method name', size=64, select=1),
397 #        'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
398 #        'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
399 #        'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
400 #        'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
401 #        'journal_id': fields.many2one('account.journal', 'Journal', required=True),
402 #        'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
403 #        'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
404 #
405 #        'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
406 #        'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
407 #        'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
408 #        'method_end': fields.date('Ending date'),
409 #
410 #        'date': fields.date('Date created'),
411 #    #'test': fields.one2many('account.pre', 'asset_id',  readonly=True, states={'draft':[('readonly',False)]}),
412 #        'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
413 #        'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
414 #
415 #        'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
416 #        'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
417 #        'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
418 ##    'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
419 ##    'partner_id': fields.many2one('res.partner', 'Partner'),
420 ##    'note': fields.text('Note'),
421 #
422 #    }
423 #    _defaults = {
424 #        'type': lambda obj, cr, uid, context: 'direct',
425 #        'state': lambda obj, cr, uid, context: 'draft',
426 #        'method': lambda obj, cr, uid, context: 'linear',
427 #        'method_time': lambda obj, cr, uid, context: 'delay',
428 #        'method_progress_factor': lambda obj, cr, uid, context: 0.3,
429 #        'method_delay': lambda obj, cr, uid, context: 5,
430 #        'method_period': lambda obj, cr, uid, context: 12,
431 #        'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
432 #    }
433 #account_asset_property()
434
435 class account_move_line(osv.osv):
436     _inherit = 'account.move.line'
437     _columns = {
438         'asset_id': fields.many2one('account.asset.asset', 'Asset'),
439         'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
440
441     }
442 account_move_line()
443
444 class account_asset_history(osv.osv):
445     _name = 'account.asset.history'
446     _description = 'Asset history'
447     _columns = {
448         'name': fields.char('History name', size=64, select=1),
449         'user_id': fields.many2one('res.users', 'User', required=True),
450         'date': fields.date('Date', required=True),
451         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
452         'method_delay': fields.integer('Number of interval'),
453         'method_period': fields.integer('Period per interval'),
454         'method_end': fields.date('Ending date'),
455         'note': fields.text('Note'),
456     }
457     _defaults = {
458         'date': lambda *args: time.strftime('%Y-%m-%d'),
459         'user_id': lambda self,cr, uid,ctx: uid
460     }
461 account_asset_history()
462
463 class account_asset_board(osv.osv):
464     _name = 'account.asset.board'
465     _description = 'Asset board'
466     _columns = {
467         'name': fields.char('Asset name', size=64, required=True, select=1),
468         'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
469         'value_gross': fields.float('Gross value', required=True, select=1),
470         'value_asset': fields.float('Asset Value', required=True, select=1),
471         'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
472         'value_net': fields.float('Net value', required=True, select=1),
473
474     }
475     _auto = False
476     def init(self, cr):
477         cr.execute("""
478             create or replace view account_asset_board as (
479                 select
480                     min(l.id) as id,
481                     min(l.id) as asset_id,
482                     0.0 as value_gross,
483                     0.0 as value_asset,
484                     0.0 as value_asset_cumul,
485                     0.0 as value_net
486                 from
487                     account_move_line l
488                 where
489                     l.state <> 'draft' and
490                     l.asset_id=3
491             )""")
492 account_asset_board()
493
494 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: