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