[FIX] Fixed hr get_photo issue.
[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 import time
23
24 import netsvc
25 from osv import fields, osv
26 from tools.translate import _
27
28 def _employee_get(obj, cr, uid, context=None):
29     if context is None:
30         context = {}
31     ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context)
32     if ids:
33         return ids[0]
34     return False
35
36 class hr_expense_expense(osv.osv):
37
38     def copy(self, cr, uid, id, default=None, context=None):
39         if context is None:
40             context = {}
41         if not default: default = {}
42         default.update({'invoice_id': False, 'date_confirm': False, 'date_valid': False, 'user_valid': False})
43         return super(hr_expense_expense, self).copy(cr, uid, id, default, context=context)
44
45     def _amount(self, cr, uid, ids, field_name, arg, context=None):
46         if context is None:
47             context = {}
48         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 IN %s GROUP BY s.id ", (tuple(ids),))
49         res = dict(cr.fetchall())
50         return res
51
52     def _get_currency(self, cr, uid, context=None):
53         if context is None:
54             context = {}
55         user = self.pool.get('res.users').browse(cr, uid, [uid], context=context)[0]
56         if user.company_id:
57             return user.company_id.currency_id.id
58         else:
59             return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)], context=context)[0]
60
61     _name = "hr.expense.expense"
62     _description = "Expense"
63     _columns = {
64         'name': fields.char('Description', size=128, required=True),
65         'id': fields.integer('Sheet ID', readonly=True),
66         'ref': fields.char('Reference', size=32),
67         'date': fields.date('Date'),
68         'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is invoiced"),
69         'employee_id': fields.many2one('hr.employee', "Employee", required=True),
70         'user_id': fields.many2one('res.users', 'User', required=True),
71         'date_confirm': fields.date('Confirmation Date', help = "Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."),
72         'date_valid': fields.date('Validation Date', help = "Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."),
73         'user_valid': fields.many2one('res.users', 'Validation User'),
74         'account_move_id': fields.many2one('account.move', 'Ledger Posting'),
75         'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
76         'note': fields.text('Note'),
77         'amount': fields.function(_amount, method=True, string='Total Amount'),
78         'invoice_id': fields.many2one('account.invoice', "Employee's Invoice"),
79         'currency_id': fields.many2one('res.currency', 'Currency', required=True),
80         'department_id':fields.many2one('hr.department','Department'),
81         'company_id': fields.many2one('res.company', 'Company', required=True),
82         'state': fields.selection([
83             ('draft', 'Draft'),
84             ('confirm', 'Waiting Approval'),
85             ('accepted', 'Approved'),
86             ('invoiced', 'Invoiced'),
87             ('paid', 'Reimbursed'),
88             ('cancelled', 'Refused')],
89             '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\'.\
90             \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\'.'),
91     }
92     _defaults = {
93         'date' : time.strftime('%Y-%m-%d'),
94         'state': 'draft',
95         'employee_id' : _employee_get,
96         'user_id' : lambda cr, uid, id, c={}: id,
97         'currency_id': _get_currency,
98         'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
99     }
100
101     def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
102         department_id = False
103         if employee_id:
104             department_id = self.pool.get('hr.employee').browse(cr, uid, employee_id).department_id.id or False
105         return {'value':{'department_id':department_id}}   
106
107     def expense_confirm(self, cr, uid, ids, *args):
108         self.write(cr, uid, ids, {
109             'state':'confirm',
110             'date_confirm': time.strftime('%Y-%m-%d')
111         })
112         return True
113
114     def expense_accept(self, cr, uid, ids, *args):
115         self.write(cr, uid, ids, {
116             'state':'accepted',
117             'date_valid':time.strftime('%Y-%m-%d'),
118             'user_valid': uid,
119             })
120         return True
121
122     def expense_canceled(self, cr, uid, ids, *args):
123         self.write(cr, uid, ids, {'state':'cancelled'})
124         return True
125
126     def expense_paid(self, cr, uid, ids, *args):
127         self.write(cr, uid, ids, {'state':'paid'})
128         return True
129
130     def action_invoice_create(self, cr, uid, ids):
131         res = False
132         invoice_obj = self.pool.get('account.invoice')
133         property_obj = self.pool.get('ir.property')
134         sequence_obj = self.pool.get('ir.sequence')
135         analytic_journal_obj = self.pool.get('account.analytic.journal')
136         account_journal = self.pool.get('account.journal')
137         for exp in self.browse(cr, uid, ids):
138             lines = []
139             for l in exp.line_ids:
140                 tax_id = []
141                 if l.product_id:
142                     acc = l.product_id.product_tmpl_id.property_account_expense
143                     if not acc:
144                         acc = l.product_id.categ_id.property_account_expense_categ
145                     tax_id = [x.id for x in l.product_id.supplier_taxes_id]
146                 else:
147                     acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category')
148                     if not acc:
149                         raise osv.except_osv(_('Error !'), _('Please configure Default Expanse account for Product purchase, `property_account_expense_categ`'))
150
151                 lines.append((0, False, {
152                     'name': l.name,
153                     'account_id': acc.id,
154                     'price_unit': l.unit_amount,
155                     'quantity': l.unit_quantity,
156                     'uos_id': l.uom_id.id,
157                     'product_id': l.product_id and l.product_id.id or False,
158                     'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False,
159                     'account_analytic_id': l.analytic_account.id,
160                 }))
161             if not exp.employee_id.address_id:
162                 raise osv.except_osv(_('Error !'), _('The employee must have a working address'))
163             acc = exp.employee_id.address_id.partner_id.property_account_payable.id
164             payment_term_id = exp.employee_id.address_id.partner_id.property_payment_term.id
165             inv = {
166                 'name': exp.name,
167                 'reference': sequence_obj.get(cr, uid, 'hr.expense.invoice'),
168                 'account_id': acc,
169                 'type': 'in_invoice',
170                 'partner_id': exp.employee_id.address_id.partner_id.id,
171                 'address_invoice_id': exp.employee_id.address_id.id,
172                 'address_contact_id': exp.employee_id.address_id.id,
173                 'origin': exp.name,
174                 'invoice_line': lines,
175                 'currency_id': exp.currency_id.id,
176                 'payment_term': payment_term_id,
177                 'fiscal_position': exp.employee_id.address_id.partner_id.property_account_position.id
178             }
179             if payment_term_id:
180                 to_update = invoice_obj.onchange_payment_term_date_invoice(cr, uid, [], payment_term_id, None)
181                 if to_update:
182                     inv.update(to_update['value'])
183             journal = False
184             if exp.journal_id:
185                 inv['journal_id']=exp.journal_id.id
186                 journal = exp.journal_id
187             else:
188                 journal_id = invoice_obj._get_journal(cr, uid, context={'type': 'in_invoice'})
189                 if journal_id:
190                     inv['journal_id'] = journal_id
191                     journal = account_journal.browse(cr, uid, journal_id)
192             if journal and not journal.analytic_journal_id:
193                 analytic_journal_ids = analytic_journal_obj.search(cr, uid, [('type','=','purchase')])
194                 if analytic_journal_ids:
195                     account_journal.write(cr, uid, [journal.id],{'analytic_journal_id':analytic_journal_ids[0]})
196             inv_id = invoice_obj.create(cr, uid, inv, {'type': 'in_invoice'})
197             invoice_obj.button_compute(cr, uid, [inv_id], {'type': 'in_invoice'}, set_total=True)
198             wf_service = netsvc.LocalService("workflow")
199             wf_service.trg_validate(uid, 'account.invoice', inv_id, 'invoice_open', cr)
200
201             self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'})
202             res = inv_id
203         return res
204
205 hr_expense_expense()
206
207 class product_product(osv.osv):
208     _inherit = "product.product"
209     _columns = {
210         'hr_expense_ok': fields.boolean('Can Constitute an Expense', help="Determines if the product can be visible in the list of product within a selection from an HR expense sheet line."),
211     }
212
213 product_product()
214
215 class hr_expense_line(osv.osv):
216     _name = "hr.expense.line"
217     _description = "Expense Line"
218
219     def _amount(self, cr, uid, ids, field_name, arg, context=None):
220         if context is None:
221             context = {}
222         if not len(ids):
223             return {}
224         cr.execute("SELECT l.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_line l WHERE id IN %s GROUP BY l.id ",(tuple(ids),))
225         res = dict(cr.fetchall())
226         return res
227
228     _columns = {
229         'name': fields.char('Expense Note', size=128, required=True),
230         'date_value': fields.date('Date', required=True),
231         'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True),
232         'total_amount': fields.function(_amount, method=True, string='Total'),
233         'unit_amount': fields.float('Unit Price'),
234         'unit_quantity': fields.float('Quantities' ),
235         'product_id': fields.many2one('product.product', 'Product', domain=[('hr_expense_ok','=',True)]),
236         'uom_id': fields.many2one('product.uom', 'UoM' ),
237         'description': fields.text('Description'),
238         'analytic_account': fields.many2one('account.analytic.account','Analytic account'),
239         'ref': fields.char('Reference', size=32),
240         'sequence' : fields.integer('Sequence', help="Gives the sequence order when displaying a list of expense lines."),
241         }
242     _defaults = {
243         'unit_quantity': 1,
244         'date_value': time.strftime('%Y-%m-%d'),
245     }
246     _order = "sequence"
247
248     def onchange_product_id(self, cr, uid, ids, product_id, uom_id, employee_id, context=None):
249         if context is None:
250             context = {}
251         res = {}
252         if product_id:
253             product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
254             res['name'] = product.name
255             # Compute based on pricetype of employee company
256             context['currency_id'] = self.pool.get('hr.employee').browse(cr, uid, employee_id, context=context).user_id.company_id.currency_id.id
257             amount_unit = product.price_get('standard_price', context)[product.id]
258             res['unit_amount'] = amount_unit
259             if not uom_id:
260                 res['uom_id'] = product.uom_id.id
261         return {'value': res}
262
263 hr_expense_line()
264
265 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: