1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
22 from osv import osv, fields
24 from datetime import datetime
26 class account_asset_category(osv.osv):
27 _name = 'account.asset.category'
28 _description = 'Asset category'
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 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True),
41 'method_delay': fields.integer('During (interval)'),
42 'method_period': fields.integer('Depre. all (period)'),
43 'method_progress_factor': fields.float('Progressif Factor'),
44 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True),
45 'prorata':fields.boolean('Prorata Temporis', help='Indicates that the accounting entries for this asset have to be done from the purchase date instead of the first January'),
49 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.category', context=context),
52 'method_time': 'delay',
54 'method_progress_factor': 0.3,
57 account_asset_category()
59 #class one2many_mod_asset(fields.one2many):
61 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
62 # prinasset_property_id if context is None:
69 # #compute depreciation board
70 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
71 # for key, value in depreciation_line_ids.items():
72 # #write values on asset
73 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
74 # return depreciation_line_ids
76 class account_asset_asset(osv.osv):
77 _name = 'account.asset.asset'
78 _description = 'Asset'
80 def _get_period(self, cr, uid, context={}):
81 periods = self.pool.get('account.period').find(cr, uid)
87 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
89 @param id: ids of a account.asset.asset objects
90 @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
93 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
94 FROM account_asset_asset a
95 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
97 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
98 return dict(cr.fetchall())
100 def compute_depreciation_board(self, cr, uid,ids, context=None):
101 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
102 for asset in self.browse(cr, uid, ids, context=context):
103 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
104 if old_depreciation_line_ids:
105 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
107 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
108 residual_amount = asset.value_residual
109 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
110 day = depreciation_date.day
111 month = depreciation_date.month
112 year = depreciation_date.year
113 for i in range(1,undone_dotation_number+1):
114 if i == undone_dotation_number + 1:
115 amount = residual_amount
117 if asset.method == 'linear':
118 amount = asset.purchase_value / undone_dotation_number
120 amount = residual_amount * asset.method_progress_factor
121 residual_amount -= amount
124 'asset_id': asset.id,
126 'name': str(asset.id) +'/'+ str(i),
127 'remaining_value': residual_amount,
128 'depreciated_value': asset.purchase_value - residual_amount,
129 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
131 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
132 month += asset.method_period
133 depreciation_date = datetime(year + (month / 12), month % 12, day)
136 def validate(self, cr, uid, ids, context={}):
137 return self.write(cr, uid, ids, {
141 def _amount_total(self, cr, uid, ids, name, args, context={}):
142 #FIXME: function not working²
143 id_set=",".join(map(str,ids))
144 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
146 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
147 res=dict(cr.fetchall())
149 res.setdefault(id, 0.0)
152 def _amount_residual(self, cr, uid, ids, name, args, context={}):
154 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
158 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
159 res=dict(cr.fetchall())
160 for asset in self.browse(cr, uid, ids, context):
161 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
163 res.setdefault(id, 0.0)
167 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
168 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
170 'name': fields.char('Asset', size=64, required=True, select=1),
171 'code': fields.char('Reference ', size=16, select=1),
172 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
173 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
174 'company_id': fields.many2one('res.company', 'Company', required=True),
175 'note': fields.text('Note'),
176 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
177 'localisation': fields.char('Localisation', size=32, select=2),
178 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
179 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
180 'purchase_date': fields.date('Purchase Date', required=True),
181 'state': fields.selection([('draft','Draft'),('open','Running'),('close','Close')], 'state', required=True),
182 'active': fields.boolean('Active', select=2),
183 'partner_id': fields.many2one('res.partner', 'Partner'),
185 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
186 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
187 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
188 'method_end': fields.date('Ending date'),
189 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
190 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
191 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
192 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
193 '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'),
194 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
195 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
198 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
199 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
200 'active': lambda obj, cr, uid, context: True,
201 'state': lambda obj, cr, uid, context: 'draft',
202 'period_id': _get_period,
203 'method': lambda obj, cr, uid, context: 'linear',
204 'method_delay': lambda obj, cr, uid, context: 5,
205 'method_time': lambda obj, cr, uid, context: 'delay',
206 'method_period': lambda obj, cr, uid, context: 12,
207 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
208 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
209 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
212 def onchange_category_id(self, cr, uid, ids, category_id, context=None):
214 asset_categ_obj = self.pool.get('account.asset.category')
216 category_obj = asset_categ_obj.browse(cr, uid, category_id, context=context)
218 'method': category_obj.method,
219 'method_delay': category_obj.method_delay,
220 'method_time': category_obj.method_time,
221 'method_period': category_obj.method_period,
222 'method_progress_factor': category_obj.method_progress_factor,
223 'prorata': category_obj.prorata,
227 def _compute_period(self, cr, uid, property, context={}):
228 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
230 if len(property.entry_asset_ids):
231 cp = property.entry_asset_ids[-1].period_id
232 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
233 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
235 current_period = property.asset_id.period_id
236 return current_period
238 def _compute_move(self, cr, uid, property, period, context={}):
239 #FIXME: fucntion not working OK
242 for move in property.asset_id.entry_ids:
243 total += move.debit-move.credit
244 for move in property.entry_asset_ids:
245 if move.account_id == property.account_asset_ids:
247 total += -move.credit
248 periods = (len(property.entry_asset_ids)/2) - property.method_delay
253 if property.method == 'linear':
254 amount = total / periods
256 amount = total * property.method_progress_factor
258 move_id = self.pool.get('account.move').create(cr, uid, {
259 'journal_id': property.journal_id.id,
260 'period_id': period.id,
261 'name': property.name or property.asset_id.name,
262 'ref': property.asset_id.code
265 id = self.pool.get('account.move.line').create(cr, uid, {
266 'name': property.name or property.asset_id.name,
268 'account_id': property.account_asset_id.id,
269 'debit': amount>0 and amount or 0.0,
270 'credit': amount<0 and -amount or 0.0,
271 'ref': property.asset_id.code,
272 'period_id': period.id,
273 'journal_id': property.journal_id.id,
274 'partner_id': property.asset_id.partner_id.id,
275 'date': time.strftime('%Y-%m-%d'),
277 id2 = self.pool.get('account.move.line').create(cr, uid, {
278 'name': property.name or property.asset_id.name,
280 'account_id': property.account_actif_id.id,
281 'credit': amount>0 and amount or 0.0,
282 'debit': amount<0 and -amount or 0.0,
283 'ref': property.asset_id.code,
284 'period_id': period.id,
285 'journal_id': property.journal_id.id,
286 'partner_id': property.asset_id.partner_id.id,
287 'date': time.strftime('%Y-%m-%d'),
290 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
291 'entry_asset_ids': [(4, id2, False),(4,id,False)]
293 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
294 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
298 def _compute_entries(self, cr, uid, asset, period_id, context={}):
299 #FIXME: function not working CHECK all res
301 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
302 for property in asset.property_ids:
303 if property.state=='open':
304 period = self._compute_period(cr, uid, property, context)
305 if period and (period.date_start<=date_start):
306 result += self._compute_move(cr, uid, property, period, context)
308 account_asset_asset()
310 class account_asset_depreciation_line(osv.osv):
311 _name = 'account.asset.depreciation.line'
312 _description = 'Asset depreciation line'
314 def _get_move_check(self, cr, uid, ids, name, args, context=None):
316 for line in self.browse(cr, uid, ids, context=context):
317 res[line.id] = bool(line.move_id)
321 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
322 'sequence': fields.integer('Sequence of the depreciation', required=True),
323 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
324 'amount': fields.float('Depreciation Amount', required=True),
325 'remaining_value': fields.float('Amount to Depreciate', required=True),
326 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
327 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
328 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
329 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Posted', store=True)
332 def create_move(self, cr, uid,ids, context=None):
335 asset_obj = self.pool.get('account.asset.asset')
336 period_obj = self.pool.get('account.period')
337 move_obj = self.pool.get('account.move')
338 move_line_obj = self.pool.get('account.move.line')
339 currency_obj = self.pool.get('res.currency')
340 for line in self.browse(cr, uid, ids, context=context):
341 depreciation_date = time.strftime('%Y-%m-%d')
342 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
343 company_currency = line.asset_id.company_id.currency_id.id
344 current_currency = line.asset_id.currency_id.id
345 context.update({'date': depreciation_date})
346 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
347 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
350 'date': depreciation_date,
352 'period_id': period_ids and period_ids[0] or False,
353 'journal_id': line.asset_id.category_id.journal_id.id,
355 move_id = move_obj.create(cr, uid, move_vals, context=context)
356 move_line_obj.create(cr, uid, {
360 'account_id': line.asset_id.category_id.account_depreciation_id.id,
363 'period_id': period_ids and period_ids[0] or False,
364 'journal_id': line.asset_id.category_id.journal_id.id,
365 'partner_id': line.asset_id.partner_id.id,
366 'currency_id': company_currency <> current_currency and current_currency or False,
367 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
368 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
369 'date': depreciation_date,
371 move_line_obj.create(cr, uid, {
375 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
378 'period_id': period_ids and period_ids[0] or False,
379 'journal_id': line.asset_id.category_id.journal_id.id,
380 'partner_id': line.asset_id.partner_id.id,
381 'currency_id': company_currency <> current_currency and current_currency or False,
382 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
383 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
384 'date': depreciation_date,
386 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
389 account_asset_depreciation_line()
391 #class account_asset_property(osv.osv):
392 # def _amount_total(self, cr, uid, ids, name, args, context={}):
393 # id_set=",".join(map(str,ids))
394 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
395 # account_asset_property p
397 # account_move_line l on (p.asset_id=l.asset_id)
398 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
399 # res=dict(cr.fetchall())
401 # res.setdefault(id, 0.0)
404 # def _close(self, cr, uid, property, context={}):
405 # if property.state<>'close':
406 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
409 # property.state='close'
410 # ok = property.asset_id.state=='open'
411 # for prop in property.asset_id.property_ids:
412 # ok = ok and prop.state=='close'
413 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
418 # _name = 'account.asset.property'
419 # _description = 'Asset property'
421 # 'name': fields.char('Method name', size=64, select=1),
422 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
423 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
424 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
425 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
426 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
427 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
428 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
430 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
431 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
432 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
433 # 'method_end': fields.date('Ending date'),
435 # 'date': fields.date('Date created'),
436 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
437 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
438 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
440 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
441 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
442 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
443 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
444 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
445 ## 'note': fields.text('Note'),
449 # 'type': lambda obj, cr, uid, context: 'direct',
450 # 'state': lambda obj, cr, uid, context: 'draft',
451 # 'method': lambda obj, cr, uid, context: 'linear',
452 # 'method_time': lambda obj, cr, uid, context: 'delay',
453 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
454 # 'method_delay': lambda obj, cr, uid, context: 5,
455 # 'method_period': lambda obj, cr, uid, context: 12,
456 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
458 #account_asset_property()
460 class account_move_line(osv.osv):
461 _inherit = 'account.move.line'
463 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
464 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
469 class account_asset_history(osv.osv):
470 _name = 'account.asset.history'
471 _description = 'Asset history'
473 'name': fields.char('History name', size=64, select=1),
474 'user_id': fields.many2one('res.users', 'User', required=True),
475 'date': fields.date('Date', required=True),
476 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
477 'method_delay': fields.integer('Number of interval'),
478 'method_period': fields.integer('Period per interval'),
479 'method_end': fields.date('Ending date'),
480 'note': fields.text('Note'),
483 'date': lambda *args: time.strftime('%Y-%m-%d'),
484 'user_id': lambda self,cr, uid,ctx: uid
486 account_asset_history()
488 class account_asset_board(osv.osv):
489 _name = 'account.asset.board'
490 _description = 'Asset board'
492 'name': fields.char('Asset name', size=64, required=True, select=1),
493 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
494 'value_gross': fields.float('Gross value', required=True, select=1),
495 'value_asset': fields.float('Asset Value', required=True, select=1),
496 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
497 'value_net': fields.float('Net value', required=True, select=1),
503 create or replace view account_asset_board as (
506 min(l.id) as asset_id,
509 0.0 as value_asset_cumul,
514 l.state <> 'draft' and
517 account_asset_board()
519 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: