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, {
130 def _amount_total(self, cr, uid, ids, name, args, context={}):
131 #FIXME: function not working²
132 id_set=",".join(map(str,ids))
133 cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
135 WHERE l.asset_id IN ("""+id_set+") GROUP BY l.asset_id ")
136 res=dict(cr.fetchall())
138 res.setdefault(id, 0.0)
141 def _amount_residual(self, cr, uid, ids, name, args, context={}):
143 l.asset_id as id, SUM(abs(l.debit-l.credit)) AS amount
147 l.asset_id IN %s GROUP BY l.asset_id """, (tuple(ids),))
148 res=dict(cr.fetchall())
149 for asset in self.browse(cr, uid, ids, context):
150 res[asset.id] = asset.purchase_value - res.get(asset.id, 0.0)
152 res.setdefault(id, 0.0)
156 'period_id': fields.many2one('account.period', 'First Period', required=True, readonly=True, states={'draft':[('readonly',False)]}),
157 'account_move_line_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
159 'name': fields.char('Asset', size=64, required=True, select=1),
160 'code': fields.char('Reference ', size=16, select=1),
161 'purchase_value': fields.float('Gross value ', required=True, size=16, select=1),
162 'currency_id': fields.many2one('res.currency','Currency',required=True,size=5,select=1),
163 'company_id': fields.many2one('res.company', 'Company', required=True),
164 'note': fields.text('Note'),
165 'category_id': fields.many2one('account.asset.category', 'Asset category',required=True, change_default=True),
166 'localisation': fields.char('Localisation', size=32, select=2),
167 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset'),
168 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'),
169 'purchase_date': fields.date('Purchase Date', required=True),
170 'state': fields.selection([('view','View'),('draft','Draft'),('normal','Normal'),('close','Close')], 'state', required=True),
171 'active': fields.boolean('Active', select=2),
172 'partner_id': fields.many2one('res.partner', 'Partner'),
174 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
175 'method_delay': fields.integer('During (interval)', readonly=True, states={'draft':[('readonly',False)]}),
176 'method_period': fields.integer('Depre. all (period)', readonly=True, states={'draft':[('readonly',False)]}),
177 'method_end': fields.date('Ending date'),
178 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross Value'),
179 'method_progress_factor': fields.float('Progressif Factor', readonly=True, states={'draft':[('readonly',False)]}),
180 'value_residual': fields.function(_amount_residual, method=True, digits=(16,2), string='Residual Value'),
181 'method_time': fields.selection([('delay','Delay'),('end','Ending Period')], 'Time Method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
182 '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'),
183 'history_ids': fields.one2many('account.asset.history', 'asset_id', 'History', readonly=True),
184 'depreciation_line_ids': fields.one2many('account.asset.depreciation.line', 'asset_id', 'Depreciation Lines', readonly=True,),
187 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.asset.code'),
188 'purchase_date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d'),
189 'active': lambda obj, cr, uid, context: True,
190 'state': lambda obj, cr, uid, context: 'draft',
191 'period_id': _get_period,
192 'method': lambda obj, cr, uid, context: 'linear',
193 'method_delay': lambda obj, cr, uid, context: 5,
194 'method_time': lambda obj, cr, uid, context: 'delay',
195 'method_period': lambda obj, cr, uid, context: 12,
196 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
197 'currency_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.currency_id.id,
198 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'account.asset.asset',context=context),
202 def _compute_period(self, cr, uid, property, context={}):
203 if (len(property.entry_asset_ids or [])/2)>=property.method_delay:
205 if len(property.entry_asset_ids):
206 cp = property.entry_asset_ids[-1].period_id
207 cpid = self.pool.get('account.period').next(cr, uid, cp, property.method_period, context)
208 current_period = self.pool.get('account.period').browse(cr, uid, cpid, context)
210 current_period = property.asset_id.period_id
211 return current_period
213 def _compute_move(self, cr, uid, property, period, context={}):
214 #FIXME: fucntion not working OK
217 for move in property.asset_id.entry_ids:
218 total += move.debit-move.credit
219 for move in property.entry_asset_ids:
220 if move.account_id == property.account_asset_ids:
222 total += -move.credit
223 periods = (len(property.entry_asset_ids)/2) - property.method_delay
228 if property.method == 'linear':
229 amount = total / periods
231 amount = total * property.method_progress_factor
233 move_id = self.pool.get('account.move').create(cr, uid, {
234 'journal_id': property.journal_id.id,
235 'period_id': period.id,
236 'name': property.name or property.asset_id.name,
237 'ref': property.asset_id.code
240 id = self.pool.get('account.move.line').create(cr, uid, {
241 'name': property.name or property.asset_id.name,
243 'account_id': property.account_asset_id.id,
244 'debit': amount>0 and amount or 0.0,
245 'credit': amount<0 and -amount or 0.0,
246 'ref': property.asset_id.code,
247 'period_id': period.id,
248 'journal_id': property.journal_id.id,
249 'partner_id': property.asset_id.partner_id.id,
250 'date': time.strftime('%Y-%m-%d'),
252 id2 = self.pool.get('account.move.line').create(cr, uid, {
253 'name': property.name or property.asset_id.name,
255 'account_id': property.account_actif_id.id,
256 'credit': amount>0 and amount or 0.0,
257 'debit': amount<0 and -amount or 0.0,
258 'ref': property.asset_id.code,
259 'period_id': period.id,
260 'journal_id': property.journal_id.id,
261 'partner_id': property.asset_id.partner_id.id,
262 'date': time.strftime('%Y-%m-%d'),
265 self.pool.get('account.asset.asset').write(cr, uid, [property.id], {
266 'entry_asset_ids': [(4, id2, False),(4,id,False)]
268 if property.method_delay - (len(property.entry_asset_ids)/2)<=1:
269 #self.pool.get('account.asset.property')._close(cr, uid, property, context)
273 def _compute_entries(self, cr, uid, asset, period_id, context={}):
274 #FIXME: function not working CHECK all res
276 date_start = self.pool.get('account.period').browse(cr, uid, period_id, context).date_start
277 for property in asset.property_ids:
278 if property.state=='open':
279 period = self._compute_period(cr, uid, property, context)
280 if period and (period.date_start<=date_start):
281 result += self._compute_move(cr, uid, property, period, context)
283 account_asset_asset()
285 class account_asset_depreciation_line(osv.osv):
286 _name = 'account.asset.depreciation.line'
287 _description = 'Asset depreciation line'
289 def _get_move_check(self, cr, uid, ids, name, args, context=None):
291 for line in self.browse(cr, uid, ids, context=context):
292 res[line.id] = bool(line.move_id)
296 'name': fields.char('Depreciation Name', size=64, required=True, select=1),
297 'sequence': fields.integer('Sequence of the depreciation', required=True),
298 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
299 'amount': fields.float('Depreciation Amount', required=True),
300 'remaining_value': fields.float('Amount to Depreciate', required=True),
301 'depreciated_value': fields.float('Amount Already Depreciated', required=True),
302 'depreciation_date': fields.char('Depreciation Date', size=64, select=1),
303 'move_id': fields.many2one('account.move', 'Depreciation Entry'),
304 'move_check': fields.function(_get_move_check, method=True, type='boolean', string='Posted', store=True)
307 def create_move(self, cr, uid,ids, context=None):
310 asset_obj = self.pool.get('account.asset.asset')
311 period_obj = self.pool.get('account.period')
312 move_obj = self.pool.get('account.move')
313 move_line_obj = self.pool.get('account.move.line')
314 currency_obj = self.pool.get('res.currency')
315 for line in self.browse(cr, uid, ids, context=context):
316 depreciation_date = time.strftime('%Y-%m-%d')
317 period_ids = period_obj.find(cr, uid, depreciation_date, context=context)
318 company_currency = line.asset_id.company_id.currency_id.id
319 current_currency = line.asset_id.currency_id.id
320 context.update({'date': depreciation_date})
321 amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
322 sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
325 'date': depreciation_date,
327 'period_id': period_ids and period_ids[0] or False,
328 'journal_id': line.asset_id.category_id.journal_id.id,
330 move_id = move_obj.create(cr, uid, move_vals, context=context)
331 move_line_obj.create(cr, uid, {
335 'account_id': line.asset_id.category_id.account_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 move_line_obj.create(cr, uid, {
350 'account_id': line.asset_id.category_id.account_expense_depreciation_id.id,
353 'period_id': period_ids and period_ids[0] or False,
354 'journal_id': line.asset_id.category_id.journal_id.id,
355 'partner_id': line.asset_id.partner_id.id,
356 'currency_id': company_currency <> current_currency and current_currency or False,
357 'amount_currency': company_currency <> current_currency and sign * line.amount or 0.0,
358 'analytic_account_id': line.asset_id.category_id.account_analytic_id.id,
359 'date': depreciation_date,
361 self.write(cr, uid, line.id, {'move_id': move_id}, context=context)
364 account_asset_depreciation_line()
366 #class account_asset_property(osv.osv):
367 # def _amount_total(self, cr, uid, ids, name, args, context={}):
368 # id_set=",".join(map(str,ids))
369 # cr.execute("""SELECT l.asset_id,abs(SUM(l.debit-l.credit)) AS amount FROM
370 # account_asset_property p
372 # account_move_line l on (p.asset_id=l.asset_id)
373 # WHERE p.id IN ("""+id_set+") GROUP BY l.asset_id ")
374 # res=dict(cr.fetchall())
376 # res.setdefault(id, 0.0)
379 # def _close(self, cr, uid, property, context={}):
380 # if property.state<>'close':
381 # self.pool.get('account.asset.property').write(cr, uid, [property.id], {
384 # property.state='close'
385 # ok = property.asset_id.state=='open'
386 # for prop in property.asset_id.property_ids:
387 # ok = ok and prop.state=='close'
388 # self.pool.get('account.asset.asset').write(cr, uid, [property.asset_id.id], {
393 # _name = 'account.asset.property'
394 # _description = 'Asset property'
396 # 'name': fields.char('Method name', size=64, select=1),
397 # 'type': fields.selection([('direct','Direct'),('indirect','Indirect')], 'Depr. method type', select=2, required=True),
398 # 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
399 # 'account_asset_id': fields.many2one('account.account', 'Asset account', required=True),
400 # 'account_actif_id': fields.many2one('account.account', 'Depreciation account', required=True),
401 # 'journal_id': fields.many2one('account.journal', 'Journal', required=True),
402 # 'journal_analytic_id': fields.many2one('account.analytic.journal', 'Analytic journal'),
403 # 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'),
405 # 'method': fields.selection([('linear','Linear'),('progressif','Progressive')], 'Computation method', required=True, readonly=True, states={'draft':[('readonly',False)]}),
406 # 'method_delay': fields.integer('During', readonly=True, states={'draft':[('readonly',False)]}),
407 # 'method_period': fields.integer('Depre. all', readonly=True, states={'draft':[('readonly',False)]}),
408 # 'method_end': fields.date('Ending date'),
410 # 'date': fields.date('Date created'),
411 # #'test': fields.one2many('account.pre', 'asset_id', readonly=True, states={'draft':[('readonly',False)]}),
412 # 'entry_asset_ids': fields.many2many('account.move.line', 'account_move_asset_entry_rel', 'asset_property_id', 'move_id', 'Asset Entries'),
413 # 'board_ids': fields.one2many('account.asset.board', 'asset_id', 'Asset board'),
415 # 'value_total': fields.function(_amount_total, method=True, digits=(16,2),string='Gross value'),
416 # 'state': fields.selection([('draft','Draft'), ('open','Open'), ('close','Close')], 'State', required=True),
417 # 'history_ids': fields.one2many('account.asset.property.history', 'asset_property_id', 'History', readonly=True)
418 ## 'parent_id': fields.many2one('account.asset.asset', 'Parent asset'),
419 ## 'partner_id': fields.many2one('res.partner', 'Partner'),
420 ## 'note': fields.text('Note'),
424 # 'type': lambda obj, cr, uid, context: 'direct',
425 # 'state': lambda obj, cr, uid, context: 'draft',
426 # 'method': lambda obj, cr, uid, context: 'linear',
427 # 'method_time': lambda obj, cr, uid, context: 'delay',
428 # 'method_progress_factor': lambda obj, cr, uid, context: 0.3,
429 # 'method_delay': lambda obj, cr, uid, context: 5,
430 # 'method_period': lambda obj, cr, uid, context: 12,
431 # 'date': lambda obj, cr, uid, context: time.strftime('%Y-%m-%d')
433 #account_asset_property()
435 class account_move_line(osv.osv):
436 _inherit = 'account.move.line'
438 'asset_id': fields.many2one('account.asset.asset', 'Asset'),
439 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}),
444 class account_asset_history(osv.osv):
445 _name = 'account.asset.history'
446 _description = 'Asset history'
448 'name': fields.char('History name', size=64, select=1),
449 'user_id': fields.many2one('res.users', 'User', required=True),
450 'date': fields.date('Date', required=True),
451 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True),
452 'method_delay': fields.integer('Number of interval'),
453 'method_period': fields.integer('Period per interval'),
454 'method_end': fields.date('Ending date'),
455 'note': fields.text('Note'),
458 'date': lambda *args: time.strftime('%Y-%m-%d'),
459 'user_id': lambda self,cr, uid,ctx: uid
461 account_asset_history()
463 class account_asset_board(osv.osv):
464 _name = 'account.asset.board'
465 _description = 'Asset board'
467 'name': fields.char('Asset name', size=64, required=True, select=1),
468 'asset_id': fields.many2one('account.asset.asset', 'Asset', required=True, select=1),
469 'value_gross': fields.float('Gross value', required=True, select=1),
470 'value_asset': fields.float('Asset Value', required=True, select=1),
471 'value_asset_cumul': fields.float('Cumul. value', required=True, select=1),
472 'value_net': fields.float('Net value', required=True, select=1),
478 create or replace view account_asset_board as (
481 min(l.id) as asset_id,
484 0.0 as value_asset_cumul,
489 l.state <> 'draft' and
492 account_asset_board()
494 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: