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),
40 'on_change_state': fields.boolean('Skip Draft State', help="Check this if you want to skip draft state for asset when it is created by invoice."),
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 residual_amount = asset.value_residual
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 = asset.purchase_value / 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': asset.purchase_value - 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, {
131 def _amount_total(self, cr, uid, ids, name, args, context={}):
132 #FIXME: function not working²
133 id_set=",".join(map(str,ids))
134 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
136 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
137 res=dict(cr.fetchall())
139 res.setdefault(id, 0.0)
142 def _amount_residual(self, cr, uid, ids, name, args, context={}):
144 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
148 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
149 res=dict(cr.fetchall())
150 for asset in self.browse(cr, uid, ids, context):
151 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
153 res.setdefault(id, 0.0)
157 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
158 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
160 'name': fields.char('Asset', size=64, required=True, select=1),
161 'code': fields.char('Reference ', size=16, select=1),
162 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
163 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
164 'company_id': fields.many2one('res.company', 'Company', required=True),
165 'note': fields.text('Note'),
166 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
167 'localisation': fields.char('Localisation', size=32, select=2),
168 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
169 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
170 'purchase_date': fields.date('Purchase Date', required=True),
171 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
172 'active': fields.boolean('Active', select=2),
173 'partner_id': fields.many2one('res.partner', 'Partner'),
175 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
176 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
177 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
178 'method_end': fields.date('Ending date'),
179 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
180 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
181 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
182 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
183 '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'),
184 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
185 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
188 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
189 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
190 'active': lambda obj, cr, uid, context: True,
191 'state': lambda obj, cr, uid, context: 'draft',
192 'period_id': _get_period,
193 'method': lambda obj, cr, uid, context: 'linear',
194 'method_delay': lambda obj, cr, uid, context: 5,
195 'method_time': lambda obj, cr, uid, context: 'delay',
196 'method_period': lambda obj, cr, uid, context: 12,
197 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
198 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
199 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
203 def _compute_period(self, cr, uid, property, context={}):
204 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
206 if len(property.entry_asset_ids):
207 cp = property.entry_asset_ids[-1].period_id
208 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
209 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
211 current_period = property.asset_id.period_id
212 return current_period
214 def _compute_move(self, cr, uid, property, period, context={}):
215 #FIXME: fucntion not working OK
218 for move in property.asset_id.entry_ids:
219 total += move.debit-move.credit
220 for move in property.entry_asset_ids:
221 if move.account_id == property.account_asset_ids:
223 total += -move.credit
224 periods = (len(property.entry_asset_ids)/2) - property.method_delay
229 if property.method == 'linear':
230 amount = total / periods
232 amount = total * property.method_progress_factor
234 move_id = self.pool.get('account.move').create(cr, uid, {
235 'journal_id': property.journal_id.id,
236 'period_id': period.id,
237 'name': property.name or property.asset_id.name,
238 'ref': property.asset_id.code
241 id = self.pool.get('account.move.line').create(cr, uid, {
242 'name': property.name or property.asset_id.name,
244 'account_id': property.account_asset_id.id,
245 'debit': amount>0 and amount or 0.0,
246 'credit': amount<0 and -amount or 0.0,
247 'ref': property.asset_id.code,
248 'period_id': period.id,
249 'journal_id': property.journal_id.id,
250 'partner_id': property.asset_id.partner_id.id,
251 'date': time.strftime('%Y-%m-%d'),
253 id2 = self.pool.get('account.move.line').create(cr, uid, {
254 'name': property.name or property.asset_id.name,
256 'account_id': property.account_actif_id.id,
257 'credit': amount>0 and amount or 0.0,
258 'debit': amount<0 and -amount or 0.0,
259 'ref': property.asset_id.code,
260 'period_id': period.id,
261 'journal_id': property.journal_id.id,
262 'partner_id': property.asset_id.partner_id.id,
263 'date': time.strftime('%Y-%m-%d'),
266 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
267 'entry_asset_ids': [(4, id2, False),(4,id,False)]
269 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
270 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
274 def _compute_entries(self, cr, uid, asset, period_id, context={}):
275 #FIXME: function not working CHECK all res
277 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
278 for property in asset.property_ids:
279 if property.state=='open':
280 period = self._compute_period(cr, uid, property, context)
281 if period and (period.date_start<=date_start):
282 result += self._compute_move(cr, uid, property, period, context)
284 account_asset_asset()
286 class account_asset_depreciation_line(osv.osv):
287 _name = 'account.asset.depreciation.line'
288 _description = 'Asset depreciation line'
290 def _get_move_check(self, cr, uid, ids, name, args, context=None):
292 for line in self.browse(cr, uid, ids, context=context):
293 res[line.id] = bool(line.move_id)
297 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
298 'sequence': fields.integer('Sequence of the depreciation', required=True),
299 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
300 'amount': fields.float('Depreciation Amount', required=True),
301 'remaining_value': fields.float('Amount to Depreciate', required=True),
302 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
303 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
304 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
305 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
308 def create_move(self, cr, uid,ids, context=None):
311 asset_obj = self.pool.get('account.asset.asset')
312 period_obj = self.pool.get('account.period')
313 move_obj = self.pool.get('account.move')
314 move_line_obj = self.pool.get('account.move.line')
315 currency_obj = self.pool.get('res.currency')
316 for line in self.browse(cr, uid, ids, context=context):
317 depreciation_date = time.strftime('%Y-%m-%d')
318 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
319 company_currency = line.asset_id.company_id.currency_id.id
320 current_currency = line.asset_id.currency_id.id
321 context.update({'date': depreciation_date})
322 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
323 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
326 'date': depreciation_date,
328 'period_id': period_ids and period_ids[0] or False,
329 'journal_id': line.asset_id.category_id.journal_id.id,
331 move_id = move_obj.create(cr, uid, move_vals, context=context)
332 move_line_obj.create(cr, uid, {
336 'account_id': line.asset_id.category_id.account_depreciation_id.id,
339 'period_id': period_ids and period_ids[0] or False,
340 'journal_id': line.asset_id.category_id.journal_id.id,
341 'partner_id': line.asset_id.partner_id.id,
342 'currency_id': company_currency <> current_currency and current_currency or False,
343 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
344 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
345 'date': depreciation_date,
347 move_line_obj.create(cr, uid, {
351 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
354 'period_id': period_ids and period_ids[0] or False,
355 'journal_id': line.asset_id.category_id.journal_id.id,
356 'partner_id': line.asset_id.partner_id.id,
357 'currency_id': company_currency <> current_currency and current_currency or False,
358 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
359 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
360 'date': depreciation_date,
362 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
365 account_asset_depreciation_line()
367 #class account_asset_property(osv.osv):
368 # def _amount_total(self, cr, uid, ids, name, args, context={}):
369 # id_set=",".join(map(str,ids))
370 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
371 # account_asset_property p
373 # account_move_line l on (p.asset_id=l.asset_id)
374 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
375 # res=dict(cr.fetchall())
377 # res.setdefault(id, 0.0)
380 # def _close(self, cr, uid, property, context={}):
381 # if property.state<>'close':
382 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
385 # property.state='close'
386 # ok = property.asset_id.state=='open'
387 # for prop in property.asset_id.property_ids:
388 # ok = ok and prop.state=='close'
389 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
394 # _name = 'account.asset.property'
395 # _description = 'Asset property'
397 # 'name': fields.char('Method name', size=64, select=1),
398 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
399 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
400 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
401 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
402 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
403 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
404 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
406 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
407 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
408 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
409 # 'method_end': fields.date('Ending date'),
411 # 'date': fields.date('Date created'),
412 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
413 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
414 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
416 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
417 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
418 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
419 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
420 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
421 ## 'note': fields.text('Note'),
425 # 'type': lambda obj, cr, uid, context: 'direct',
426 # 'state': lambda obj, cr, uid, context: 'draft',
427 # 'method': lambda obj, cr, uid, context: 'linear',
428 # 'method_time': lambda obj, cr, uid, context: 'delay',
429 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
430 # 'method_delay': lambda obj, cr, uid, context: 5,
431 # 'method_period': lambda obj, cr, uid, context: 12,
432 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
434 #account_asset_property()
436 class account_move_line(osv.osv):
437 _inherit = 'account.move.line'
439 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
440 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
445 class account_asset_history(osv.osv):
446 _name = 'account.asset.history'
447 _description = 'Asset history'
449 'name': fields.char('History name', size=64, select=1),
450 'user_id': fields.many2one('res.users', 'User', required=True),
451 'date': fields.date('Date', required=True),
452 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
453 'method_delay': fields.integer('Number of interval'),
454 'method_period': fields.integer('Period per interval'),
455 'method_end': fields.date('Ending date'),
456 'note': fields.text('Note'),
459 'date': lambda *args: time.strftime('%Y-%m-%d'),
460 'user_id': lambda self,cr, uid,ctx: uid
462 account_asset_history()
464 class account_asset_board(osv.osv):
465 _name = 'account.asset.board'
466 _description = 'Asset board'
468 'name': fields.char('Asset name', size=64, required=True, select=1),
469 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
470 'value_gross': fields.float('Gross value', required=True, select=1),
471 'value_asset': fields.float('Asset Value', required=True, select=1),
472 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
473 'value_net': fields.float('Net value', required=True, select=1),
479 create or replace view account_asset_board as (
482 min(l.id) as asset_id,
485 0.0 as value_asset_cumul,
490 l.state <> 'draft' and
493 account_asset_board()
495 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: