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 def onchange_account_asset(self, cr, uid, ids, account_asset_id, context=None):
49 res['value'] = {'account_depreciation_id': account_asset_id}
52 account_asset_category()
54 #class one2many_mod_asset(fields.one2many):
56 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
57 # prinasset_property_id if context is None:
64 # #compute depreciation board
65 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
66 # for key, value in depreciation_line_ids.items():
67 # #write values on asset
68 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
69 # return depreciation_line_ids
71 class account_asset_asset(osv.osv):
72 _name = 'account.asset.asset'
73 _description = 'Asset'
75 def _get_period(self, cr, uid, context={}):
76 periods = self.pool.get('account.period').find(cr, uid)
82 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
84 @param id: ids of a account.asset.asset objects
85 @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
88 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
89 FROM account_asset_asset a
90 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
92 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
93 return dict(cr.fetchall())
95 def compute_depreciation_board(self, cr, uid,ids, context=None):
96 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
97 for asset in self.browse(cr, uid, ids, context=context):
98 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
99 if old_depreciation_line_ids:
100 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
102 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
103 residual_amount = asset.value_residual
104 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
105 day = depreciation_date.day
106 month = depreciation_date.month
107 year = depreciation_date.year
108 for i in range(1,undone_dotation_number+1):
109 if i == undone_dotation_number + 1:
110 amount = residual_amount
112 if asset.method == 'linear':
113 amount = asset.purchase_value / undone_dotation_number
115 amount = residual_amount * asset.method_progress_factor
116 residual_amount -= amount
119 'asset_id': asset.id,
121 'name': str(asset.id) +'/'+ str(i),
122 'remaining_value': residual_amount,
123 'depreciated_value': asset.purchase_value - residual_amount,
124 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
126 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
127 month += asset.method_period
128 depreciation_date = datetime(year + (month / 12), month % 12, day)
131 def validate(self, cr, uid, ids, context={}):
132 return self.write(cr, uid, ids, {
136 def _amount_total(self, cr, uid, ids, name, args, context={}):
137 #FIXME: function not working²
138 id_set=",".join(map(str,ids))
139 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
141 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
142 res=dict(cr.fetchall())
144 res.setdefault(id, 0.0)
147 def _amount_residual(self, cr, uid, ids, name, args, context={}):
149 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
153 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
154 res=dict(cr.fetchall())
155 for asset in self.browse(cr, uid, ids, context):
156 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
158 res.setdefault(id, 0.0)
162 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
163 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
165 'name': fields.char('Asset', size=64, required=True, select=1),
166 'code': fields.char('Reference ', size=16, select=1),
167 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
168 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
169 'company_id': fields.many2one('res.company', 'Company', required=True),
170 'note': fields.text('Note'),
171 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
172 'localisation': fields.char('Localisation', size=32, select=2),
173 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
174 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
175 'purchase_date': fields.date('Purchase Date', required=True),
176 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
177 'active': fields.boolean('Active', select=2),
178 'partner_id': fields.many2one('res.partner', 'Partner'),
180 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
181 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
182 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
183 'method_end': fields.date('Ending date'),
184 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
185 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
186 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
187 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
188 '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'),
189 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
190 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
193 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
194 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
195 'active': lambda obj, cr, uid, context: True,
196 'state': lambda obj, cr, uid, context: 'draft',
197 'period_id': _get_period,
198 'method': lambda obj, cr, uid, context: 'linear',
199 'method_delay': lambda obj, cr, uid, context: 5,
200 'method_time': lambda obj, cr, uid, context: 'delay',
201 'method_period': lambda obj, cr, uid, context: 12,
202 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
203 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
204 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
208 def _compute_period(self, cr, uid, property, context={}):
209 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
211 if len(property.entry_asset_ids):
212 cp = property.entry_asset_ids[-1].period_id
213 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
214 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
216 current_period = property.asset_id.period_id
217 return current_period
219 def _compute_move(self, cr, uid, property, period, context={}):
220 #FIXME: fucntion not working OK
223 for move in property.asset_id.entry_ids:
224 total += move.debit-move.credit
225 for move in property.entry_asset_ids:
226 if move.account_id == property.account_asset_ids:
228 total += -move.credit
229 periods = (len(property.entry_asset_ids)/2) - property.method_delay
234 if property.method == 'linear':
235 amount = total / periods
237 amount = total * property.method_progress_factor
239 move_id = self.pool.get('account.move').create(cr, uid, {
240 'journal_id': property.journal_id.id,
241 'period_id': period.id,
242 'name': property.name or property.asset_id.name,
243 'ref': property.asset_id.code
246 id = self.pool.get('account.move.line').create(cr, uid, {
247 'name': property.name or property.asset_id.name,
249 'account_id': property.account_asset_id.id,
250 'debit': amount>0 and amount or 0.0,
251 'credit': amount<0 and -amount or 0.0,
252 'ref': property.asset_id.code,
253 'period_id': period.id,
254 'journal_id': property.journal_id.id,
255 'partner_id': property.asset_id.partner_id.id,
256 'date': time.strftime('%Y-%m-%d'),
258 id2 = self.pool.get('account.move.line').create(cr, uid, {
259 'name': property.name or property.asset_id.name,
261 'account_id': property.account_actif_id.id,
262 'credit': amount>0 and amount or 0.0,
263 'debit': amount<0 and -amount or 0.0,
264 'ref': property.asset_id.code,
265 'period_id': period.id,
266 'journal_id': property.journal_id.id,
267 'partner_id': property.asset_id.partner_id.id,
268 'date': time.strftime('%Y-%m-%d'),
271 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
272 'entry_asset_ids': [(4, id2, False),(4,id,False)]
274 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
275 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
279 def _compute_entries(self, cr, uid, asset, period_id, context={}):
280 #FIXME: function not working CHECK all res
282 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
283 for property in asset.property_ids:
284 if property.state=='open':
285 period = self._compute_period(cr, uid, property, context)
286 if period and (period.date_start<=date_start):
287 result += self._compute_move(cr, uid, property, period, context)
289 account_asset_asset()
291 class account_asset_depreciation_line(osv.osv):
292 _name = 'account.asset.depreciation.line'
293 _description = 'Asset depreciation line'
295 def _get_move_check(self, cr, uid, ids, name, args, context=None):
297 for line in self.browse(cr, uid, ids, context=context):
298 res[line.id] = bool(line.move_id)
302 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
303 'sequence': fields.integer('Sequence of the depreciation', required=True),
304 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
305 'amount': fields.float('Depreciation Amount', required=True),
306 'remaining_value': fields.float('Amount to Depreciate', required=True),
307 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
308 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
309 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
310 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
313 def create_move(self, cr, uid,ids, context=None):
316 asset_obj = self.pool.get('account.asset.asset')
317 period_obj = self.pool.get('account.period')
318 move_obj = self.pool.get('account.move')
319 move_line_obj = self.pool.get('account.move.line')
320 currency_obj = self.pool.get('res.currency')
321 for line in self.browse(cr, uid, ids, context=context):
322 depreciation_date = time.strftime('%Y-%m-%d')
323 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
324 company_currency = line.asset_id.company_id.currency_id.id
325 current_currency = line.asset_id.currency_id.id
326 context.update({'date': depreciation_date})
327 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
328 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
331 'date': depreciation_date,
333 'period_id': period_ids and period_ids[0] or False,
334 'journal_id': line.asset_id.category_id.journal_id.id,
336 move_id = move_obj.create(cr, uid, move_vals, context=context)
337 move_line_obj.create(cr, uid, {
341 'account_id': line.asset_id.category_id.account_depreciation_id.id,
344 'period_id': period_ids and period_ids[0] or False,
345 'journal_id': line.asset_id.category_id.journal_id.id,
346 'partner_id': line.asset_id.partner_id.id,
347 'currency_id': company_currency <> current_currency and current_currency or False,
348 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
349 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
350 'date': depreciation_date,
352 move_line_obj.create(cr, uid, {
356 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
359 'period_id': period_ids and period_ids[0] or False,
360 'journal_id': line.asset_id.category_id.journal_id.id,
361 'partner_id': line.asset_id.partner_id.id,
362 'currency_id': company_currency <> current_currency and current_currency or False,
363 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
364 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
365 'date': depreciation_date,
367 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
370 account_asset_depreciation_line()
372 #class account_asset_property(osv.osv):
373 # def _amount_total(self, cr, uid, ids, name, args, context={}):
374 # id_set=",".join(map(str,ids))
375 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
376 # account_asset_property p
378 # account_move_line l on (p.asset_id=l.asset_id)
379 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
380 # res=dict(cr.fetchall())
382 # res.setdefault(id, 0.0)
385 # def _close(self, cr, uid, property, context={}):
386 # if property.state<>'close':
387 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
390 # property.state='close'
391 # ok = property.asset_id.state=='open'
392 # for prop in property.asset_id.property_ids:
393 # ok = ok and prop.state=='close'
394 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
399 # _name = 'account.asset.property'
400 # _description = 'Asset property'
402 # 'name': fields.char('Method name', size=64, select=1),
403 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
404 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
405 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
406 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
407 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
408 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
409 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
411 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
412 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
413 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
414 # 'method_end': fields.date('Ending date'),
416 # 'date': fields.date('Date created'),
417 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
418 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
419 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
421 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
422 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
423 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
424 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
425 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
426 ## 'note': fields.text('Note'),
430 # 'type': lambda obj, cr, uid, context: 'direct',
431 # 'state': lambda obj, cr, uid, context: 'draft',
432 # 'method': lambda obj, cr, uid, context: 'linear',
433 # 'method_time': lambda obj, cr, uid, context: 'delay',
434 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
435 # 'method_delay': lambda obj, cr, uid, context: 5,
436 # 'method_period': lambda obj, cr, uid, context: 12,
437 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
439 #account_asset_property()
441 class account_move_line(osv.osv):
442 _inherit = 'account.move.line'
444 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
445 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
450 class account_asset_history(osv.osv):
451 _name = 'account.asset.history'
452 _description = 'Asset history'
454 'name': fields.char('History name', size=64, select=1),
455 'user_id': fields.many2one('res.users', 'User', required=True),
456 'date': fields.date('Date', required=True),
457 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
458 'method_delay': fields.integer('Number of interval'),
459 'method_period': fields.integer('Period per interval'),
460 'method_end': fields.date('Ending date'),
461 'note': fields.text('Note'),
464 'date': lambda *args: time.strftime('%Y-%m-%d'),
465 'user_id': lambda self,cr, uid,ctx: uid
467 account_asset_history()
469 class account_asset_board(osv.osv):
470 _name = 'account.asset.board'
471 _description = 'Asset board'
473 'name': fields.char('Asset name', size=64, required=True, select=1),
474 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
475 'value_gross': fields.float('Gross value', required=True, select=1),
476 'value_asset': fields.float('Asset Value', required=True, select=1),
477 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
478 'value_net': fields.float('Net value', required=True, select=1),
484 create or replace view account_asset_board as (
487 min(l.id) as asset_id,
490 0.0 as value_asset_cumul,
495 l.state <> 'draft' and
498 account_asset_board()
500 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: