HR_EXPENSE: add generate invoice
[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 _amount(self, cr, uid, ids, field_name, arg, context):
43                 id_set = ",".join(map(str, ids))
44                 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 ")
45                 res = dict(cr.fetchall())
46                 return res
47
48         _name = "hr.expense.expense"
49         _description = "Expense"
50         _columns = {
51                 'name': fields.char('Expense Sheet', size=128, required=True),
52                 'id': fields.integer('Sheet ID', readonly=True),
53                 'ref': fields.char('Reference', size=32),
54                 'date': fields.date('Date'),
55                 'journal_id': fields.many2one('account.journal', 'Force Journal'),
56                 'analytic_journal_id': fields.many2one('account.analytic.journal', 'Force Analytic Journal'),
57                 'employee_id': fields.many2one('hr.employee', 'Employee', required=True),
58                 'user_id': fields.many2one('res.users', 'User', required=True),
59                 'date_confirm': fields.date('Date Confirmed'),
60                 'date_valid': fields.date('Date Valided'),
61                 'user_valid': fields.many2one('res.users', 'Validation User'),
62
63                 'account_move_id': fields.many2one('account.move', 'Account Move'),
64                 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines'),
65                 'note': fields.text('Note'),
66
67                 # fields.function
68                 'amount': fields.function(_amount, method=True, string='Total Amount'),
69                 'invoice_id': fields.many2one('account.invoice', 'Invoice'),
70
71                 'state': fields.selection([
72                         ('draft', 'Draft'),
73                         ('confirm', 'Waiting confirmation'),
74                         ('accepted', 'Accepted'),
75                         ('invoiced', 'Invoiced'),
76                         ('paid', 'Reimbursed'),
77                         ('canceled', 'Canceled')],
78                         'State', readonly=True),
79         }
80         _defaults = {
81                 'date' : lambda *a: time.strftime('%Y-%m-%d'),
82                 'state': lambda *a: 'draft',
83                 'employee_id' : _employee_get,
84                 'user_id' : lambda cr,uid,id,c={}: id
85         }
86         def expense_confirm(self, cr, uid, ids, *args):
87                 #for exp in self.browse(cr, uid, ids):
88                 self.write(cr, uid, ids, {
89                         'state':'confirm',
90                         'date_confirm': time.strftime('%Y-%m-%d')
91                 })
92                 return True
93
94         def expense_accept(self, cr, uid, ids, *args):
95                 self.write(cr, uid, ids, {
96                         'state':'accepted',
97                         'date_valid':time.strftime('%Y-%m-%d'),
98                         'user_valid': uid,
99                         })
100                 return True
101
102         def expense_canceled(self, cr, uid, ids, *args):
103                 self.write(cr, uid, ids, {'state':'canceled'})
104                 return True
105
106         def expense_paid(self, cr, uid, ids, *args):
107                 self.write(cr, uid, ids, {'state':'paid'})
108                 return True
109
110         def action_invoice_create(self, cr, uid, ids):
111                 res = False
112                 for exp in self.browse(cr, uid, ids):
113                         lines = []
114                         for l in exp.line_ids:
115                                 tax_id = []
116                                 if l.product_id:
117                                         acc = l.product_id.product_tmpl_id.property_account_expense
118                                         if not acc:
119                                                 acc = l.product_id.categ_id.property_account_expense_categ[0]
120                                         else:
121                                                 acc = acc[0]
122                                         tax_id = [x.id for x in l.product_id.supplier_taxes_id]
123                                 else:
124                                         acc = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category')
125                                 lines.append((0, False, {
126                                         'name': l.name,
127                                         'account_id': acc,
128                                         'price_unit': l.unit_amount,
129                                         'quantity': l.unit_quantity,
130                                         'uos_id': l.uom_id.id,
131                                         'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False,
132                                         'account_analytic_id': l.analytic_account.id,
133                                 }))
134                         if not exp.employee_id.address_id:
135                                 raise osv.except_osv('Error !', 'The employee must have a contact address')
136                         acc = exp.employee_id.address_id.partner_id.property_account_payable[0]
137                         inv = {
138                                 'name': exp.name,
139                                 'reference': "EMP%dEXP%d" % (exp.employee_id.id, exp.id),
140                                 'account_id': acc,
141                                 'type': 'in_invoice',
142                                 'partner_id': exp.employee_id.address_id.partner_id.id,
143                                 'address_invoice_id': exp.employee_id.address_id.id,
144                                 'address_contact_id': exp.employee_id.address_id.id,
145                                 'origin': exp.name,
146                                 'invoice_line': lines,
147                                 'price_type': 'tax_included',
148                                 'journal_id': exp.journal_id and exp.journal_id.id or False,
149                         }
150                         inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'})
151                         self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'})
152                         res = inv_id
153                 return res
154 hr_expense_expense()
155
156
157 class hr_expense_line(osv.osv):
158         _name = "hr.expense.line"
159         _description = "Expense Line"
160         def _amount(self, cr, uid, ids, field_name, arg, context):
161                 if not len(ids):
162                         return {}
163                 id_set = ",".join(map(str, ids))
164                 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 ")
165                 res = dict(cr.fetchall())
166                 return res
167
168         _columns = {
169                 'name': fields.char('Short Description', size=128, required=True),
170                 'date_value': fields.date('Date', required=True),
171                 'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True),
172                 'total_amount': fields.function(_amount, method=True, string='Total'),
173                 'unit_amount': fields.float('Unit Price', readonly=True, states={'draft':[('readonly',False)]}),
174                 'unit_quantity': fields.float('Quantities', readonly=True, states={'draft':[('readonly',False)]}),
175                 'product_id': fields.many2one('product.product', 'Product', readonly=True, states={'draft':[('readonly',False)]}),
176                 'uom_id': fields.many2one('product.uom', 'UoM', readonly=True, states={'draft':[('readonly',False)]}),
177                 'description': fields.text('Description'),
178                 'analytic_account': fields.many2one('account.analytic.account','Analytic account'),
179                 'ref': fields.char('Reference', size=32),
180                 'sequence' : fields.integer('Sequence'),
181         }
182         _defaults = {
183                 'unit_quantity': lambda *a: 1,
184                 'date_value' : lambda *a: time.strftime('%Y-%m-%d'),
185         }
186         _order = "sequence"
187         def onchange_product_id(self, cr, uid, ids, product_id, uom_id, context={}):
188                 v={}
189                 if product_id:
190                         product=self.pool.get('product.product').browse(cr,uid,product_id, context=context)
191                         v['name']=product.name
192                         v['unit_amount']=product.standard_price
193                         if not uom_id:
194                                 v['uom_id']=product.uom_id.id
195                 return {'value':v}
196
197 hr_expense_line()
198
199 # vim:tw=0:noexpandtab