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)
96 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
97 if asset.method == 'linear' and asset.prorata:
98 undone_dotation_number += 1
99 residual_amount = asset.value_residual
100 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
101 day = depreciation_date.day
102 month = depreciation_date.month
103 year = depreciation_date.year
104 total_days = (year % 4) and 365 or 366
105 for i in range(1,undone_dotation_number+1):
106 if i == undone_dotation_number + 1:
107 amount = residual_amount
109 if asset.method == 'linear':
110 amount = asset.purchase_value / asset.method_delay
111 if asset.prorata and i == 1:
112 days = float(total_days) - float(depreciation_date.strftime('%j'))
113 first_amount = amount = (asset.purchase_value / asset.method_delay)/total_days * days
114 elif asset.prorata and i == undone_dotation_number:
115 amount = (asset.purchase_value / asset.method_delay) - first_amount
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 or 1), 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),
209 def _check_prorata(self, cr, uid, ids, context=None):
210 for asset in self.browse(cr, uid, ids, context=context):
211 if asset.prorata and asset.method != 'linear':
216 (_check_prorata, '\nProrata Temporis can be applied only for Linear method.', ['prorata']),
219 def _compute_period(self, cr, uid, property, context={}):
220 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
222 if len(property.entry_asset_ids):
223 cp = property.entry_asset_ids[-1].period_id
224 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
225 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
227 current_period = property.asset_id.period_id
228 return current_period
230 def _compute_move(self, cr, uid, property, period, context={}):
231 #FIXME: fucntion not working OK
234 for move in property.asset_id.entry_ids:
235 total += move.debit-move.credit
236 for move in property.entry_asset_ids:
237 if move.account_id == property.account_asset_ids:
239 total += -move.credit
240 periods = (len(property.entry_asset_ids)/2) - property.method_delay
245 if property.method == 'linear':
246 amount = total / periods
248 amount = total * property.method_progress_factor
250 move_id = self.pool.get('account.move').create(cr, uid, {
251 'journal_id': property.journal_id.id,
252 'period_id': period.id,
253 'name': property.name or property.asset_id.name,
254 'ref': property.asset_id.code
257 id = self.pool.get('account.move.line').create(cr, uid, {
258 'name': property.name or property.asset_id.name,
260 'account_id': property.account_asset_id.id,
261 'debit': amount>0 and amount or 0.0,
262 'credit': amount<0 and -amount or 0.0,
263 'ref': property.asset_id.code,
264 'period_id': period.id,
265 'journal_id': property.journal_id.id,
266 'partner_id': property.asset_id.partner_id.id,
267 'date': time.strftime('%Y-%m-%d'),
269 id2 = self.pool.get('account.move.line').create(cr, uid, {
270 'name': property.name or property.asset_id.name,
272 'account_id': property.account_actif_id.id,
273 'credit': amount>0 and amount or 0.0,
274 'debit': amount<0 and -amount or 0.0,
275 'ref': property.asset_id.code,
276 'period_id': period.id,
277 'journal_id': property.journal_id.id,
278 'partner_id': property.asset_id.partner_id.id,
279 'date': time.strftime('%Y-%m-%d'),
282 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
283 'entry_asset_ids': [(4, id2, False),(4,id,False)]
285 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
286 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
290 def _compute_entries(self, cr, uid, asset, period_id, context={}):
291 #FIXME: function not working CHECK all res
293 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
294 for property in asset.property_ids:
295 if property.state=='open':
296 period = self._compute_period(cr, uid, property, context)
297 if period and (period.date_start<=date_start):
298 result += self._compute_move(cr, uid, property, period, context)
300 account_asset_asset()
302 class account_asset_depreciation_line(osv.osv):
303 _name = 'account.asset.depreciation.line'
304 _description = 'Asset depreciation line'
306 def _get_move_check(self, cr, uid, ids, name, args, context=None):
308 for line in self.browse(cr, uid, ids, context=context):
309 res[line.id] = bool(line.move_id)
313 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
314 'sequence': fields.integer('Sequence of the depreciation', required=True),
315 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
316 'amount': fields.float('Depreciation Amount', required=True),
317 'remaining_value': fields.float('Amount to Depreciate', required=True),
318 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
319 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
320 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
321 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
324 def create_move(self, cr, uid,ids, context=None):
327 asset_obj = self.pool.get('account.asset.asset')
328 period_obj = self.pool.get('account.period')
329 move_obj = self.pool.get('account.move')
330 move_line_obj = self.pool.get('account.move.line')
331 currency_obj = self.pool.get('res.currency')
332 for line in self.browse(cr, uid, ids, context=context):
333 depreciation_date = line.asset_id.prorata and line.asset_id.purchase_date or time.strftime('%Y-%m-%d')
334 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
335 company_currency = line.asset_id.company_id.currency_id.id
336 current_currency = line.asset_id.currency_id.id
337 context.update({'date': depreciation_date})
338 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
339 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
342 'date': depreciation_date,
344 'period_id': period_ids and period_ids[0] or False,
345 'journal_id': line.asset_id.category_id.journal_id.id,
347 move_id = move_obj.create(cr, uid, move_vals, context=context)
348 move_line_obj.create(cr, uid, {
352 'account_id': line.asset_id.category_id.account_depreciation_id.id,
355 'period_id': period_ids and period_ids[0] or False,
356 'journal_id': line.asset_id.category_id.journal_id.id,
357 'partner_id': line.asset_id.partner_id.id,
358 'currency_id': company_currency <> current_currency and current_currency or False,
359 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
360 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
361 'date': depreciation_date,
363 move_line_obj.create(cr, uid, {
367 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
370 'period_id': period_ids and period_ids[0] or False,
371 'journal_id': line.asset_id.category_id.journal_id.id,
372 'partner_id': line.asset_id.partner_id.id,
373 'currency_id': company_currency <> current_currency and current_currency or False,
374 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
375 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
376 'date': depreciation_date,
378 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
381 account_asset_depreciation_line()
383 #class account_asset_property(osv.osv):
384 # def _amount_total(self, cr, uid, ids, name, args, context={}):
385 # id_set=",".join(map(str,ids))
386 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
387 # account_asset_property p
389 # account_move_line l on (p.asset_id=l.asset_id)
390 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
391 # res=dict(cr.fetchall())
393 # res.setdefault(id, 0.0)
396 # def _close(self, cr, uid, property, context={}):
397 # if property.state<>'close':
398 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
401 # property.state='close'
402 # ok = property.asset_id.state=='open'
403 # for prop in property.asset_id.property_ids:
404 # ok = ok and prop.state=='close'
405 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
410 # _name = 'account.asset.property'
411 # _description = 'Asset property'
413 # 'name': fields.char('Method name', size=64, select=1),
414 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
415 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
416 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
417 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
418 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
419 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
420 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
422 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
423 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
424 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
425 # 'method_end': fields.date('Ending date'),
427 # 'date': fields.date('Date created'),
428 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
429 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
430 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
432 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
433 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
434 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
435 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
436 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
437 ## 'note': fields.text('Note'),
441 # 'type': lambda obj, cr, uid, context: 'direct',
442 # 'state': lambda obj, cr, uid, context: 'draft',
443 # 'method': lambda obj, cr, uid, context: 'linear',
444 # 'method_time': lambda obj, cr, uid, context: 'delay',
445 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
446 # 'method_delay': lambda obj, cr, uid, context: 5,
447 # 'method_period': lambda obj, cr, uid, context: 12,
448 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
450 #account_asset_property()
452 class account_move_line(osv.osv):
453 _inherit = 'account.move.line'
455 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
456 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
461 class account_asset_history(osv.osv):
462 _name = 'account.asset.history'
463 _description = 'Asset history'
465 'name': fields.char('History name', size=64, select=1),
466 'user_id': fields.many2one('res.users', 'User', required=True),
467 'date': fields.date('Date', required=True),
468 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
469 'method_delay': fields.integer('Number of interval'),
470 'method_period': fields.integer('Period per interval'),
471 'method_end': fields.date('Ending date'),
472 'note': fields.text('Note'),
475 'date': lambda *args: time.strftime('%Y-%m-%d'),
476 'user_id': lambda self,cr, uid,ctx: uid
478 account_asset_history()
480 class account_asset_board(osv.osv):
481 _name = 'account.asset.board'
482 _description = 'Asset board'
484 'name': fields.char('Asset name', size=64, required=True, select=1),
485 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
486 'value_gross': fields.float('Gross value', required=True, select=1),
487 'value_asset': fields.float('Asset Value', required=True, select=1),
488 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
489 'value_net': fields.float('Net value', required=True, select=1),
495 create or replace view account_asset_board as (
498 min(l.id) as asset_id,
501 0.0 as value_asset_cumul,
506 l.state <> 'draft' and
509 account_asset_board()
511 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: