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 ##############################################################################
24 from osv import fields, osv
25 import decimal_precision as dp
27 class account_analytic_account(osv.osv):
28 _name = 'account.analytic.account'
29 _description = 'Analytic Account'
31 def _compute_level_tree(self, cr, uid, ids, child_ids, res, field_names, context=None):
32 def recursive_computation(account_id, res):
33 account = self.browse(cr, uid, account_id)
34 for son in account.child_ids:
35 res = recursive_computation(son.id, res)
36 for field in field_names:
37 res[account.id][field] += res[son.id][field]
39 for account in self.browse(cr, uid, ids, context=context):
40 if account.id not in child_ids:
42 res = recursive_computation(account.id, res)
45 def _debit_credit_bal_qtty(self, cr, uid, ids, name, arg, context=None):
49 child_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
59 where_clause_args = [tuple(child_ids)]
60 if context.get('from_date', False):
61 where_date += " AND l.date >= %s"
62 where_clause_args += [context['from_date']]
63 if context.get('to_date', False):
64 where_date += " AND l.date <= %s"
65 where_clause_args += [context['to_date']]
69 CASE WHEN l.amount > 0
75 CASE WHEN l.amount < 0
80 COALESCE(SUM(l.amount),0) AS balance,
81 COALESCE(SUM(l.unit_amount),0) AS quantity
82 FROM account_analytic_account a
83 LEFT JOIN account_analytic_line l ON (a.id = l.account_id)
85 """ + where_date + """
86 GROUP BY a.id""", where_clause_args)
87 for ac_id, debit, credit, balance, quantity in cr.fetchall():
88 res[ac_id] = {'debit': debit, 'credit': credit, 'balance': balance, 'quantity': quantity}
89 return self._compute_level_tree(cr, uid, ids, child_ids, res, ['debit', 'credit', 'balance', 'quantity'], context)
91 def name_get(self, cr, uid, ids, context=None):
95 for account in self.browse(cr, uid, ids, context=context):
99 data.insert(0, acc.name)
101 data = ' / '.join(data)
102 res.append((account.id, data))
105 def _complete_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict):
106 res = self.name_get(cr, uid, ids)
110 'name': fields.char('Account Name', size=128, required=True),
111 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Full Account Name'),
112 'code': fields.char('Account Code', size=24),
113 'type': fields.selection([('view','View'), ('normal','Normal')], 'Account Type', help='If you select the View Type, it means you won\'t allow to create journal entries using that account.'),
114 'description': fields.text('Description'),
115 'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2),
116 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Child Accounts'),
117 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic Entries'),
118 'balance': fields.function(_debit_credit_bal_qtty, method=True, type='float', string='Balance', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
119 'debit': fields.function(_debit_credit_bal_qtty, method=True, type='float', string='Debit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
120 'credit': fields.function(_debit_credit_bal_qtty, method=True, type='float', string='Credit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
121 'quantity': fields.function(_debit_credit_bal_qtty, method=True, type='float', string='Quantity', multi='debit_credit_bal_qtty'),
122 'quantity_max': fields.float('Maximum Quantity', help='Sets the higher limit of quantity of hours.'),
123 'partner_id': fields.many2one('res.partner', 'Partner'),
124 'contact_id': fields.many2one('res.partner.address', 'Contact'),
125 'user_id': fields.many2one('res.users', 'Account Manager'),
126 'date_start': fields.date('Date Start'),
127 'date': fields.date('Date End'),
128 'company_id': fields.many2one('res.company', 'Company', required=True),
129 'state': fields.selection([('draft','Draft'),('open','Open'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Closed'),('template', 'Template')], 'State', required=True,
130 help='* When an account is created its in \'Draft\' state.\
131 \n* If any associated partner is there, it can be in \'Open\' state.\
132 \n* If any pending balance is there it can be in \'Pending\'. \
133 \n* And finally when all the transactions are over, it can be in \'Close\' state. \
134 \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.\
135 \n If it is to be reviewed then the state is \'Pending\'.\n When the project is completed the state is set to \'Done\'.'),
136 'currency_id': fields.related('company_id', 'currency_id', type='many2one', relation='res.currency', string='Account currency', store=True, readonly=True),
139 def _default_company(self, cr, uid, context=None):
140 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
142 return user.company_id.id
143 return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
147 'company_id': _default_company,
149 'user_id': lambda self, cr, uid, ctx: uid,
150 'partner_id': lambda self, cr, uid, ctx: ctx.get('partner_id', False),
151 'contact_id': lambda self, cr, uid, ctx: ctx.get('contact_id', False),
152 'date_start': lambda *a: time.strftime('%Y-%m-%d')
155 def check_recursion(self, cr, uid, ids, parent=None):
156 return super(account_analytic_account, self).check_recursion(cr, uid, ids, parent=parent)
158 _order = 'date_start desc,parent_id desc,code'
160 (check_recursion, 'Error! You can not create recursive analytic accounts.', ['parent_id'])
163 def copy(self, cr, uid, id, default=None, context=None):
166 default['code'] = False
167 default['line_ids'] = []
168 return super(account_analytic_account, self).copy(cr, uid, id, default, context=context)
170 def on_change_parent(self, cr, uid, id, parent_id):
173 parent = self.read(cr, uid, [parent_id], ['partner_id','code'])[0]
174 if parent['partner_id']:
175 partner = parent['partner_id'][0]
180 res['value']['partner_id'] = partner
183 def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
188 account = self.search(cr, uid, [('code', '=', name)]+args, limit=limit, context=context)
190 account = self.search(cr, uid, [('name', 'ilike', '%%%s%%' % name)]+args, limit=limit, context=context)
193 newacc = self.search(cr, uid, [('parent_id', 'in', newacc)]+args, limit=limit, context=context)
195 return self.name_get(cr, uid, account, context=context)
197 account_analytic_account()
200 class account_analytic_line(osv.osv):
201 _name = 'account.analytic.line'
202 _description = 'Analytic Line'
205 'name': fields.char('Description', size=256, required=True),
206 'date': fields.date('Date', required=True, select=1),
207 'amount': fields.float('Amount', required=True, help='Calculated by multiplying the quantity and the price given in the Product\'s cost price. Always expressed in the company main currency.', digits_compute=dp.get_precision('Account')),
208 'unit_amount': fields.float('Quantity', help='Specifies the amount of quantity to count.'),
209 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', required=True, ondelete='cascade', select=True),
210 'user_id': fields.many2one('res.users', 'User'),
211 'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
215 'date': lambda *a: time.strftime('%Y-%m-%d'),
216 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context=c),
222 account_analytic_line()
224 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: