[IMP] HR : Employee,Department search view added. coach_id added in employee. Full...
[odoo/odoo.git] / addons / hr_expense / hr_expense.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
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.
11 #
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.
16 #
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/>.
19 #
20 ##############################################################################
21
22 from mx import DateTime
23 import time
24
25 from osv import fields, osv
26 from tools.translate import _
27
28 def _employee_get(obj,cr,uid,context={}):
29     ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
30     if ids:
31         return ids[0]
32     return False
33
34 class hr_expense_expense(osv.osv):
35     def copy(self, cr, uid, id, default=None, context={}):
36         if not default: default = {}
37         default.update( {'invoice_id':False,'date_confirm':False,'date_valid':False,'user_valid':False})
38         return super(hr_expense_expense, self).copy(cr, uid, id, default, context)
39
40     def _amount(self, cr, uid, ids, field_name, arg, context):
41         cr.execute("SELECT s.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_expense s LEFT OUTER JOIN hr_expense_line l ON (s.id=l.expense_id) WHERE s.id =ANY(%s) GROUP BY s.id ",(ids,))
42         res = dict(cr.fetchall())
43         return res
44
45     def _get_currency(self, cr, uid, context):
46         user = self.pool.get('res.users').browse(cr, uid, [uid])[0]
47         if user.company_id:
48             return user.company_id.currency_id.id
49         else:
50             return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
51
52     _name = "hr.expense.expense"
53     _description = "Expense"
54     _columns = {
55         'name': fields.char('Expense Sheet', size=128, required=True),
56         'id': fields.integer('Sheet ID', readonly=True),
57         'ref': fields.char('Reference', size=32),
58         'date': fields.date('Date'),
59         'journal_id': fields.many2one('account.journal', 'Force Journal'),
60         'employee_id': fields.many2one('hr.employee', "Employee's Name", required=True),
61         'user_id': fields.many2one('res.users', 'User', required=True),
62         'date_confirm': fields.date('Date Confirmed'),
63         'date_valid': fields.date('Date Validated'),
64         'user_valid': fields.many2one('res.users', 'Validation User'),
65         'account_move_id': fields.many2one('account.move', 'Ledger Posting'),
66         'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
67         'note': fields.text('Note'),
68         'amount': fields.function(_amount, method=True, string='Total Amount'),
69         'invoice_id': fields.many2one('account.invoice', 'Invoice'),
70         'currency_id': fields.many2one('res.currency', 'Currency', required=True),
71         'department_id':fields.many2one('hr.department','Department'),
72         'state': fields.selection([
73             ('draft', 'Draft'),
74             ('confirm', 'Waiting confirmation'),
75             ('accepted', 'Accepted'),
76             ('invoiced', 'Invoiced'),
77             ('paid', 'Reimbursed'),
78             ('cancelled', 'Cancelled')],
79             'State', readonly=True, help='When the expense request is created the state is \'Draft\'.\n It is confirmed by the user and request is sent to admin, the state is \'Waiting Confirmation\'.\
80             \nIf the admin accepts it, the state is \'Accepted\'.\n If an invoice is made for the expense request, the state is \'Invoiced\'.\n If the expense is paid to user, the state is \'Reimbursed\'.'),
81     }
82     _defaults = {
83         'date' : lambda *a: time.strftime('%Y-%m-%d'),
84         'state': lambda *a: 'draft',
85         'employee_id' : _employee_get,
86         'user_id' : lambda cr,uid,id,c={}: id,
87         'currency_id': _get_currency,
88     }
89     def expense_confirm(self, cr, uid, ids, *args):
90         #for exp in self.browse(cr, uid, ids):
91         self.write(cr, uid, ids, {
92             'state':'confirm',
93             'date_confirm': time.strftime('%Y-%m-%d')
94         })
95         return True
96
97     def expense_accept(self, cr, uid, ids, *args):
98         self.write(cr, uid, ids, {
99             'state':'accepted',
100             'date_valid':time.strftime('%Y-%m-%d'),
101             'user_valid': uid,
102             })
103         return True
104
105     def expense_canceled(self, cr, uid, ids, *args):
106         self.write(cr, uid, ids, {'state':'cancelled'})
107         return True
108
109     def expense_paid(self, cr, uid, ids, *args):
110         self.write(cr, uid, ids, {'state':'paid'})
111         return True
112
113     def action_invoice_create(self, cr, uid, ids):
114         res = False
115         invoice_obj = self.pool.get('account.invoice')
116         for exp in self.browse(cr, uid, ids):
117             lines = []
118             for l in exp.line_ids:
119                 tax_id = []
120                 if l.product_id:
121                     acc = l.product_id.product_tmpl_id.property_account_expense.id
122                     if not acc:
123                         acc = l.product_id.categ_id.property_account_expense_categ.id
124                     tax_id = [x.id for x in l.product_id.supplier_taxes_id]
125                 else:
126                     acc = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category')
127                     if not acc:
128                         raise osv.except_osv(_('Error !'), _('Please configure Default Expanse account for Product purchase, `property_account_expense_categ`'))
129
130                 lines.append((0, False, {
131                     'name': l.name,
132                     'account_id': acc,
133                     'price_unit': l.unit_amount,
134                     'quantity': l.unit_quantity,
135                     'uos_id': l.uom_id.id,
136                     'product_id': l.product_id and l.product_id.id or False,
137                     'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False,
138                     'account_analytic_id': l.analytic_account.id,
139                 }))
140             if not exp.employee_id.address_id:
141                 raise osv.except_osv(_('Error !'), _('The employee must have a working address'))
142             acc = exp.employee_id.address_id.partner_id.property_account_payable.id
143             payment_term_id = exp.employee_id.address_id.partner_id.property_payment_term.id
144             inv = {
145                 'name': exp.name,
146                 'reference': self.pool.get('ir.sequence').get(cr, uid, 'hr.expense.invoice'),
147                 'account_id': acc,
148                 'type': 'in_invoice',
149                 'partner_id': exp.employee_id.address_id.partner_id.id,
150                 'address_invoice_id': exp.employee_id.address_id.id,
151                 'address_contact_id': exp.employee_id.address_id.id,
152                 'origin': exp.name,
153                 'invoice_line': lines,
154                 'price_type': 'tax_included',
155                 'currency_id': exp.currency_id.id,
156                 'payment_term': payment_term_id,
157                 'fiscal_position': exp.employee_id.address_id.partner_id.property_account_position.id
158             }
159             if payment_term_id:
160                 to_update = invoice_obj.onchange_payment_term_date_invoice(cr, uid, [],
161                         payment_term_id, None)
162                 if to_update:
163                     inv.update(to_update['value'])
164             if exp.journal_id:
165                 inv['journal_id']=exp.journal_id.id
166             inv_id = invoice_obj.create(cr, uid, inv, {'type':'in_invoice'})
167             invoice_obj.button_compute(cr, uid, [inv_id], {'type':'in_invoice'},
168                     set_total=True)
169             self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'})
170             res = inv_id
171         return res
172 hr_expense_expense()
173
174 class product_product(osv.osv):
175     _inherit = "product.product"
176
177     _columns = {
178                 'hr_expense_ok': fields.boolean('Can be Expensed', help="Determines if the product can be visible in the list of product within a selection from an HR expense sheet line."),
179     }
180
181 product_product()
182
183
184 class hr_expense_line(osv.osv):
185     _name = "hr.expense.line"
186     _description = "Expense Line"
187     def _amount(self, cr, uid, ids, field_name, arg, context):
188         if not len(ids):
189             return {}
190         cr.execute("SELECT l.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_line l WHERE id =ANY(%s) GROUP BY l.id ",(ids,))
191         res = dict(cr.fetchall())
192         return res
193
194     _columns = {
195         'name': fields.char('Short Description', size=128, required=True),
196         'date_value': fields.date('Date', required=True),
197         'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True),
198         'total_amount': fields.function(_amount, method=True, string='Total'),
199         'unit_amount': fields.float('Unit Price'),
200         'unit_quantity': fields.float('Quantities' ),
201         'product_id': fields.many2one('product.product', 'Product', domain=[('hr_expense_ok','=',True)]),
202         'uom_id': fields.many2one('product.uom', 'UoM' ),
203         'description': fields.text('Description'),
204         'analytic_account': fields.many2one('account.analytic.account','Analytic account'),
205         'ref': fields.char('Reference', size=32),
206         'sequence' : fields.integer('Sequence', help="Gives the sequence order when displaying a list of expense lines."),
207     }
208     _defaults = {
209         'unit_quantity': lambda *a: 1,
210         'date_value' : lambda *a: time.strftime('%Y-%m-%d'),
211     }
212     _order = "sequence"
213     def onchange_product_id(self, cr, uid, ids, product_id, uom_id, employee_id, context={}):
214         v={}
215         if product_id:
216             product=self.pool.get('product.product').browse(cr,uid,product_id, context=context)
217             v['name']=product.name
218
219             # Compute based on pricetype of employee company
220             pricetype_id = self.pool.get('hr.employee').browse(cr,uid,employee_id).user_id.company_id.property_valuation_price_type.id
221             pricetype=self.pool.get('product.price.type').browse(cr,uid,pricetype_id)
222             amount_unit=product.price_get(pricetype.field, context)[product.id]
223
224             v['unit_amount']=amount_unit
225             if not uom_id:
226                 v['uom_id']=product.uom_id.id
227         return {'value':v}
228
229 hr_expense_line()
230
231 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
232