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'
30 'name': fields.char('Name', size=64, required=True, select=1),
31 'note': fields.text('Note'),
32 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic Journal'), #FIXME:add in the form view with group = analytic
33 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'), #FIXME:add in the form view with group = analytic
34 'account_asset_id': fields.many2one('account.account', 'Asset Account', required=True),
35 'account_depreciation_id': fields.many2one('account.account', 'Depreciation Account', required=True),
36 'account_expense_depreciation_id': fields.many2one('account.account', 'Depr. Expense Account', required=True),
37 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
38 'company_id': fields.many2one('res.company', 'Company'),
40 account_asset_category()
42 #class one2many_mod_asset(fields.one2many):
44 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
45 # prinasset_property_id if context is None:
52 # #compute depreciation board
53 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
54 # for key, value in depreciation_line_ids.items():
55 # #write values on asset
56 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
57 # return depreciation_line_ids
59 class account_asset_asset(osv.osv):
60 _name = 'account.asset.asset'
61 _description = 'Asset'
63 def _get_period(self, cr, uid, context={}):
64 periods = self.pool.get('account.period').find(cr, uid)
70 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
72 @param id: ids of a account.asset.asset objects
73 @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
76 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
77 FROM account_asset_asset a
78 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
80 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
81 return dict(cr.fetchall())
83 def compute_depreciation_board(self, cr, uid,ids, context=None):
84 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
85 for asset in self.browse(cr, uid, ids, context=context):
86 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
87 if old_depreciation_line_ids:
88 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
90 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
91 residual_amount = asset.value_residual
92 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
93 day = depreciation_date.day
94 month = depreciation_date.month
95 year = depreciation_date.year
96 for i in range(1,undone_dotation_number+1):
97 if i == undone_dotation_number + 1:
98 amount = residual_amount
100 if asset.method == 'linear':
101 amount = asset.purchase_value / undone_dotation_number
103 amount = residual_amount * asset.method_progress_factor
104 residual_amount -= amount
107 'asset_id': asset.id,
109 'name': str(asset.id) +'/'+ str(i),
110 'remaining_value': residual_amount,
111 'depreciated_value': asset.purchase_value - residual_amount,
112 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
114 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
115 month += asset.method_period
116 depreciation_date = datetime(year + (month / 12), month % 12, day)
119 def validate(self, cr, uid, ids, context={}):
120 return self.write(cr, uid, ids, {
124 def _amount_total(self, cr, uid, ids, name, args, context={}):
125 #FIXME: function not working²
126 id_set=",".join(map(str,ids))
127 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
129 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
130 res=dict(cr.fetchall())
132 res.setdefault(id, 0.0)
135 def _amount_residual(self, cr, uid, ids, name, args, context={}):
137 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
141 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
142 res=dict(cr.fetchall())
143 for asset in self.browse(cr, uid, ids, context):
144 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
146 res.setdefault(id, 0.0)
150 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
151 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
153 'name': fields.char('Asset', size=64, required=True, select=1),
154 'code': fields.char('Reference ', size=16, select=1),
155 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
156 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
157 'company_id': fields.many2one('res.company', 'Company', required=True),
158 'note': fields.text('Note'),
159 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
160 'localisation': fields.char('Localisation', size=32, select=2),
161 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
162 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
163 'purchase_date': fields.date('Purchase Date', required=True),
164 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
165 'active': fields.boolean('Active', select=2),
166 'partner_id': fields.many2one('res.partner', 'Partner'),
168 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
169 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
170 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
171 'method_end': fields.date('Ending date'),
172 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
173 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
174 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
175 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
176 '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'),
177 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
178 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
181 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
182 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
183 'active': lambda obj, cr, uid, context: True,
184 'state': lambda obj, cr, uid, context: 'draft',
185 'period_id': _get_period,
186 'method': lambda obj, cr, uid, context: 'linear',
187 'method_delay': lambda obj, cr, uid, context: 5,
188 'method_time': lambda obj, cr, uid, context: 'delay',
189 'method_period': lambda obj, cr, uid, context: 12,
190 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
191 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
195 def _compute_period(self, cr, uid, property, context={}):
196 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
198 if len(property.entry_asset_ids):
199 cp = property.entry_asset_ids[-1].period_id
200 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
201 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
203 current_period = property.asset_id.period_id
204 return current_period
206 def _compute_move(self, cr, uid, property, period, context={}):
207 #FIXME: fucntion not working OK
210 for move in property.asset_id.entry_ids:
211 total += move.debit-move.credit
212 for move in property.entry_asset_ids:
213 if move.account_id == property.account_asset_ids:
215 total += -move.credit
216 periods = (len(property.entry_asset_ids)/2) - property.method_delay
221 if property.method == 'linear':
222 amount = total / periods
224 amount = total * property.method_progress_factor
226 move_id = self.pool.get('account.move').create(cr, uid, {
227 'journal_id': property.journal_id.id,
228 'period_id': period.id,
229 'name': property.name or property.asset_id.name,
230 'ref': property.asset_id.code
233 id = self.pool.get('account.move.line').create(cr, uid, {
234 'name': property.name or property.asset_id.name,
236 'account_id': property.account_asset_id.id,
237 'debit': amount>0 and amount or 0.0,
238 'credit': amount<0 and -amount or 0.0,
239 'ref': property.asset_id.code,
240 'period_id': period.id,
241 'journal_id': property.journal_id.id,
242 'partner_id': property.asset_id.partner_id.id,
243 'date': time.strftime('%Y-%m-%d'),
245 id2 = self.pool.get('account.move.line').create(cr, uid, {
246 'name': property.name or property.asset_id.name,
248 'account_id': property.account_actif_id.id,
249 'credit': amount>0 and amount or 0.0,
250 'debit': amount<0 and -amount or 0.0,
251 'ref': property.asset_id.code,
252 'period_id': period.id,
253 'journal_id': property.journal_id.id,
254 'partner_id': property.asset_id.partner_id.id,
255 'date': time.strftime('%Y-%m-%d'),
258 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
259 'entry_asset_ids': [(4, id2, False),(4,id,False)]
261 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
262 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
266 def _compute_entries(self, cr, uid, asset, period_id, context={}):
267 #FIXME: function not working CHECK all res
269 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
270 for property in asset.property_ids:
271 if property.state=='open':
272 period = self._compute_period(cr, uid, property, context)
273 if period and (period.date_start<=date_start):
274 result += self._compute_move(cr, uid, property, period, context)
276 account_asset_asset()
278 class account_asset_depreciation_line(osv.osv):
279 _name = 'account.asset.depreciation.line'
280 _description = 'Asset depreciation line'
282 def _get_move_check(self, cr, uid, ids, name, args, context=None):
284 for line in self.browse(cr, uid, ids, context=context):
285 res[line.id] = bool(line.move_id)
289 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
290 'sequence': fields.integer('Sequence of the depreciation', required=True),
291 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
292 'amount': fields.float('Depreciation Amount', required=True),
293 'remaining_value': fields.float('Amount to Depreciate', required=True),
294 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
295 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
296 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
297 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
300 def create_move(self, cr, uid,ids, context=None):
303 asset_obj = self.pool.get('account.asset.asset')
304 period_obj = self.pool.get('account.period')
305 move_obj = self.pool.get('account.move')
306 move_line_obj = self.pool.get('account.move.line')
307 currency_obj = self.pool.get('res.currency')
308 for line in self.browse(cr, uid, ids, context=context):
309 depreciation_date = time.strftime('%Y-%m-%d')
310 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
311 company_currency = line.asset_id.company_id.currency_id.id
312 current_currency = line.asset_id.currency_id.id
313 context.update({'date': depreciation_date})
314 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
315 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
318 'date': depreciation_date,
320 'period_id': period_ids and period_ids[0] or False,
321 'journal_id': line.asset_id.category_id.journal_id.id,
323 move_id = move_obj.create(cr, uid, move_vals, context=context)
324 move_line_obj.create(cr, uid, {
328 'account_id': line.asset_id.category_id.account_depreciation_id.id,
331 'period_id': period_ids and period_ids[0] or False,
332 'journal_id': line.asset_id.category_id.journal_id.id,
333 'partner_id': line.asset_id.partner_id.id,
334 'currency_id': company_currency <> current_currency and current_currency or False,
335 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
336 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
337 'date': depreciation_date,
339 move_line_obj.create(cr, uid, {
343 'account_id': line.asset_id.category_id.account_expense_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 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
357 account_asset_depreciation_line()
359 #class account_asset_property(osv.osv):
360 # def _amount_total(self, cr, uid, ids, name, args, context={}):
361 # id_set=",".join(map(str,ids))
362 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
363 # account_asset_property p
365 # account_move_line l on (p.asset_id=l.asset_id)
366 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
367 # res=dict(cr.fetchall())
369 # res.setdefault(id, 0.0)
372 # def _close(self, cr, uid, property, context={}):
373 # if property.state<>'close':
374 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
377 # property.state='close'
378 # ok = property.asset_id.state=='open'
379 # for prop in property.asset_id.property_ids:
380 # ok = ok and prop.state=='close'
381 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
386 # _name = 'account.asset.property'
387 # _description = 'Asset property'
389 # 'name': fields.char('Method name', size=64, select=1),
390 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
391 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
392 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
393 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
394 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
395 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
396 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
398 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
399 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
400 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
401 # 'method_end': fields.date('Ending date'),
403 # 'date': fields.date('Date created'),
404 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
405 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
406 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
408 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
409 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
410 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
411 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
412 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
413 ## 'note': fields.text('Note'),
417 # 'type': lambda obj, cr, uid, context: 'direct',
418 # 'state': lambda obj, cr, uid, context: 'draft',
419 # 'method': lambda obj, cr, uid, context: 'linear',
420 # 'method_time': lambda obj, cr, uid, context: 'delay',
421 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
422 # 'method_delay': lambda obj, cr, uid, context: 5,
423 # 'method_period': lambda obj, cr, uid, context: 12,
424 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
426 #account_asset_property()
428 class account_move_line(osv.osv):
429 _inherit = 'account.move.line'
431 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
432 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
437 class account_asset_history(osv.osv):
438 _name = 'account.asset.history'
439 _description = 'Asset history'
441 'name': fields.char('History name', size=64, select=1),
442 'user_id': fields.many2one('res.users', 'User', required=True),
443 'date': fields.date('Date', required=True),
444 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
445 'method_delay': fields.integer('Number of interval'),
446 'method_period': fields.integer('Period per interval'),
447 'method_end': fields.date('Ending date'),
448 'note': fields.text('Note'),
451 'date': lambda *args: time.strftime('%Y-%m-%d'),
452 'user_id': lambda self,cr, uid,ctx: uid
454 account_asset_history()
456 class account_asset_board(osv.osv):
457 _name = 'account.asset.board'
458 _description = 'Asset board'
460 'name': fields.char('Asset name', size=64, required=True, select=1),
461 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
462 'value_gross': fields.float('Gross value', required=True, select=1),
463 'value_asset': fields.float('Asset Value', required=True, select=1),
464 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
465 'value_net': fields.float('Net value', required=True, select=1),
471 create or replace view account_asset_board as (
474 min(l.id) as asset_id,
477 0.0 as value_asset_cumul,
482 l.state <> 'draft' and
485 account_asset_board()
487 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: