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 account_asset_category()
48 #class one2many_mod_asset(fields.one2many):
50 # def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
51 # prinasset_property_id if context is None:
58 # #compute depreciation board
59 # depreciation_line_ids = obj.pool.get('account.asset.asset').compute_depreciation_board(cr, user, ids, context=context)
60 # for key, value in depreciation_line_ids.items():
61 # #write values on asset
62 # obj.pool.get(self._obj).write(cr, user, key, {'depreciation_line_ids': [6,0,value]})
63 # return depreciation_line_ids
65 class account_asset_asset(osv.osv):
66 _name = 'account.asset.asset'
67 _description = 'Asset'
69 def _get_period(self, cr, uid, context={}):
70 periods = self.pool.get('account.period').find(cr, uid)
76 def _get_last_depreciation_date(self, cr, uid, ids, context=None):
78 @param id: ids of a account.asset.asset objects
79 @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
82 SELECT a.id as id, COALESCE(MAX(l.date),a.purchase_date) AS date
83 FROM account_asset_asset a
84 LEFT JOIN account_move_line l ON (l.asset_id = a.id)
86 GROUP BY a.id, a.purchase_date """, (tuple(ids),))
87 return dict(cr.fetchall())
89 def compute_depreciation_board(self, cr, uid,ids, context=None):
90 depreciation_lin_obj = self.pool.get('account.asset.depreciation.line')
91 for asset in self.browse(cr, uid, ids, context=context):
92 old_depreciation_line_ids = depreciation_lin_obj.search(cr, uid, [('asset_id', '=', asset.id), ('move_id', '=', False)])
93 if old_depreciation_line_ids:
94 depreciation_lin_obj.unlink(cr, uid, old_depreciation_line_ids, context=context)
96 undone_dotation_number = asset.method_delay - len(asset.account_move_line_ids)
97 residual_amount = asset.value_residual
98 depreciation_date = datetime.strptime(self._get_last_depreciation_date(cr, uid, [asset.id], context)[asset.id], '%Y-%m-%d')
99 day = depreciation_date.day
100 month = depreciation_date.month
101 year = depreciation_date.year
102 for i in range(1,undone_dotation_number+1):
103 if i == undone_dotation_number + 1:
104 amount = residual_amount
106 if asset.method == 'linear':
107 amount = asset.purchase_value / undone_dotation_number
109 amount = residual_amount * asset.method_progress_factor
110 residual_amount -= amount
113 'asset_id': asset.id,
115 'name': str(asset.id) +'/'+ str(i),
116 'remaining_value': residual_amount,
117 'depreciated_value': asset.purchase_value - residual_amount,
118 'depreciation_date': depreciation_date.strftime('%Y-%m-%d'),
120 self.pool.get('account.asset.depreciation.line').create(cr, uid, vals)
121 month += asset.method_period
122 depreciation_date = datetime(year + (month / 12), month % 12, day)
125 def validate(self, cr, uid, ids, context={}):
126 return self.write(cr, uid, ids, {
132 def _amount_residual(self, cr, uid, ids, name, args, context={}):
134 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
138 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
139 res=dict(cr.fetchall())
140 for asset in self.browse(cr, uid, ids, context):
141 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
143 res.setdefault(id, 0.0)
147 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
148 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
150 'name': fields.char('Asset', size=64, required=True, select=1),
151 'code': fields.char('Reference ', size=16, select=1),
152 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
153 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
154 'company_id': fields.many2one('res.company', 'Company', required=True),
155 'note': fields.text('Note'),
156 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
157 'localisation': fields.char('Localisation', size=32, select=2),
158 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
159 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
160 'purchase_date': fields.date('Purchase Date', required=True),
161 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
162 'active': fields.boolean('Active', select=2),
163 'partner_id': fields.many2one('res.partner', 'Partner'),
165 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
166 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
167 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
168 'method_end': fields.date('Ending date'),
169 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
170 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
171 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
172 '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'),
173 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
174 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
177 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
178 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
179 'active': lambda obj, cr, uid, context: True,
180 'state': lambda obj, cr, uid, context: 'draft',
181 'period_id': _get_period,
182 'method': lambda obj, cr, uid, context: 'linear',
183 'method_delay': lambda obj, cr, uid, context: 5,
184 'method_time': lambda obj, cr, uid, context: 'delay',
185 'method_period': lambda obj, cr, uid, context: 12,
186 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
187 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
188 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
192 def _compute_period(self, cr, uid, property, context={}):
193 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
195 if len(property.entry_asset_ids):
196 cp = property.entry_asset_ids[-1].period_id
197 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
198 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
200 current_period = property.asset_id.period_id
201 return current_period
203 def _compute_move(self, cr, uid, property, period, context={}):
204 #FIXME: fucntion not working OK
207 for move in property.asset_id.entry_ids:
208 total += move.debit-move.credit
209 for move in property.entry_asset_ids:
210 if move.account_id == property.account_asset_ids:
212 total += -move.credit
213 periods = (len(property.entry_asset_ids)/2) - property.method_delay
218 if property.method == 'linear':
219 amount = total / periods
221 amount = total * property.method_progress_factor
223 move_id = self.pool.get('account.move').create(cr, uid, {
224 'journal_id': property.journal_id.id,
225 'period_id': period.id,
226 'name': property.name or property.asset_id.name,
227 'ref': property.asset_id.code
230 id = self.pool.get('account.move.line').create(cr, uid, {
231 'name': property.name or property.asset_id.name,
233 'account_id': property.account_asset_id.id,
234 'debit': amount>0 and amount or 0.0,
235 'credit': amount<0 and -amount or 0.0,
236 'ref': property.asset_id.code,
237 'period_id': period.id,
238 'journal_id': property.journal_id.id,
239 'partner_id': property.asset_id.partner_id.id,
240 'date': time.strftime('%Y-%m-%d'),
242 id2 = self.pool.get('account.move.line').create(cr, uid, {
243 'name': property.name or property.asset_id.name,
245 'account_id': property.account_actif_id.id,
246 'credit': amount>0 and amount or 0.0,
247 'debit': amount<0 and -amount or 0.0,
248 'ref': property.asset_id.code,
249 'period_id': period.id,
250 'journal_id': property.journal_id.id,
251 'partner_id': property.asset_id.partner_id.id,
252 'date': time.strftime('%Y-%m-%d'),
255 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
256 'entry_asset_ids': [(4, id2, False),(4,id,False)]
258 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
259 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
263 def _compute_entries(self, cr, uid, asset, period_id, context={}):
264 #FIXME: function not working CHECK all res
266 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
267 for property in asset.property_ids:
268 if property.state=='open':
269 period = self._compute_period(cr, uid, property, context)
270 if period and (period.date_start<=date_start):
271 result += self._compute_move(cr, uid, property, period, context)
273 account_asset_asset()
275 class account_asset_depreciation_line(osv.osv):
276 _name = 'account.asset.depreciation.line'
277 _description = 'Asset depreciation line'
279 def _get_move_check(self, cr, uid, ids, name, args, context=None):
281 for line in self.browse(cr, uid, ids, context=context):
282 res[line.id] = bool(line.move_id)
286 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
287 'sequence': fields.integer('Sequence of the depreciation', required=True),
288 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
289 'amount': fields.float('Depreciation Amount', required=True),
290 'remaining_value': fields.float('Amount to Depreciate', required=True),
291 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
292 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
293 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
294 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Move Included', store=True)
297 def create_move(self, cr, uid,ids, context=None):
300 asset_obj = self.pool.get('account.asset.asset')
301 period_obj = self.pool.get('account.period')
302 move_obj = self.pool.get('account.move')
303 move_line_obj = self.pool.get('account.move.line')
304 currency_obj = self.pool.get('res.currency')
305 for line in self.browse(cr, uid, ids, context=context):
306 depreciation_date = time.strftime('%Y-%m-%d')
307 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
308 company_currency = line.asset_id.company_id.currency_id.id
309 current_currency = line.asset_id.currency_id.id
310 context.update({'date': depreciation_date})
311 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
312 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
315 'date': depreciation_date,
317 'period_id': period_ids and period_ids[0] or False,
318 'journal_id': line.asset_id.category_id.journal_id.id,
320 move_id = move_obj.create(cr, uid, move_vals, context=context)
321 move_line_obj.create(cr, uid, {
325 'account_id': line.asset_id.category_id.account_depreciation_id.id,
328 'period_id': period_ids and period_ids[0] or False,
329 'journal_id': line.asset_id.category_id.journal_id.id,
330 'partner_id': line.asset_id.partner_id.id,
331 'currency_id': company_currency <> current_currency and current_currency or False,
332 'amount_currency': company_currency <> current_currency and - sign * line.amount or 0.0,
333 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
334 'date': depreciation_date,
336 move_line_obj.create(cr, uid, {
340 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
343 'period_id': period_ids and period_ids[0] or False,
344 'journal_id': line.asset_id.category_id.journal_id.id,
345 'partner_id': line.asset_id.partner_id.id,
346 'currency_id': company_currency <> current_currency and current_currency or False,
347 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
348 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
349 'date': depreciation_date,
351 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
354 account_asset_depreciation_line()
356 #class account_asset_property(osv.osv):
357 # def _amount_total(self, cr, uid, ids, name, args, context={}):
358 # id_set=",".join(map(str,ids))
359 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
360 # account_asset_property p
362 # account_move_line l on (p.asset_id=l.asset_id)
363 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
364 # res=dict(cr.fetchall())
366 # res.setdefault(id, 0.0)
369 # def _close(self, cr, uid, property, context={}):
370 # if property.state<>'close':
371 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
374 # property.state='close'
375 # ok = property.asset_id.state=='open'
376 # for prop in property.asset_id.property_ids:
377 # ok = ok and prop.state=='close'
378 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
383 # _name = 'account.asset.property'
384 # _description = 'Asset property'
386 # 'name': fields.char('Method name', size=64, select=1),
387 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
388 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
389 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
390 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
391 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
392 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
393 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
395 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
396 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
397 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
398 # 'method_end': fields.date('Ending date'),
400 # 'date': fields.date('Date created'),
401 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
402 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
403 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
405 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
406 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
407 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
408 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
409 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
410 ## 'note': fields.text('Note'),
414 # 'type': lambda obj, cr, uid, context: 'direct',
415 # 'state': lambda obj, cr, uid, context: 'draft',
416 # 'method': lambda obj, cr, uid, context: 'linear',
417 # 'method_time': lambda obj, cr, uid, context: 'delay',
418 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
419 # 'method_delay': lambda obj, cr, uid, context: 5,
420 # 'method_period': lambda obj, cr, uid, context: 12,
421 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
423 #account_asset_property()
425 class account_move_line(osv.osv):
426 _inherit = 'account.move.line'
428 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
429 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
434 class account_asset_history(osv.osv):
435 _name = 'account.asset.history'
436 _description = 'Asset history'
438 'name': fields.char('History name', size=64, select=1),
439 'user_id': fields.many2one('res.users', 'User', required=True),
440 'date': fields.date('Date', required=True),
441 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
442 'method_delay': fields.integer('Number of interval'),
443 'method_period': fields.integer('Period per interval'),
444 'method_end': fields.date('Ending date'),
445 'note': fields.text('Note'),
448 'date': lambda *args: time.strftime('%Y-%m-%d'),
449 'user_id': lambda self,cr, uid,ctx: uid
451 account_asset_history()
453 class account_asset_board(osv.osv):
454 _name = 'account.asset.board'
455 _description = 'Asset board'
457 'name': fields.char('Asset name', size=64, required=True, select=1),
458 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
459 'value_gross': fields.float('Gross value', required=True, select=1),
460 'value_asset': fields.float('Asset Value', required=True, select=1),
461 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
462 'value_net': fields.float('Net value', required=True, select=1),
468 create or replace view account_asset_board as (
471 min(l.id) as asset_id,
474 0.0 as value_asset_cumul,
479 l.state <> 'draft' and
482 account_asset_board()
484 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: