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 account_asset_category()
48 #class one2many_mod_asset(fields.one2many):
50 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
51 # prinasset_property_id if context is None:
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
65 class account_asset_asset(osv.osv):
66 _name = 'account.asset.asset'
67 _description = 'Asset'
69 def _get_period(self, cr, uid, context={}):
70 periods = self.pool.get('account.period').find(cr, uid)
76 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
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
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)
86 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
87 return dict(cr.fetchall())
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 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
96 residual_amount = asset.value_residual
97 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
98 day = depreciation_date.day
99 month = depreciation_date.month
100 year = depreciation_date.year
101 for i in range(1,undone_dotation_number+1):
102 if i == undone_dotation_number + 1:
103 amount = residual_amount
105 if asset.method == 'linear':
106 amount = asset.value_residual / undone_dotation_number
108 amount = residual_amount * asset.method_progress_factor
109 residual_amount -= amount
112 'asset_id': asset.id,
114 'name': str(asset.id) +'/'+ str(i),
115 'remaining_value': residual_amount,
116 'depreciated_value': asset.purchase_value - residual_amount,
117 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
119 depreciation_lin_obj.create(cr, uid, vals, context=context)
120 month += asset.method_period
121 depreciation_date = datetime(year + (month / 12), month % 12, day)
124 def validate(self, cr, uid, ids, context={}):
125 return self.write(cr, uid, ids, {
129 def _amount_total(self, cr, uid, ids, name, args, context={}):
130 #FIXME: function not working²
131 id_set=",".join(map(str,ids))
132 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
134 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
135 res=dict(cr.fetchall())
137 res.setdefault(id, 0.0)
140 def _amount_residual(self, cr, uid, ids, name, args, context={}):
142 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
146 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
147 res=dict(cr.fetchall())
148 for asset in self.browse(cr, uid, ids, context):
149 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
151 res.setdefault(id, 0.0)
155 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
156 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
158 'name': fields.char('Asset', size=64, required=True, select=1),
159 'code': fields.char('Reference ', size=16, select=1),
160 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
161 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
162 'company_id': fields.many2one('res.company', 'Company', required=True),
163 'note': fields.text('Note'),
164 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
165 'localisation': fields.char('Localisation', size=32, select=2),
166 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
167 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
168 'purchase_date': fields.date('Purchase Date', required=True),
169 'state': fields.selection([('draft','Draft'),('open','Running'),('close','Close')], 'state', required=True),
170 'active': fields.boolean('Active', select=2),
171 'partner_id': fields.many2one('res.partner', 'Partner'),
173 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
174 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
175 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
176 'method_end': fields.date('Ending date'),
177 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
178 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
179 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
180 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
181 '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'),
182 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
183 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
186 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
187 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
188 'active': lambda obj, cr, uid, context: True,
189 'state': lambda obj, cr, uid, context: 'draft',
190 'period_id': _get_period,
191 'method': lambda obj, cr, uid, context: 'linear',
192 'method_delay': lambda obj, cr, uid, context: 5,
193 'method_time': lambda obj, cr, uid, context: 'delay',
194 'method_period': lambda obj, cr, uid, context: 12,
195 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
196 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
197 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
201 def _compute_period(self, cr, uid, property, context={}):
202 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
204 if len(property.entry_asset_ids):
205 cp = property.entry_asset_ids[-1].period_id
206 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
207 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
209 current_period = property.asset_id.period_id
210 return current_period
212 def _compute_move(self, cr, uid, property, period, context={}):
213 #FIXME: fucntion not working OK
216 for move in property.asset_id.entry_ids:
217 total += move.debit-move.credit
218 for move in property.entry_asset_ids:
219 if move.account_id == property.account_asset_ids:
221 total += -move.credit
222 periods = (len(property.entry_asset_ids)/2) - property.method_delay
227 if property.method == 'linear':
228 amount = total / periods
230 amount = total * property.method_progress_factor
232 move_id = self.pool.get('account.move').create(cr, uid, {
233 'journal_id': property.journal_id.id,
234 'period_id': period.id,
235 'name': property.name or property.asset_id.name,
236 'ref': property.asset_id.code
239 id = self.pool.get('account.move.line').create(cr, uid, {
240 'name': property.name or property.asset_id.name,
242 'account_id': property.account_asset_id.id,
243 'debit': amount>0 and amount or 0.0,
244 'credit': amount<0 and -amount or 0.0,
245 'ref': property.asset_id.code,
246 'period_id': period.id,
247 'journal_id': property.journal_id.id,
248 'partner_id': property.asset_id.partner_id.id,
249 'date': time.strftime('%Y-%m-%d'),
251 id2 = self.pool.get('account.move.line').create(cr, uid, {
252 'name': property.name or property.asset_id.name,
254 'account_id': property.account_actif_id.id,
255 'credit': amount>0 and amount or 0.0,
256 'debit': amount<0 and -amount or 0.0,
257 'ref': property.asset_id.code,
258 'period_id': period.id,
259 'journal_id': property.journal_id.id,
260 'partner_id': property.asset_id.partner_id.id,
261 'date': time.strftime('%Y-%m-%d'),
264 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
265 'entry_asset_ids': [(4, id2, False),(4,id,False)]
267 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
268 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
272 def _compute_entries(self, cr, uid, asset, period_id, context={}):
273 #FIXME: function not working CHECK all res
275 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
276 for property in asset.property_ids:
277 if property.state=='open':
278 period = self._compute_period(cr, uid, property, context)
279 if period and (period.date_start<=date_start):
280 result += self._compute_move(cr, uid, property, period, context)
282 account_asset_asset()
284 class account_asset_depreciation_line(osv.osv):
285 _name = 'account.asset.depreciation.line'
286 _description = 'Asset depreciation line'
288 def _get_move_check(self, cr, uid, ids, name, args, context=None):
290 for line in self.browse(cr, uid, ids, context=context):
291 res[line.id] = bool(line.move_id)
295 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
296 'sequence': fields.integer('Sequence of the depreciation', required=True),
297 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
298 'amount': fields.float('Depreciation Amount', required=True),
299 'remaining_value': fields.float('Amount to Depreciate', required=True),
300 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
301 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
302 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
303 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Posted', store=True)
306 def create_move(self, cr, uid,ids, context=None):
309 asset_obj = self.pool.get('account.asset.asset')
310 period_obj = self.pool.get('account.period')
311 move_obj = self.pool.get('account.move')
312 move_line_obj = self.pool.get('account.move.line')
313 currency_obj = self.pool.get('res.currency')
314 for line in self.browse(cr, uid, ids, context=context):
315 depreciation_date = time.strftime('%Y-%m-%d')
316 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
317 company_currency = line.asset_id.company_id.currency_id.id
318 current_currency = line.asset_id.currency_id.id
319 context.update({'date': depreciation_date})
320 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
321 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
324 'date': depreciation_date,
326 'period_id': period_ids and period_ids[0] or False,
327 'journal_id': line.asset_id.category_id.journal_id.id,
329 move_id = move_obj.create(cr, uid, move_vals, context=context)
330 move_line_obj.create(cr, uid, {
334 'account_id': line.asset_id.category_id.account_depreciation_id.id,
337 'period_id': period_ids and period_ids[0] or False,
338 'journal_id': line.asset_id.category_id.journal_id.id,
339 'partner_id': line.asset_id.partner_id.id,
340 'currency_id': company_currency <> current_currency and current_currency or False,
341 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
342 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
343 'date': depreciation_date,
345 move_line_obj.create(cr, uid, {
349 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
352 'period_id': period_ids and period_ids[0] or False,
353 'journal_id': line.asset_id.category_id.journal_id.id,
354 'partner_id': line.asset_id.partner_id.id,
355 'currency_id': company_currency <> current_currency and current_currency or False,
356 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
357 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
358 'date': depreciation_date,
360 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
363 account_asset_depreciation_line()
365 #class account_asset_property(osv.osv):
366 # def _amount_total(self, cr, uid, ids, name, args, context={}):
367 # id_set=",".join(map(str,ids))
368 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
369 # account_asset_property p
371 # account_move_line l on (p.asset_id=l.asset_id)
372 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
373 # res=dict(cr.fetchall())
375 # res.setdefault(id, 0.0)
378 # def _close(self, cr, uid, property, context={}):
379 # if property.state<>'close':
380 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
383 # property.state='close'
384 # ok = property.asset_id.state=='open'
385 # for prop in property.asset_id.property_ids:
386 # ok = ok and prop.state=='close'
387 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
392 # _name = 'account.asset.property'
393 # _description = 'Asset property'
395 # 'name': fields.char('Method name', size=64, select=1),
396 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
397 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
398 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
399 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
400 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
401 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
402 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
404 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
405 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
406 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
407 # 'method_end': fields.date('Ending date'),
409 # 'date': fields.date('Date created'),
410 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
411 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
412 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
414 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
415 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
416 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
417 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
418 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
419 ## 'note': fields.text('Note'),
423 # 'type': lambda obj, cr, uid, context: 'direct',
424 # 'state': lambda obj, cr, uid, context: 'draft',
425 # 'method': lambda obj, cr, uid, context: 'linear',
426 # 'method_time': lambda obj, cr, uid, context: 'delay',
427 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
428 # 'method_delay': lambda obj, cr, uid, context: 5,
429 # 'method_period': lambda obj, cr, uid, context: 12,
430 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
432 #account_asset_property()
434 class account_move_line(osv.osv):
435 _inherit = 'account.move.line'
437 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
438 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
443 class account_asset_history(osv.osv):
444 _name = 'account.asset.history'
445 _description = 'Asset history'
447 'name': fields.char('History name', size=64, select=1),
448 'user_id': fields.many2one('res.users', 'User', required=True),
449 'date': fields.date('Date', required=True),
450 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
451 'method_delay': fields.integer('Number of interval'),
452 'method_period': fields.integer('Period per interval'),
453 'method_end': fields.date('Ending date'),
454 'note': fields.text('Note'),
457 'date': lambda *args: time.strftime('%Y-%m-%d'),
458 'user_id': lambda self,cr, uid,ctx: uid
460 account_asset_history()
462 class account_asset_board(osv.osv):
463 _name = 'account.asset.board'
464 _description = 'Asset board'
466 'name': fields.char('Asset name', size=64, required=True, select=1),
467 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
468 'value_gross': fields.float('Gross value', required=True, select=1),
469 'value_asset': fields.float('Asset Value', required=True, select=1),
470 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
471 'value_net': fields.float('Net value', required=True, select=1),
477 create or replace view account_asset_board as (
480 min(l.id) as asset_id,
483 0.0 as value_asset_cumul,
488 l.state <> 'draft' and
491 account_asset_board()
493 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: