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
25 import decimal_precision as dp
27 class account_asset_category(osv.osv):
28 _name = 'account.asset.category'
29 _description = 'Asset category'
32 'name': fields.char('Name', size=64, required=True, select=1),
33 'note': fields.text('Note'),
34 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
35 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
36 'account_asset_id': fields.many2one('account.account', 'Asset Account', required=True),
37 'account_depreciation_id': fields.many2one('account.account', 'Depreciation Account', required=True),
38 'account_expense_depreciation_id': fields.many2one('account.account', 'Depr. Expense Account', required=True),
39 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
40 'company_id': fields.many2one('res.company', 'Company', required=True),
41 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True),
42 'method_delay': fields.integer('Number of Depreciation'),
43 'method_period': fields.integer('Period Length'),
44 'method_progress_factor': fields.float('Progressif Factor'),
45 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True),
46 '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'),
47 'open_asset': fields.boolean('Skip Draft State', help="Check this if you want to automatically confirm the assets of this category when created by invoice."),
51 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.category', context=context),
54 'method_time': 'delay',
56 'method_progress_factor': 0.3,
59 account_asset_category()
61 #class one2many_mod_asset(fields.one2many):
63 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
64 # prinasset_property_id if context is None:
71 # #compute depreciation board
72 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
73 # for key, value in depreciation_line_ids.items():
74 # #write values on asset
75 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
76 # return depreciation_line_ids
78 class account_asset_asset(osv.osv):
79 _name = 'account.asset.asset'
80 _description = 'Asset'
82 def _get_period(self, cr, uid, context={}):
83 periods = self.pool.get('account.period').find(cr, uid)
89 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
91 @param id: ids of a account.asset.asset objects
92 @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
95 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
96 FROM account_asset_asset a
97 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
99 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
100 return dict(cr.fetchall())
102 def compute_depreciation_board(self, cr, uid,ids, context=None):
103 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
104 for asset in self.browse(cr, uid, ids, context=context):
105 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
106 if old_depreciation_line_ids:
107 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
108 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
109 residual_amount = asset.value_residual
110 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
111 day = depreciation_date.day
112 month = depreciation_date.month
113 year = depreciation_date.year
114 for i in range(1,undone_dotation_number+1):
115 if i == undone_dotation_number + 1:
116 amount = residual_amount
118 if asset.method == 'linear':
119 amount = asset.value_residual / undone_dotation_number
121 amount = residual_amount * asset.method_progress_factor
122 residual_amount -= amount
125 'asset_id': asset.id,
127 'name': str(asset.id) +'/'+ str(i),
128 'remaining_value': residual_amount,
129 'depreciated_value': asset.purchase_value - residual_amount,
130 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
132 depreciation_lin_obj.create(cr, uid, vals, context=context)
133 month += asset.method_period
134 depreciation_date = datetime(year + (month / 12), month % 12, day)
137 def validate(self, cr, uid, ids, context={}):
138 return self.write(cr, uid, ids, {
144 def _amount_residual(self, cr, uid, ids, name, args, context={}):
146 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
150 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
151 res=dict(cr.fetchall())
152 for asset in self.browse(cr, uid, ids, context):
153 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0) - asset.salvage_value
155 res.setdefault(id, 0.0)
159 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
160 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
162 'name': fields.char('Asset', size=64, required=True, select=1),
163 'code': fields.char('Reference ', size=16, select=1),
164 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
165 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
166 'company_id': fields.many2one('res.company', 'Company', required=True),
167 'note': fields.text('Note'),
168 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
169 'localisation': fields.char('Localisation', size=32, select=2),
170 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
171 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
172 'purchase_date': fields.date('Purchase Date', required=True),
173 'state': fields.selection([('draft','Draft'),('open','Running'),('close','Close')], 'state', required=True),
174 'active': fields.boolean('Active', select=2),
175 'partner_id': fields.many2one('res.partner', 'Partner'),
177 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
178 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
179 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
180 'method_end': fields.date('Ending date'),
181 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
182 'value_residual': fields.function(_amount_residual, method=True, digits_compute=dp.get_precision('Account'), string='Residual Value'),
183 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
184 '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'),
185 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
186 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines'),
187 'salvage_value': fields.float('Salvage Value', digits_compute=dp.get_precision('Account'), help="It is the amount you plan to have that you cannot depreciate."),
190 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
191 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
192 'active': lambda obj, cr, uid, context: True,
193 'state': lambda obj, cr, uid, context: 'draft',
194 'period_id': _get_period,
195 'method': lambda obj, cr, uid, context: 'linear',
196 'method_delay': lambda obj, cr, uid, context: 5,
197 'method_time': lambda obj, cr, uid, context: 'delay',
198 'method_period': lambda obj, cr, uid, context: 12,
199 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
200 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
201 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
204 def onchange_category_id(self, cr, uid, ids, category_id, context=None):
206 asset_categ_obj = self.pool.get('account.asset.category')
208 category_obj = asset_categ_obj.browse(cr, uid, category_id, context=context)
210 'method': category_obj.method,
211 'method_delay': category_obj.method_delay,
212 'method_time': category_obj.method_time,
213 'method_period': category_obj.method_period,
214 'method_progress_factor': category_obj.method_progress_factor,
215 'prorata': category_obj.prorata,
219 def copy(self, cr, uid, id, default=None, context=None):
224 default.update({'depreciation_line_ids': [], 'state': 'draft'})
225 return super(account_asset_asset, self).copy(cr, uid, id, default, context=context)
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,
385 'asset_id': line.asset_id.id
387 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
390 account_asset_depreciation_line()
392 #class account_asset_property(osv.osv):
393 # def _amount_total(self, cr, uid, ids, name, args, context={}):
394 # id_set=",".join(map(str,ids))
395 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
396 # account_asset_property p
398 # account_move_line l on (p.asset_id=l.asset_id)
399 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
400 # res=dict(cr.fetchall())
402 # res.setdefault(id, 0.0)
405 # def _close(self, cr, uid, property, context={}):
406 # if property.state<>'close':
407 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
410 # property.state='close'
411 # ok = property.asset_id.state=='open'
412 # for prop in property.asset_id.property_ids:
413 # ok = ok and prop.state=='close'
414 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
419 # _name = 'account.asset.property'
420 # _description = 'Asset property'
422 # 'name': fields.char('Method name', size=64, select=1),
423 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
424 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
425 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
426 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
427 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
428 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
429 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
431 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
432 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
433 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
434 # 'method_end': fields.date('Ending date'),
436 # 'date': fields.date('Date created'),
437 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
438 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
439 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
441 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
442 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
443 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
444 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
445 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
446 ## 'note': fields.text('Note'),
450 # 'type': lambda obj, cr, uid, context: 'direct',
451 # 'state': lambda obj, cr, uid, context: 'draft',
452 # 'method': lambda obj, cr, uid, context: 'linear',
453 # 'method_time': lambda obj, cr, uid, context: 'delay',
454 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
455 # 'method_delay': lambda obj, cr, uid, context: 5,
456 # 'method_period': lambda obj, cr, uid, context: 12,
457 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
459 #account_asset_property()
461 class account_move_line(osv.osv):
462 _inherit = 'account.move.line'
464 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
465 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
470 class account_asset_history(osv.osv):
471 _name = 'account.asset.history'
472 _description = 'Asset history'
474 'name': fields.char('History name', size=64, select=1),
475 'user_id': fields.many2one('res.users', 'User', required=True),
476 'date': fields.date('Date', required=True),
477 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
478 'method_delay': fields.integer('Number of interval'),
479 'method_period': fields.integer('Period per interval'),
480 'method_end': fields.date('Ending date'),
481 'note': fields.text('Note'),
484 'date': lambda *args: time.strftime('%Y-%m-%d'),
485 'user_id': lambda self,cr, uid,ctx: uid
487 account_asset_history()
489 class account_asset_board(osv.osv):
490 _name = 'account.asset.board'
491 _description = 'Asset board'
493 'name': fields.char('Asset name', size=64, required=True, select=1),
494 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
495 'value_gross': fields.float('Gross value', required=True, select=1),
496 'value_asset': fields.float('Asset Value', required=True, select=1),
497 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
498 'value_net': fields.float('Net value', required=True, select=1),
504 create or replace view account_asset_board as (
507 min(l.id) as asset_id,
510 0.0 as value_asset_cumul,
515 l.state <> 'draft' and
518 account_asset_board()
520 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: