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),
43 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.category', context=context),
46 def onchange_account_asset(self, cr, uid, ids, account_asset_id, context=None):
49 res['value'] = {'account_depreciation_id': account_asset_id}
52 account_asset_category()
54 #class one2many_mod_asset(fields.one2many):
56 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
57 # prinasset_property_id if context is None:
64 # #compute depreciation board
65 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
66 # for key, value in depreciation_line_ids.items():
67 # #write values on asset
68 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
69 # return depreciation_line_ids
71 class account_asset_asset(osv.osv):
72 _name = 'account.asset.asset'
73 _description = 'Asset'
75 def _get_period(self, cr, uid, context={}):
76 periods = self.pool.get('account.period').find(cr, uid)
82 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
84 @param id: ids of a account.asset.asset objects
85 @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
88 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
89 FROM account_asset_asset a
90 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
92 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
93 return dict(cr.fetchall())
95 def compute_depreciation_board(self, cr, uid,ids, context=None):
96 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
98 for asset in self.browse(cr, uid, ids, context=context):
100 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
101 if old_depreciation_line_ids:
102 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
104 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
105 residual_amount = asset.value_residual
106 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
107 day = depreciation_date.day
108 month = depreciation_date.month
109 year = depreciation_date.year
110 for i in range(1,undone_dotation_number+1):
111 if i == undone_dotation_number + 1:
112 amount = residual_amount
114 if asset.method == 'linear':
115 amount = asset.purchase_value / undone_dotation_number
117 amount = residual_amount * asset.method_progress_factor
118 residual_amount -= amount
121 'asset_id': asset.id,
123 'name': str(asset.id) +'/'+ str(i),
124 'remaining_value': residual_amount,
125 'depreciated_value': asset.purchase_value - residual_amount,
126 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
128 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
129 month += asset.method_period
130 depreciation_date = datetime(year + (month / 12), month % 12, day)
133 def validate(self, cr, uid, ids, context={}):
134 return self.write(cr, uid, ids, {
138 def _amount_total(self, cr, uid, ids, name, args, context={}):
139 #FIXME: function not working²
140 id_set=",".join(map(str,ids))
141 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
143 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
144 res=dict(cr.fetchall())
146 res.setdefault(id, 0.0)
149 def _amount_residual(self, cr, uid, ids, name, args, context={}):
151 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
155 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
156 res=dict(cr.fetchall())
157 for asset in self.browse(cr, uid, ids, context):
158 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
160 res.setdefault(id, 0.0)
164 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
165 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
167 'name': fields.char('Asset', size=64, required=True, select=1),
168 'code': fields.char('Reference ', size=16, select=1),
169 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
170 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
171 'company_id': fields.many2one('res.company', 'Company', required=True),
172 'note': fields.text('Note'),
173 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
174 'localisation': fields.char('Localisation', size=32, select=2),
175 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
176 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
177 'purchase_date': fields.date('Purchase Date', required=True),
178 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'State', required=True),
179 'active': fields.boolean('Active', select=2),
180 'partner_id': fields.many2one('res.partner', 'Partner'),
182 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
183 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
184 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
185 'method_end': fields.date('Ending date'),
186 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
187 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
188 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
189 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
190 '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'),
191 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
192 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
195 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
196 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
197 'active': lambda obj, cr, uid, context: True,
198 'state': lambda obj, cr, uid, context: 'draft',
199 'period_id': _get_period,
200 'method': lambda obj, cr, uid, context: 'linear',
201 'method_delay': lambda obj, cr, uid, context: 5,
202 'method_time': lambda obj, cr, uid, context: 'delay',
203 'method_period': lambda obj, cr, uid, context: 12,
204 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
205 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
206 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
210 def _compute_period(self, cr, uid, property, context={}):
211 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
213 if len(property.entry_asset_ids):
214 cp = property.entry_asset_ids[-1].period_id
215 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
216 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
218 current_period = property.asset_id.period_id
219 return current_period
221 def _compute_move(self, cr, uid, property, period, context={}):
222 #FIXME: fucntion not working OK
225 for move in property.asset_id.entry_ids:
226 total += move.debit-move.credit
227 for move in property.entry_asset_ids:
228 if move.account_id == property.account_asset_ids:
230 total += -move.credit
231 periods = (len(property.entry_asset_ids)/2) - property.method_delay
236 if property.method == 'linear':
237 amount = total / periods
239 amount = total * property.method_progress_factor
241 move_id = self.pool.get('account.move').create(cr, uid, {
242 'journal_id': property.journal_id.id,
243 'period_id': period.id,
244 'name': property.name or property.asset_id.name,
245 'ref': property.asset_id.code
248 id = self.pool.get('account.move.line').create(cr, uid, {
249 'name': property.name or property.asset_id.name,
251 'account_id': property.account_asset_id.id,
252 'debit': amount>0 and amount or 0.0,
253 'credit': amount<0 and -amount or 0.0,
254 'ref': property.asset_id.code,
255 'period_id': period.id,
256 'journal_id': property.journal_id.id,
257 'partner_id': property.asset_id.partner_id.id,
258 'date': time.strftime('%Y-%m-%d'),
260 id2 = self.pool.get('account.move.line').create(cr, uid, {
261 'name': property.name or property.asset_id.name,
263 'account_id': property.account_actif_id.id,
264 'credit': amount>0 and amount or 0.0,
265 'debit': amount<0 and -amount or 0.0,
266 'ref': property.asset_id.code,
267 'period_id': period.id,
268 'journal_id': property.journal_id.id,
269 'partner_id': property.asset_id.partner_id.id,
270 'date': time.strftime('%Y-%m-%d'),
273 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
274 'entry_asset_ids': [(4, id2, False),(4,id,False)]
276 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
277 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
281 def _compute_entries(self, cr, uid, asset, period_id, context={}):
282 #FIXME: function not working CHECK all res
284 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
285 for property in asset.property_ids:
286 if property.state=='open':
287 period = self._compute_period(cr, uid, property, context)
288 if period and (period.date_start<=date_start):
289 result += self._compute_move(cr, uid, property, period, context)
291 account_asset_asset()
293 class account_asset_depreciation_line(osv.osv):
294 _name = 'account.asset.depreciation.line'
295 _description = 'Asset depreciation line'
297 def _get_move_check(self, cr, uid, ids, name, args, context=None):
299 for line in self.browse(cr, uid, ids, context=context):
300 res[line.id] = bool(line.move_id)
304 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
305 'sequence': fields.integer('Sequence of the depreciation', required=True),
306 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
307 'amount': fields.float('Depreciation Amount', required=True),
308 'remaining_value': fields.float('Amount to Depreciate', required=True),
309 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
310 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
311 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
312 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
315 def create_move(self, cr, uid,ids, context=None):
318 asset_obj = self.pool.get('account.asset.asset')
319 period_obj = self.pool.get('account.period')
320 move_obj = self.pool.get('account.move')
321 move_line_obj = self.pool.get('account.move.line')
322 currency_obj = self.pool.get('res.currency')
323 for line in self.browse(cr, uid, ids, context=context):
324 depreciation_date = time.strftime('%Y-%m-%d')
325 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
326 company_currency = line.asset_id.company_id.currency_id.id
327 current_currency = line.asset_id.currency_id.id
328 context.update({'date': depreciation_date})
329 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
330 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
333 'date': depreciation_date,
335 'period_id': period_ids and period_ids[0] or False,
336 'journal_id': line.asset_id.category_id.journal_id.id,
338 move_id = move_obj.create(cr, uid, move_vals, context=context)
339 move_line_obj.create(cr, uid, {
343 'account_id': line.asset_id.category_id.account_depreciation_id.id,
346 'period_id': period_ids and period_ids[0] or False,
347 'journal_id': line.asset_id.category_id.journal_id.id,
348 'partner_id': line.asset_id.partner_id.id,
349 'currency_id': company_currency <> current_currency and current_currency or False,
350 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
351 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
352 'date': depreciation_date,
354 move_line_obj.create(cr, uid, {
358 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
361 'period_id': period_ids and period_ids[0] or False,
362 'journal_id': line.asset_id.category_id.journal_id.id,
363 'partner_id': line.asset_id.partner_id.id,
364 'currency_id': company_currency <> current_currency and current_currency or False,
365 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
366 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
367 'date': depreciation_date,
369 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
372 account_asset_depreciation_line()
374 #class account_asset_property(osv.osv):
375 # def _amount_total(self, cr, uid, ids, name, args, context={}):
376 # id_set=",".join(map(str,ids))
377 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
378 # account_asset_property p
380 # account_move_line l on (p.asset_id=l.asset_id)
381 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
382 # res=dict(cr.fetchall())
384 # res.setdefault(id, 0.0)
387 # def _close(self, cr, uid, property, context={}):
388 # if property.state<>'close':
389 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
392 # property.state='close'
393 # ok = property.asset_id.state=='open'
394 # for prop in property.asset_id.property_ids:
395 # ok = ok and prop.state=='close'
396 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
401 # _name = 'account.asset.property'
402 # _description = 'Asset property'
404 # 'name': fields.char('Method name', size=64, select=1),
405 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
406 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
407 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
408 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
409 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
410 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
411 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
413 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
414 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
415 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
416 # 'method_end': fields.date('Ending date'),
418 # 'date': fields.date('Date created'),
419 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
420 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
421 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
423 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
424 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
425 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
426 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
427 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
428 ## 'note': fields.text('Note'),
432 # 'type': lambda obj, cr, uid, context: 'direct',
433 # 'state': lambda obj, cr, uid, context: 'draft',
434 # 'method': lambda obj, cr, uid, context: 'linear',
435 # 'method_time': lambda obj, cr, uid, context: 'delay',
436 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
437 # 'method_delay': lambda obj, cr, uid, context: 5,
438 # 'method_period': lambda obj, cr, uid, context: 12,
439 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
441 #account_asset_property()
443 class account_move_line(osv.osv):
444 _inherit = 'account.move.line'
446 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
447 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
452 class account_asset_history(osv.osv):
453 _name = 'account.asset.history'
454 _description = 'Asset history'
456 'name': fields.char('History name', size=64, select=1),
457 'user_id': fields.many2one('res.users', 'User', required=True),
458 'date': fields.date('Date', required=True),
459 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
460 'method_delay': fields.integer('Number of interval'),
461 'method_period': fields.integer('Period per interval'),
462 'method_end': fields.date('Ending date'),
463 'note': fields.text('Note'),
466 'date': lambda *args: time.strftime('%Y-%m-%d'),
467 'user_id': lambda self,cr, uid,ctx: uid
469 account_asset_history()
471 class account_asset_board(osv.osv):
472 _name = 'account.asset.board'
473 _description = 'Asset board'
475 'name': fields.char('Asset name', size=64, required=True, select=1),
476 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
477 'value_gross': fields.float('Gross value', required=True, select=1),
478 'value_asset': fields.float('Asset Value', required=True, select=1),
479 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
480 'value_net': fields.float('Net value', required=True, select=1),
486 create or replace view account_asset_board as (
489 min(l.id) as asset_id,
492 0.0 as value_asset_cumul,
497 l.state <> 'draft' and
500 account_asset_board()
502 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: