1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 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 ##############################################################################
25 from osv import fields
32 class account_analytic_account(osv.osv):
33 _name = 'account.analytic.account'
34 _description = 'Analytic Accounts'
36 def _compute_currency_for_level_tree(self, cr, uid, ids, ids2, res, acc_set, context={}):
37 # Handle multi-currency on each level of analytic account
38 # This is a refactoring of _balance_calc computation
39 cr.execute("SELECT a.id, r.currency_id FROM account_analytic_account a INNER JOIN res_company r ON (a.company_id = r.id) where a.id in (%s)" % acc_set)
40 currency = dict(cr.fetchall())
41 res_currency = self.pool.get('res.currency')
45 for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]):
47 res.setdefault(id, 0.0)
48 if currency[child] != currency[id]:
49 res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context)
51 res[id] += res.get(child, 0.0)
53 cur_obj = res_currency.browse(cr, uid, currency.values(), context)
54 cur_obj = dict([(o.id, o) for o in cur_obj])
57 res[id] = res_currency.round(cr, uid, cur_obj[currency[id]], res.get(id, 0.0))
59 return dict([(i, res[i]) for i in ids ])
62 def _credit_calc(self, cr, uid, ids, name, arg, context={}):
64 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
65 acc_set = ",".join(map(str, ids2))
68 res.setdefault(i, 0.0)
74 if context.get('from_date', False):
75 where_date += " AND l.date >= '" + context['from_date'] + "'"
76 if context.get('to_date', False):
77 where_date += " AND l.date <= '" + context['to_date'] + "'"
78 cr.execute("SELECT a.id, COALESCE(SUM(l.amount_currency),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id " + where_date + ") WHERE l.amount_currency<0 and a.id =ANY(%s) GROUP BY a.id", (ids2,))
79 r = dict(cr.fetchall())
80 return self._compute_currency_for_level_tree(cr, uid, ids, ids2, r, acc_set, context)
82 def _debit_calc(self, cr, uid, ids, name, arg, context={}):
84 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
85 acc_set = ",".join(map(str, ids2))
88 res.setdefault(i, 0.0)
94 if context.get('from_date', False):
95 where_date += " AND l.date >= '" + context['from_date'] + "'"
96 if context.get('to_date', False):
97 where_date += " AND l.date <= '" + context['to_date'] + "'"
98 cr.execute("SELECT a.id, COALESCE(SUM(l.amount_currency),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id " + where_date + ") WHERE l.amount_currency>0 and a.id =ANY(%s) GROUP BY a.id" , (ids2,))
99 r = dict(cr.fetchall())
100 return self._compute_currency_for_level_tree(cr, uid, ids, ids2, r, acc_set, context)
102 def _balance_calc(self, cr, uid, ids, name, arg, context={}):
104 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
105 acc_set = ",".join(map(str, ids2))
108 res.setdefault(i, 0.0)
114 if context.get('from_date', False):
115 where_date += " AND l.date >= '" + context['from_date'] + "'"
116 if context.get('to_date', False):
117 where_date += " AND l.date <= '" + context['to_date'] + "'"
118 cr.execute("SELECT a.id, COALESCE(SUM(l.amount_currency),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id " + where_date + ") WHERE a.id =ANY(%s) GROUP BY a.id", (ids2,))
120 for account_id, sum in cr.fetchall():
121 res[account_id] = sum
123 return self._compute_currency_for_level_tree(cr, uid, ids, ids2, res, acc_set, context)
125 def _quantity_calc(self, cr, uid, ids, name, arg, context={}):
126 #XXX must convert into one uom
128 ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
129 acc_set = ",".join(map(str, ids2))
132 res.setdefault(i, 0.0)
138 if context.get('from_date', False):
139 where_date += " AND l.date >= '" + context['from_date'] + "'"
140 if context.get('to_date', False):
141 where_date += " AND l.date <= '" + context['to_date'] + "'"
143 cr.execute('SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \
144 FROM account_analytic_account a \
145 LEFT JOIN account_analytic_line l ON (a.id = l.account_id ' + where_date + ') \
146 WHERE a.id =ANY(%s) GROUP BY a.id', (ids2,))
148 for account_id, sum in cr.fetchall():
149 res[account_id] = sum
154 for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]):
156 res.setdefault(id, 0.0)
157 res[id] += res.get(child, 0.0)
158 return dict([(i, res[i]) for i in ids])
160 def name_get(self, cr, uid, ids, context={}):
163 reads = self.read(cr, uid, ids, ['name', 'parent_id'], context)
166 name = record['name']
167 if record['parent_id']:
168 name = record['parent_id'][1] + ' / ' + name
169 res.append((record['id'], name))
172 def _complete_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
173 res = self.name_get(cr, uid, ids)
176 def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
178 for rec in self.browse(cr, uid, ids, context):
179 result[rec.id] = (rec.company_id.currency_id.id, rec.company_id.currency_id.code) or False
182 def _get_account_currency(self, cr, uid, ids, field_name, arg, context={}):
183 result = self._get_company_currency(cr, uid, ids, field_name, arg, context={})
187 'name' : fields.char('Account Name', size=128, required=True),
188 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Full Account Name'),
189 'code' : fields.char('Account Code', size=24),
190 'type': fields.selection([('view', 'View'), ('normal', 'Normal')], 'Account Type'),
191 'description' : fields.text('Description'),
192 'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2),
193 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Child Accounts'),
194 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic Entries'),
195 'balance' : fields.function(_balance_calc, method=True, type='float', string='Balance'),
196 'debit' : fields.function(_debit_calc, method=True, type='float', string='Debit'),
197 'credit' : fields.function(_credit_calc, method=True, type='float', string='Credit'),
198 'quantity': fields.function(_quantity_calc, method=True, type='float', string='Quantity'),
199 'quantity_max': fields.float('Maximum Quantity', help='Sets the higher limit of quantity of hours.'),
200 'partner_id' : fields.many2one('res.partner', 'Associated Partner'),
201 'contact_id' : fields.many2one('res.partner.address', 'Contact'),
202 'user_id' : fields.many2one('res.users', 'Account Manager'),
203 'date_start': fields.date('Date Start'),
204 'date': fields.date('Date End'),
205 'company_id': fields.many2one('res.company', 'Company', required=True),
206 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Currency'),
207 'state': fields.selection([('draft', 'Draft'), ('open', 'Open'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('close', 'Close'), ('template', 'Template')], 'State', required=True, readonly=True,
208 help='* When an account is created its in \'Draft\' state.\
209 \n* If any associated partner is there, it can be in \'Open\' state.\
210 \n* If any pending balance is there it can be in \'Pending\'. \
211 \n* And finally when all the transactions are over, it can be in \'Close\' state. \
212 \n* The project can be in either if the states \'Template\' and \'Running\'.\n If it is template then we can make projects based on the template projects. If its in \'Running\' state it is a normal project.\
213 \n If it is to be reviewed then the state is \'Pending\'.\n When the project is completed the state is set to \'Done\'.'),
214 'currency_id': fields.function(_get_account_currency, method=True, type='many2one', relation='res.currency', string='Account currency', store=True),
217 def _default_company(self, cr, uid, context={}):
218 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
220 return user.company_id.id
221 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
223 'type' : lambda * a : 'normal',
224 'company_id': _default_company,
225 'state' : lambda * a : 'open',
226 'user_id' : lambda self, cr, uid, ctx : uid,
227 'partner_id': lambda self, cr, uid, ctx: ctx.get('partner_id', False),
228 'contact_id': lambda self, cr, uid, ctx: ctx.get('contact_id', False),
229 'date_start': lambda * a: time.strftime('%Y-%m-%d')
232 def check_recursion(self, cr, uid, ids, parent=None):
233 return super(account_analytic_account, self).check_recursion(cr, uid, ids, parent=parent)
235 _order = 'parent_id desc,code'
237 (check_recursion, 'Error! You can not create recursive analytic accounts.', ['parent_id'])
240 def create(self, cr, uid, vals, context=None):
241 parent_id = vals.get('parent_id', 0)
242 if ('code' not in vals or not vals['code']) and not parent_id:
243 vals['code'] = self.pool.get('ir.sequence').get(cr, uid, 'account.analytic.account')
244 return super(account_analytic_account, self).create(cr, uid, vals, context=context)
246 def copy(self, cr, uid, id, default=None, context={}):
249 default['code'] = False
250 default['line_ids'] = []
251 return super(account_analytic_account, self).copy(cr, uid, id, default, context=context)
254 def on_change_parent(self, cr, uid, id, parent_id):
257 parent = self.read(cr, uid, [parent_id], ['partner_id', 'code'])[0]
258 childs = self.search(cr, uid, [('parent_id', '=', parent_id), ('active', 'in', [True, False])])
259 numchild = len(childs)
260 if parent['partner_id']:
261 partner = parent['partner_id'][0]
264 res = {'value' : {'code' : '%s - %03d' % (parent['code'] or '', numchild + 1), }}
266 res['value']['partner_id'] = partner
269 def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
274 account = self.search(cr, uid, [('code', '=', name)] + args, limit=limit, context=context)
276 account = self.search(cr, uid, [('name', 'ilike', '%%%s%%' % name)] + args, limit=limit, context=context)
279 newacc = self.search(cr, uid, [('parent_id', 'in', newacc)] + args, limit=limit, context=context)
281 return self.name_get(cr, uid, account, context=context)
283 account_analytic_account()