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_depreciation_id(self, cr, uid, ids, account_asset_id, context=None):
49 account_depreciation_id = self.pool.get('account.account').browse(cr, uid, account_asset_id,
52 'account_depreciation_id': account_depreciation_id,
56 account_asset_category()
58 #class one2many_mod_asset(fields.one2many):
60 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
61 # prinasset_property_id if context is None:
68 # #compute depreciation board
69 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
70 # for key, value in depreciation_line_ids.items():
71 # #write values on asset
72 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
73 # return depreciation_line_ids
75 class account_asset_asset(osv.osv):
76 _name = 'account.asset.asset'
77 _description = 'Asset'
79 def _get_period(self, cr, uid, context={}):
80 periods = self.pool.get('account.period').find(cr, uid)
86 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
88 @param id: ids of a account.asset.asset objects
89 @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
92 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
93 FROM account_asset_asset a
94 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
96 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
97 return dict(cr.fetchall())
99 def compute_depreciation_board(self, cr, uid,ids, context=None):
100 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
101 for asset in self.browse(cr, uid, ids, context=context):
102 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
103 if old_depreciation_line_ids:
104 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
106 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
107 residual_amount = asset.value_residual
108 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
109 day = depreciation_date.day
110 month = depreciation_date.month
111 year = depreciation_date.year
112 for i in range(1,undone_dotation_number+1):
113 if i == undone_dotation_number + 1:
114 amount = residual_amount
116 if asset.method == 'linear':
117 amount = asset.purchase_value / undone_dotation_number
119 amount = residual_amount * asset.method_progress_factor
120 residual_amount -= amount
123 'asset_id': asset.id,
125 'name': str(asset.id) +'/'+ str(i),
126 'remaining_value': residual_amount,
127 'depreciated_value': asset.purchase_value - residual_amount,
128 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
130 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
131 month += asset.method_period
132 depreciation_date = datetime(year + (month / 12), month % 12, day)
135 def validate(self, cr, uid, ids, context={}):
136 return self.write(cr, uid, ids, {
140 def _amount_total(self, cr, uid, ids, name, args, context={}):
141 #FIXME: function not working²
142 id_set=",".join(map(str,ids))
143 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
145 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
146 res=dict(cr.fetchall())
148 res.setdefault(id, 0.0)
151 def _amount_residual(self, cr, uid, ids, name, args, context={}):
153 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
157 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
158 res=dict(cr.fetchall())
159 for asset in self.browse(cr, uid, ids, context):
160 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
162 res.setdefault(id, 0.0)
166 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
167 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
169 'name': fields.char('Asset', size=64, required=True, select=1),
170 'code': fields.char('Reference ', size=16, select=1),
171 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
172 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
173 'company_id': fields.many2one('res.company', 'Company', required=True),
174 'note': fields.text('Note'),
175 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
176 'localisation': fields.char('Localisation', size=32, select=2),
177 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
178 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
179 'purchase_date': fields.date('Purchase Date', required=True),
180 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
181 'active': fields.boolean('Active', select=2),
182 'partner_id': fields.many2one('res.partner', 'Partner'),
184 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
185 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
186 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
187 'method_end': fields.date('Ending date'),
188 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
189 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
190 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
191 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
192 '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'),
193 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
194 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
197 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
198 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
199 'active': lambda obj, cr, uid, context: True,
200 'state': lambda obj, cr, uid, context: 'draft',
201 'period_id': _get_period,
202 'method': lambda obj, cr, uid, context: 'linear',
203 'method_delay': lambda obj, cr, uid, context: 5,
204 'method_time': lambda obj, cr, uid, context: 'delay',
205 'method_period': lambda obj, cr, uid, context: 12,
206 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
207 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
208 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
212 def _compute_period(self, cr, uid, property, context={}):
213 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
215 if len(property.entry_asset_ids):
216 cp = property.entry_asset_ids[-1].period_id
217 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
218 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
220 current_period = property.asset_id.period_id
221 return current_period
223 def _compute_move(self, cr, uid, property, period, context={}):
224 #FIXME: fucntion not working OK
227 for move in property.asset_id.entry_ids:
228 total += move.debit-move.credit
229 for move in property.entry_asset_ids:
230 if move.account_id == property.account_asset_ids:
232 total += -move.credit
233 periods = (len(property.entry_asset_ids)/2) - property.method_delay
238 if property.method == 'linear':
239 amount = total / periods
241 amount = total * property.method_progress_factor
243 move_id = self.pool.get('account.move').create(cr, uid, {
244 'journal_id': property.journal_id.id,
245 'period_id': period.id,
246 'name': property.name or property.asset_id.name,
247 'ref': property.asset_id.code
250 id = self.pool.get('account.move.line').create(cr, uid, {
251 'name': property.name or property.asset_id.name,
253 'account_id': property.account_asset_id.id,
254 'debit': amount>0 and amount or 0.0,
255 'credit': amount<0 and -amount or 0.0,
256 'ref': property.asset_id.code,
257 'period_id': period.id,
258 'journal_id': property.journal_id.id,
259 'partner_id': property.asset_id.partner_id.id,
260 'date': time.strftime('%Y-%m-%d'),
262 id2 = self.pool.get('account.move.line').create(cr, uid, {
263 'name': property.name or property.asset_id.name,
265 'account_id': property.account_actif_id.id,
266 'credit': amount>0 and amount or 0.0,
267 'debit': amount<0 and -amount or 0.0,
268 'ref': property.asset_id.code,
269 'period_id': period.id,
270 'journal_id': property.journal_id.id,
271 'partner_id': property.asset_id.partner_id.id,
272 'date': time.strftime('%Y-%m-%d'),
275 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
276 'entry_asset_ids': [(4, id2, False),(4,id,False)]
278 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
279 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
283 def _compute_entries(self, cr, uid, asset, period_id, context={}):
284 #FIXME: function not working CHECK all res
286 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
287 for property in asset.property_ids:
288 if property.state=='open':
289 period = self._compute_period(cr, uid, property, context)
290 if period and (period.date_start<=date_start):
291 result += self._compute_move(cr, uid, property, period, context)
293 account_asset_asset()
295 class account_asset_depreciation_line(osv.osv):
296 _name = 'account.asset.depreciation.line'
297 _description = 'Asset depreciation line'
299 def _get_move_check(self, cr, uid, ids, name, args, context=None):
301 for line in self.browse(cr, uid, ids, context=context):
302 res[line.id] = bool(line.move_id)
306 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
307 'sequence': fields.integer('Sequence of the depreciation', required=True),
308 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
309 'amount': fields.float('Depreciation Amount', required=True),
310 'remaining_value': fields.float('Amount to Depreciate', required=True),
311 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
312 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
313 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
314 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
317 def create_move(self, cr, uid,ids, context=None):
320 asset_obj = self.pool.get('account.asset.asset')
321 period_obj = self.pool.get('account.period')
322 move_obj = self.pool.get('account.move')
323 move_line_obj = self.pool.get('account.move.line')
324 currency_obj = self.pool.get('res.currency')
325 for line in self.browse(cr, uid, ids, context=context):
326 depreciation_date = time.strftime('%Y-%m-%d')
327 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
328 company_currency = line.asset_id.company_id.currency_id.id
329 current_currency = line.asset_id.currency_id.id
330 context.update({'date': depreciation_date})
331 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
332 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
335 'date': depreciation_date,
337 'period_id': period_ids and period_ids[0] or False,
338 'journal_id': line.asset_id.category_id.journal_id.id,
340 move_id = move_obj.create(cr, uid, move_vals, context=context)
341 move_line_obj.create(cr, uid, {
345 'account_id': line.asset_id.category_id.account_depreciation_id.id,
348 'period_id': period_ids and period_ids[0] or False,
349 'journal_id': line.asset_id.category_id.journal_id.id,
350 'partner_id': line.asset_id.partner_id.id,
351 'currency_id': company_currency <> current_currency and current_currency or False,
352 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
353 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
354 'date': depreciation_date,
356 move_line_obj.create(cr, uid, {
360 'account_id': line.asset_id.category_id.account_expense_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 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
374 account_asset_depreciation_line()
376 #class account_asset_property(osv.osv):
377 # def _amount_total(self, cr, uid, ids, name, args, context={}):
378 # id_set=",".join(map(str,ids))
379 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
380 # account_asset_property p
382 # account_move_line l on (p.asset_id=l.asset_id)
383 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
384 # res=dict(cr.fetchall())
386 # res.setdefault(id, 0.0)
389 # def _close(self, cr, uid, property, context={}):
390 # if property.state<>'close':
391 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
394 # property.state='close'
395 # ok = property.asset_id.state=='open'
396 # for prop in property.asset_id.property_ids:
397 # ok = ok and prop.state=='close'
398 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
403 # _name = 'account.asset.property'
404 # _description = 'Asset property'
406 # 'name': fields.char('Method name', size=64, select=1),
407 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
408 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
409 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
410 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
411 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
412 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
413 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
415 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
416 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
417 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
418 # 'method_end': fields.date('Ending date'),
420 # 'date': fields.date('Date created'),
421 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
422 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
423 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
425 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
426 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
427 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
428 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
429 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
430 ## 'note': fields.text('Note'),
434 # 'type': lambda obj, cr, uid, context: 'direct',
435 # 'state': lambda obj, cr, uid, context: 'draft',
436 # 'method': lambda obj, cr, uid, context: 'linear',
437 # 'method_time': lambda obj, cr, uid, context: 'delay',
438 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
439 # 'method_delay': lambda obj, cr, uid, context: 5,
440 # 'method_period': lambda obj, cr, uid, context: 12,
441 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
443 #account_asset_property()
445 class account_move_line(osv.osv):
446 _inherit = 'account.move.line'
448 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
449 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
454 class account_asset_history(osv.osv):
455 _name = 'account.asset.history'
456 _description = 'Asset history'
458 'name': fields.char('History name', size=64, select=1),
459 'user_id': fields.many2one('res.users', 'User', required=True),
460 'date': fields.date('Date', required=True),
461 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
462 'method_delay': fields.integer('Number of interval'),
463 'method_period': fields.integer('Period per interval'),
464 'method_end': fields.date('Ending date'),
465 'note': fields.text('Note'),
468 'date': lambda *args: time.strftime('%Y-%m-%d'),
469 'user_id': lambda self,cr, uid,ctx: uid
471 account_asset_history()
473 class account_asset_board(osv.osv):
474 _name = 'account.asset.board'
475 _description = 'Asset board'
477 'name': fields.char('Asset name', size=64, required=True, select=1),
478 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
479 'value_gross': fields.float('Gross value', required=True, select=1),
480 'value_asset': fields.float('Asset Value', required=True, select=1),
481 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
482 'value_net': fields.float('Net value', required=True, select=1),
488 create or replace view account_asset_board as (
491 min(l.id) as asset_id,
494 0.0 as value_asset_cumul,
499 l.state <> 'draft' and
502 account_asset_board()
504 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: