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),
44 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.category', context=context),
47 account_asset_category()
49 #class one2many_mod_asset(fields.one2many):
51 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
52 # prinasset_property_id if context is None:
59 # #compute depreciation board
60 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
61 # for key, value in depreciation_line_ids.items():
62 # #write values on asset
63 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
64 # return depreciation_line_ids
66 class account_asset_asset(osv.osv):
67 _name = 'account.asset.asset'
68 _description = 'Asset'
70 def _get_period(self, cr, uid, context={}):
71 periods = self.pool.get('account.period').find(cr, uid)
77 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
79 @param id: ids of a account.asset.asset objects
80 @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
83 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
84 FROM account_asset_asset a
85 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
87 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
88 return dict(cr.fetchall())
90 def compute_depreciation_board(self, cr, uid,ids, context=None):
91 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
92 for asset in self.browse(cr, uid, ids, context=context):
93 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
94 if old_depreciation_line_ids:
95 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
97 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
98 salvage_amount = residual_amount = asset.value_residual - asset.salvage_value
99 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
100 day = depreciation_date.day
101 month = depreciation_date.month
102 year = depreciation_date.year
103 for i in range(1,undone_dotation_number+1):
104 if i == undone_dotation_number + 1:
105 amount = residual_amount
107 if asset.method == 'linear':
108 amount = salvage_amount / undone_dotation_number
110 amount = residual_amount * asset.method_progress_factor
111 residual_amount -= amount
114 'asset_id': asset.id,
116 'name': str(asset.id) +'/'+ str(i),
117 'remaining_value': residual_amount,
118 'depreciated_value': salvage_amount - residual_amount,
119 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
121 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
122 month += asset.method_period
123 depreciation_date = datetime(year + (month / 12), month % 12, day)
126 def validate(self, cr, uid, ids, context={}):
127 return self.write(cr, uid, ids, {
133 def _amount_residual(self, cr, uid, ids, name, args, context={}):
135 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
139 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
140 res=dict(cr.fetchall())
141 for asset in self.browse(cr, uid, ids, context):
142 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
144 res.setdefault(id, 0.0)
148 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
149 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
151 'name': fields.char('Asset', size=64, required=True, select=1),
152 'code': fields.char('Reference ', size=16, select=1),
153 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
154 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
155 'company_id': fields.many2one('res.company', 'Company', required=True),
156 'note': fields.text('Note'),
157 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
158 'localisation': fields.char('Localisation', size=32, select=2),
159 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
160 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
161 'purchase_date': fields.date('Purchase Date', required=True),
162 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
163 'active': fields.boolean('Active', select=2),
164 'partner_id': fields.many2one('res.partner', 'Partner'),
166 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
167 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
168 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
169 'method_end': fields.date('Ending date'),
170 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
171 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
172 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
173 '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'),
174 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
175 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
176 'salvage_value': fields.float('Salvage Value', digits_compute=dp.get_precision('Account'), help="It is the remaining value of an asset after it has been fully depreciated."),
179 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
180 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
181 'active': lambda obj, cr, uid, context: True,
182 'state': lambda obj, cr, uid, context: 'draft',
183 'period_id': _get_period,
184 'method': lambda obj, cr, uid, context: 'linear',
185 'method_delay': lambda obj, cr, uid, context: 5,
186 'method_time': lambda obj, cr, uid, context: 'delay',
187 'method_period': lambda obj, cr, uid, context: 12,
188 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
189 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
190 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
194 def _compute_period(self, cr, uid, property, context={}):
195 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
197 if len(property.entry_asset_ids):
198 cp = property.entry_asset_ids[-1].period_id
199 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
200 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
202 current_period = property.asset_id.period_id
203 return current_period
205 def _compute_move(self, cr, uid, property, period, context={}):
206 #FIXME: fucntion not working OK
209 for move in property.asset_id.entry_ids:
210 total += move.debit-move.credit
211 for move in property.entry_asset_ids:
212 if move.account_id == property.account_asset_ids:
214 total += -move.credit
215 periods = (len(property.entry_asset_ids)/2) - property.method_delay
220 if property.method == 'linear':
221 amount = total / periods
223 amount = total * property.method_progress_factor
225 move_id = self.pool.get('account.move').create(cr, uid, {
226 'journal_id': property.journal_id.id,
227 'period_id': period.id,
228 'name': property.name or property.asset_id.name,
229 'ref': property.asset_id.code
232 id = self.pool.get('account.move.line').create(cr, uid, {
233 'name': property.name or property.asset_id.name,
235 'account_id': property.account_asset_id.id,
236 'debit': amount>0 and amount or 0.0,
237 'credit': amount<0 and -amount or 0.0,
238 'ref': property.asset_id.code,
239 'period_id': period.id,
240 'journal_id': property.journal_id.id,
241 'partner_id': property.asset_id.partner_id.id,
242 'date': time.strftime('%Y-%m-%d'),
244 id2 = self.pool.get('account.move.line').create(cr, uid, {
245 'name': property.name or property.asset_id.name,
247 'account_id': property.account_actif_id.id,
248 'credit': amount>0 and amount or 0.0,
249 'debit': amount<0 and -amount or 0.0,
250 'ref': property.asset_id.code,
251 'period_id': period.id,
252 'journal_id': property.journal_id.id,
253 'partner_id': property.asset_id.partner_id.id,
254 'date': time.strftime('%Y-%m-%d'),
257 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
258 'entry_asset_ids': [(4, id2, False),(4,id,False)]
260 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
261 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
265 def _compute_entries(self, cr, uid, asset, period_id, context={}):
266 #FIXME: function not working CHECK all res
268 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
269 for property in asset.property_ids:
270 if property.state=='open':
271 period = self._compute_period(cr, uid, property, context)
272 if period and (period.date_start<=date_start):
273 result += self._compute_move(cr, uid, property, period, context)
275 account_asset_asset()
277 class account_asset_depreciation_line(osv.osv):
278 _name = 'account.asset.depreciation.line'
279 _description = 'Asset depreciation line'
281 def _get_move_check(self, cr, uid, ids, name, args, context=None):
283 for line in self.browse(cr, uid, ids, context=context):
284 res[line.id] = bool(line.move_id)
288 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
289 'sequence': fields.integer('Sequence of the depreciation', required=True),
290 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
291 'amount': fields.float('Depreciation Amount', required=True),
292 'remaining_value': fields.float('Amount to Depreciate', required=True),
293 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
294 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
295 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
296 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
299 def create_move(self, cr, uid,ids, context=None):
302 asset_obj = self.pool.get('account.asset.asset')
303 period_obj = self.pool.get('account.period')
304 move_obj = self.pool.get('account.move')
305 move_line_obj = self.pool.get('account.move.line')
306 currency_obj = self.pool.get('res.currency')
307 for line in self.browse(cr, uid, ids, context=context):
308 depreciation_date = time.strftime('%Y-%m-%d')
309 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
310 company_currency = line.asset_id.company_id.currency_id.id
311 current_currency = line.asset_id.currency_id.id
312 context.update({'date': depreciation_date})
313 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
314 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
317 'date': depreciation_date,
319 'period_id': period_ids and period_ids[0] or False,
320 'journal_id': line.asset_id.category_id.journal_id.id,
322 move_id = move_obj.create(cr, uid, move_vals, context=context)
323 move_line_obj.create(cr, uid, {
327 'account_id': line.asset_id.category_id.account_depreciation_id.id,
330 'period_id': period_ids and period_ids[0] or False,
331 'journal_id': line.asset_id.category_id.journal_id.id,
332 'partner_id': line.asset_id.partner_id.id,
333 'currency_id': company_currency <> current_currency and current_currency or False,
334 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
335 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
336 'date': depreciation_date,
338 move_line_obj.create(cr, uid, {
342 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
345 'period_id': period_ids and period_ids[0] or False,
346 'journal_id': line.asset_id.category_id.journal_id.id,
347 'partner_id': line.asset_id.partner_id.id,
348 'currency_id': company_currency <> current_currency and current_currency or False,
349 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
350 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
351 'date': depreciation_date,
353 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
356 account_asset_depreciation_line()
358 #class account_asset_property(osv.osv):
359 # def _amount_total(self, cr, uid, ids, name, args, context={}):
360 # id_set=",".join(map(str,ids))
361 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
362 # account_asset_property p
364 # account_move_line l on (p.asset_id=l.asset_id)
365 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
366 # res=dict(cr.fetchall())
368 # res.setdefault(id, 0.0)
371 # def _close(self, cr, uid, property, context={}):
372 # if property.state<>'close':
373 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
376 # property.state='close'
377 # ok = property.asset_id.state=='open'
378 # for prop in property.asset_id.property_ids:
379 # ok = ok and prop.state=='close'
380 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
385 # _name = 'account.asset.property'
386 # _description = 'Asset property'
388 # 'name': fields.char('Method name', size=64, select=1),
389 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
390 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
391 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
392 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
393 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
394 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
395 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
397 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
398 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
399 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
400 # 'method_end': fields.date('Ending date'),
402 # 'date': fields.date('Date created'),
403 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
404 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
405 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
407 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
408 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
409 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
410 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
411 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
412 ## 'note': fields.text('Note'),
416 # 'type': lambda obj, cr, uid, context: 'direct',
417 # 'state': lambda obj, cr, uid, context: 'draft',
418 # 'method': lambda obj, cr, uid, context: 'linear',
419 # 'method_time': lambda obj, cr, uid, context: 'delay',
420 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
421 # 'method_delay': lambda obj, cr, uid, context: 5,
422 # 'method_period': lambda obj, cr, uid, context: 12,
423 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
425 #account_asset_property()
427 class account_move_line(osv.osv):
428 _inherit = 'account.move.line'
430 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
431 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
436 class account_asset_history(osv.osv):
437 _name = 'account.asset.history'
438 _description = 'Asset history'
440 'name': fields.char('History name', size=64, select=1),
441 'user_id': fields.many2one('res.users', 'User', required=True),
442 'date': fields.date('Date', required=True),
443 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
444 'method_delay': fields.integer('Number of interval'),
445 'method_period': fields.integer('Period per interval'),
446 'method_end': fields.date('Ending date'),
447 'note': fields.text('Note'),
450 'date': lambda *args: time.strftime('%Y-%m-%d'),
451 'user_id': lambda self,cr, uid,ctx: uid
453 account_asset_history()
455 class account_asset_board(osv.osv):
456 _name = 'account.asset.board'
457 _description = 'Asset board'
459 'name': fields.char('Asset name', size=64, required=True, select=1),
460 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
461 'value_gross': fields.float('Gross value', required=True, select=1),
462 'value_asset': fields.float('Asset Value', required=True, select=1),
463 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
464 'value_net': fields.float('Net value', required=True, select=1),
470 create or replace view account_asset_board as (
473 min(l.id) as asset_id,
476 0.0 as value_asset_cumul,
481 l.state <> 'draft' and
484 account_asset_board()
486 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: