832092385ecbb755cfe6487df2b2e1b4295f3559
[odoo/odoo.git] / addons / hr_expense / hr_expense.py
1 ##############################################################################
2 #
3 # Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
4 #
5 # $Id: hr.py 3751 2006-08-09 13:15:36Z mvd $
6 #
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsability of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # garantees and support are strongly adviced to contract a Free Software
12 # Service Company
13 #
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27 #
28 ##############################################################################
29
30 from mx import DateTime
31 import time
32
33 from osv import fields, osv
34
35 def _employee_get(obj,cr,uid,context={}):
36         ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)])
37         if ids:
38                 return ids[0]
39         return False
40
41 class hr_expense_expense(osv.osv):
42         def copy(self, cr, uid, id, default=None, context={}):
43                 if not default: default = {}
44                 default.update( {'invoice_id':False,'date_confirm':False,'date_valid':False,'user_valid':False})
45                 return super(hr_expense_expense, self).copy(cr, uid, id, default, context)
46
47         def _amount(self, cr, uid, ids, field_name, arg, context):
48                 id_set = ",".join(map(str, ids))
49                 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 ("+id_set+") GROUP BY s.id ")
50                 res = dict(cr.fetchall())
51                 return res
52
53         def _get_currency(self, cr, uid, context):
54                 user = self.pool.get('res.users').browse(cr, uid, [uid])[0]
55                 if user.company_id:
56                         return user.company_id.currency_id.id
57                 else:
58                         return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
59
60         _name = "hr.expense.expense"
61         _description = "Expense"
62         _columns = {
63                 'name': fields.char('Expense Sheet', size=128, required=True),
64                 'id': fields.integer('Sheet ID', readonly=True),
65                 'ref': fields.char('Reference', size=32),
66                 'date': fields.date('Date'),
67                 'journal_id': fields.many2one('account.journal', 'Force Journal'),
68                 'employee_id': fields.many2one('hr.employee', 'Employee', required=True),
69                 'user_id': fields.many2one('res.users', 'User', required=True),
70                 'date_confirm': fields.date('Date Confirmed'),
71                 'date_valid': fields.date('Date Valided'),
72                 'user_valid': fields.many2one('res.users', 'Validation User'),
73                 'account_move_id': fields.many2one('account.move', 'Account Move'),
74                 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines'),
75                 'note': fields.text('Note'),
76                 'amount': fields.function(_amount, method=True, string='Total Amount'),
77                 'invoice_id': fields.many2one('account.invoice', 'Invoice'),
78                 'currency_id': fields.many2one('res.currency', 'Currency', required=True),
79
80                 'state': fields.selection([
81                         ('draft', 'Draft'),
82                         ('confirm', 'Waiting confirmation'),
83                         ('accepted', 'Accepted'),
84                         ('invoiced', 'Invoiced'),
85                         ('paid', 'Reimbursed'),
86                         ('canceled', 'Canceled')],
87                         'State', readonly=True),
88         }
89         _defaults = {
90                 'date' : lambda *a: time.strftime('%Y-%m-%d'),
91                 'state': lambda *a: 'draft',
92                 'employee_id' : _employee_get,
93                 'user_id' : lambda cr,uid,id,c={}: id,
94                 'currency_id': _get_currency,
95         }
96         def expense_confirm(self, cr, uid, ids, *args):
97                 #for exp in self.browse(cr, uid, ids):
98                 self.write(cr, uid, ids, {
99                         'state':'confirm',
100                         'date_confirm': time.strftime('%Y-%m-%d')
101                 })
102                 return True
103
104         def expense_accept(self, cr, uid, ids, *args):
105                 self.write(cr, uid, ids, {
106                         'state':'accepted',
107                         'date_valid':time.strftime('%Y-%m-%d'),
108                         'user_valid': uid,
109                         })
110                 return True
111
112         def expense_canceled(self, cr, uid, ids, *args):
113                 self.write(cr, uid, ids, {'state':'canceled'})
114                 return True
115
116         def expense_paid(self, cr, uid, ids, *args):
117                 self.write(cr, uid, ids, {'state':'paid'})
118                 return True
119
120         def action_invoice_create(self, cr, uid, ids):
121                 res = False
122                 for exp in self.browse(cr, uid, ids):
123                         lines = []
124                         for l in exp.line_ids:
125                                 tax_id = []
126                                 if l.product_id:
127                                         acc = l.product_id.product_tmpl_id.property_account_expense
128                                         if not acc:
129                                                 acc = l.product_id.categ_id.property_account_expense_categ[0]
130                                         else:
131                                                 acc = acc[0]
132                                         tax_id = [x.id for x in l.product_id.supplier_taxes_id]
133                                 else:
134                                         acc = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category')
135                                 lines.append((0, False, {
136                                         'name': l.name,
137                                         'account_id': acc,
138                                         'price_unit': l.unit_amount,
139                                         'quantity': l.unit_quantity,
140                                         'uos_id': l.uom_id.id,
141                                         'product_id': l.product_id and l.product_id.id or False,
142                                         'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False,
143                                         'account_analytic_id': l.analytic_account.id,
144                                 }))
145                         if not exp.employee_id.address_id:
146                                 raise osv.except_osv('Error !', 'The employee must have a contact address')
147                         acc = exp.employee_id.address_id.partner_id.property_account_payable[0]
148                         inv = {
149                                 'name': exp.name,
150                                 'reference': self.pool.get('ir.sequence').get(cr, uid, 'hr.expense.invoice'),
151                                 'account_id': acc,
152                                 'type': 'in_invoice',
153                                 'partner_id': exp.employee_id.address_id.partner_id.id,
154                                 'address_invoice_id': exp.employee_id.address_id.id,
155                                 'address_contact_id': exp.employee_id.address_id.id,
156                                 'origin': exp.name,
157                                 'invoice_line': lines,
158                                 'price_type': 'tax_included',
159                                 'currency_id': exp.currency_id.id,
160                         }
161                         if exp.journal_id:
162                                 inv['journal_id']=exp.journal_id.id
163                         inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'})
164                         self.pool.get('account.invoice').button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, set_total=True)
165                         self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'})
166                         res = inv_id
167                 return res
168 hr_expense_expense()
169
170
171 class hr_expense_line(osv.osv):
172         _name = "hr.expense.line"
173         _description = "Expense Line"
174         def _amount(self, cr, uid, ids, field_name, arg, context):
175                 if not len(ids):
176                         return {}
177                 id_set = ",".join(map(str, ids))
178                 cr.execute("SELECT l.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_line l WHERE id IN ("+id_set+") GROUP BY l.id ")
179                 res = dict(cr.fetchall())
180                 return res
181
182         _columns = {
183                 'name': fields.char('Short Description', size=128, required=True),
184                 'date_value': fields.date('Date', required=True),
185                 'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True),
186                 'total_amount': fields.function(_amount, method=True, string='Total'),
187                 'unit_amount': fields.float('Unit Price', readonly=True, states={'draft':[('readonly',False)]}),
188                 'unit_quantity': fields.float('Quantities', readonly=True, states={'draft':[('readonly',False)]}),
189                 'product_id': fields.many2one('product.product', 'Product', readonly=True, states={'draft':[('readonly',False)]}),
190                 'uom_id': fields.many2one('product.uom', 'UoM', readonly=True, states={'draft':[('readonly',False)]}),
191                 'description': fields.text('Description'),
192                 'analytic_account': fields.many2one('account.analytic.account','Analytic account'),
193                 'ref': fields.char('Reference', size=32),
194                 'sequence' : fields.integer('Sequence'),
195         }
196         _defaults = {
197                 'unit_quantity': lambda *a: 1,
198                 'date_value' : lambda *a: time.strftime('%Y-%m-%d'),
199         }
200         _order = "sequence"
201         def onchange_product_id(self, cr, uid, ids, product_id, uom_id, context={}):
202                 v={}
203                 if product_id:
204                         product=self.pool.get('product.product').browse(cr,uid,product_id, context=context)
205                         v['name']=product.name
206                         v['unit_amount']=product.standard_price
207                         if not uom_id:
208                                 v['uom_id']=product.uom_id.id
209                 return {'value':v}
210
211 hr_expense_line()
212
213 # vim:tw=0:noexpandtab