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('Asset category', 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 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
283 'sequence': fields.integer('Sequence of the depreciation', required=True),
284 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
285 'amount': fields.float('Depreciation Amount', required=True),
286 'remaining_value': fields.float('Amount to Depreciate', required=True),
287 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
288 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
289 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
292 def create_move(self, cr, uid,ids, context=None):
295 asset_obj = self.pool.get('account.asset.asset')
296 period_obj = self.pool.get('account.period')
297 move_obj = self.pool.get('account.move')
298 move_line_obj = self.pool.get('account.move.line')
299 currency_obj = self.pool.get('res.currency')
300 for line in self.browse(cr, uid, ids, context=context):
301 depreciation_date = asset_obj._get_last_depreciation_date(cr, uid, [line.asset_id.id], context=context)[line.asset_id.id]
302 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
303 company_currency = line.asset_id.company_id.currency_id.id
304 current_currency = line.asset_id.currency_id.id
305 context.update({'date': depreciation_date})
306 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
307 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
310 'date': depreciation_date,
312 'period_id': period_ids and period_ids[0] or False,
313 'journal_id': line.asset_id.category_id.journal_id.id,
315 move_id = move_obj.create(cr, uid, move_vals, context=context)
316 move_line_obj.create(cr, uid, {
320 'account_id': line.asset_id.category_id.account_depreciation_id.id,
323 'period_id': period_ids and period_ids[0] or False,
324 'journal_id': line.asset_id.category_id.journal_id.id,
325 'partner_id': line.asset_id.partner_id.id,
326 'currency_id': company_currency <> current_currency and current_currency or False,
327 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
328 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
329 'date': depreciation_date,
331 move_line_obj.create(cr, uid, {
335 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
338 'period_id': period_ids and period_ids[0] or False,
339 'journal_id': line.asset_id.category_id.journal_id.id,
340 'partner_id': line.asset_id.partner_id.id,
341 'currency_id': company_currency <> current_currency and current_currency or False,
342 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
343 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
344 'date': depreciation_date,
346 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
349 account_asset_depreciation_line()
351 #class account_asset_property(osv.osv):
352 # def _amount_total(self, cr, uid, ids, name, args, context={}):
353 # id_set=",".join(map(str,ids))
354 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
355 # account_asset_property p
357 # account_move_line l on (p.asset_id=l.asset_id)
358 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
359 # res=dict(cr.fetchall())
361 # res.setdefault(id, 0.0)
364 # def _close(self, cr, uid, property, context={}):
365 # if property.state<>'close':
366 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
369 # property.state='close'
370 # ok = property.asset_id.state=='open'
371 # for prop in property.asset_id.property_ids:
372 # ok = ok and prop.state=='close'
373 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
378 # _name = 'account.asset.property'
379 # _description = 'Asset property'
381 # 'name': fields.char('Method name', size=64, select=1),
382 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
383 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
384 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
385 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
386 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
387 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
388 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
390 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
391 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
392 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
393 # 'method_end': fields.date('Ending date'),
395 # 'date': fields.date('Date created'),
396 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
397 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
398 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
400 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
401 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
402 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
403 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
404 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
405 ## 'note': fields.text('Note'),
409 # 'type': lambda obj, cr, uid, context: 'direct',
410 # 'state': lambda obj, cr, uid, context: 'draft',
411 # 'method': lambda obj, cr, uid, context: 'linear',
412 # 'method_time': lambda obj, cr, uid, context: 'delay',
413 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
414 # 'method_delay': lambda obj, cr, uid, context: 5,
415 # 'method_period': lambda obj, cr, uid, context: 12,
416 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
418 #account_asset_property()
420 class account_move_line(osv.osv):
421 _inherit = 'account.move.line'
423 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
424 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
429 class account_asset_history(osv.osv):
430 _name = 'account.asset.history'
431 _description = 'Asset history'
433 'name': fields.char('History name', size=64, select=1),
434 'user_id': fields.many2one('res.users', 'User', required=True),
435 'date': fields.date('Date', required=True),
436 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
437 'method_delay': fields.integer('Number of interval'),
438 'method_period': fields.integer('Period per interval'),
439 'method_end': fields.date('Ending date'),
440 'note': fields.text('Note'),
443 'date': lambda *args: time.strftime('%Y-%m-%d'),
444 'user_id': lambda self,cr, uid,ctx: uid
446 account_asset_history()
448 class account_asset_board(osv.osv):
449 _name = 'account.asset.board'
450 _description = 'Asset board'
452 'name': fields.char('Asset name', size=64, required=True, select=1),
453 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
454 'value_gross': fields.float('Gross value', required=True, select=1),
455 'value_asset': fields.float('Asset Value', required=True, select=1),
456 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
457 'value_net': fields.float('Net value', required=True, select=1),
463 create or replace view account_asset_board as (
466 min(l.id) as asset_id,
469 0.0 as value_asset_cumul,
474 l.state <> 'draft' and
477 account_asset_board()
479 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: