diff --git a/addons/hr_expense/__openerp__.py b/addons/hr_expense/__openerp__.py index c40670508f4..5ecf8f8e75b 100644 --- a/addons/hr_expense/__openerp__.py +++ b/addons/hr_expense/__openerp__.py @@ -34,8 +34,7 @@ The whole workflow is implemented: * Draft expense * Confirmation of the sheet by the employee * Validation by his manager - * Validation by the accountant and invoice creation - * Payment of the invoice to the employee + * Validation by the accountant and receipt creation This module also uses the analytic accounting and is compatible with the invoice on timesheet module so that you will be able to automatically @@ -44,7 +43,7 @@ re-invoice your customer's expenses if your work by project. 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', 'images': ['images/hr_expenses_analysis.jpeg', 'images/hr_expenses.jpeg'], - 'depends': ['hr', 'account'], + 'depends': ['hr', 'account_voucher'], 'init_xml': [], 'update_xml': [ 'security/ir.model.access.csv', diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 991437f3600..594b869c71e 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -40,12 +40,16 @@ class hr_expense_expense(osv.osv): if context is None: context = {} if not default: default = {} - default.update({'invoice_id': False, 'date_confirm': False, 'date_valid': False, 'user_valid': False}) + default.update({'voucher_id': False, 'date_confirm': False, 'date_valid': False, 'user_valid': False}) return super(hr_expense_expense, self).copy(cr, uid, id, default, context=context) def _amount(self, cr, uid, ids, field_name, arg, context=None): - 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),)) - res = dict(cr.fetchall()) + res= {} + for expense in self.browse(cr, uid, ids, context=context): + total = 0.0 + for line in expense.line_ids: + total += line.unit_amount * line.unit_quantity + res[expense.id] = total return res def _get_currency(self, cr, uid, context=None): @@ -63,7 +67,7 @@ class hr_expense_expense(osv.osv): 'name': fields.char('Description', size=128, required=True), 'id': fields.integer('Sheet ID', readonly=True), 'date': fields.date('Date', select=True), - 'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is invoiced"), + 'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is done."), 'employee_id': fields.many2one('hr.employee', "Employee", required=True), 'user_id': fields.many2one('res.users', 'User', required=True), '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."), @@ -73,7 +77,7 @@ class hr_expense_expense(osv.osv): 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ), 'note': fields.text('Note'), 'amount': fields.function(_amount, string='Total Amount', digits_compute= dp.get_precision('Account')), - 'invoice_id': fields.many2one('account.invoice', "Employee's Invoice"), + 'voucher_id': fields.many2one('account.voucher', "Employee's Receipt"), 'currency_id': fields.many2one('res.currency', 'Currency', required=True), 'department_id':fields.many2one('hr.department','Department'), 'company_id': fields.many2one('res.company', 'Company', required=True), @@ -82,11 +86,10 @@ class hr_expense_expense(osv.osv): ('cancelled', 'Refused'), ('confirm', 'Waiting Approval'), ('accepted', 'Approved'), - ('invoiced', 'Invoiced'), - ('paid', 'Reimbursed') + ('done', 'Done'), ], 'Status', readonly=True, help='When the expense request is created the status is \'Draft\'.\n It is confirmed by the user and request is sent to admin, the status is \'Waiting Confirmation\'.\ - \nIf the admin accepts it, the status is \'Accepted\'.\n If an invoice is made for the expense request, the status is \'Invoiced\'.\n If the expense is paid to user, the status is \'Reimbursed\'.'), + \nIf the admin accepts it, the status is \'Accepted\'.\n If a receipt is made for the expense request, the status is \'Done\'.'), } _defaults = { 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.employee', context=c), @@ -97,6 +100,13 @@ class hr_expense_expense(osv.osv): 'currency_id': _get_currency, } + def onchange_currency_id(self, cr, uid, ids, currency_id=False, company_id=False, context=None): + res = {'value': {'journal_id': False}} + journal_ids = self.pool.get('account.journal').search(cr, uid, [('type','=','purchase'), ('currency','=',currency_id), ('company_id', '=', company_id)], context=context) + if journal_ids: + res['value']['journal_id'] = journal_ids[0] + return res + def onchange_employee_id(self, cr, uid, ids, employee_id, context=None): emp_obj = self.pool.get('hr.employee') department_id = False @@ -126,101 +136,94 @@ class hr_expense_expense(osv.osv): self.write(cr, uid, ids, {'state':'cancelled'}) return True - def expense_paid(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'paid'}) - return True - - def invoice(self, cr, uid, ids, context=None): - wf_service = netsvc.LocalService("workflow") - mod_obj = self.pool.get('ir.model.data') - res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form') - inv_ids = [] - for id in ids: - wf_service.trg_validate(uid, 'hr.expense.expense', id, 'invoice', cr) - inv_ids.append(self.browse(cr, uid, id).invoice_id.id) - return { - 'name': _('Supplier Invoices'), - 'view_type': 'form', - 'view_mode': 'form', - 'view_id': [res and res[1] or False], - 'res_model': 'account.invoice', - 'context': "{'type':'out_invoice', 'journal_type': 'purchase'}", - 'type': 'ir.actions.act_window', - 'nodestroy': True, - 'target': 'current', - 'res_id': inv_ids and inv_ids[0] or False, - } - - def action_invoice_create(self, cr, uid, ids): - res = False - invoice_obj = self.pool.get('account.invoice') + def action_receipt_create(self, cr, uid, ids, context=None): property_obj = self.pool.get('ir.property') sequence_obj = self.pool.get('ir.sequence') analytic_journal_obj = self.pool.get('account.analytic.journal') account_journal = self.pool.get('account.journal') - for exp in self.browse(cr, uid, ids): + voucher_obj = self.pool.get('account.voucher') + currency_obj = self.pool.get('res.currency') + wkf_service = netsvc.LocalService("workflow") + if context is None: + context = {} + for exp in self.browse(cr, uid, ids, context=context): company_id = exp.company_id.id lines = [] - for l in exp.line_ids: - tax_id = [] - if l.product_id: - acc = l.product_id.product_tmpl_id.property_account_expense + total = 0.0 + ctx = context.copy() + ctx.update({'date': exp.date}) + journal = False + if exp.journal_id: + journal = exp.journal_id + else: + journal_id = voucher_obj._get_journal(cr, uid, context={'type': 'purchase', 'company_id': company_id}) + if journal_id: + journal = account_journal.browse(cr, uid, journal_id, context=context) + for line in exp.line_ids: + if line.product_id: + acc = line.product_id.product_tmpl_id.property_account_expense if not acc: - acc = l.product_id.categ_id.property_account_expense_categ - tax_id = [x.id for x in l.product_id.supplier_taxes_id] + acc = line.product_id.categ_id.property_account_expense_categ else: acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context={'force_company': company_id}) if not acc: raise osv.except_osv(_('Error!'), _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.')) + total_amount = line.total_amount + if journal.currency: + if exp.currency_id != journal.currency: + total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, journal.currency.id, total_amount, context=ctx) + elif exp.currency_id != exp.company_id.currency_id: + total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, exp.company_id.currency_id.id, total_amount, context=ctx) lines.append((0, False, { - 'name': l.name, + 'name': line.name, 'account_id': acc.id, - 'price_unit': l.unit_amount, - 'quantity': l.unit_quantity, - 'uos_id': l.uom_id.id, - 'product_id': l.product_id and l.product_id.id or False, - 'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False, - 'account_analytic_id': l.analytic_account.id, + 'account_analytic_id': line.analytic_account.id, + 'amount': total_amount, + 'type': 'dr' })) + total += total_amount if not exp.employee_id.address_home_id: raise osv.except_osv(_('Error!'), _('The employee must have a home address.')) acc = exp.employee_id.address_home_id.property_account_payable.id - payment_term_id = exp.employee_id.address_home_id.property_payment_term.id - inv = { + voucher = { 'name': exp.name, 'reference': sequence_obj.get(cr, uid, 'hr.expense.invoice'), 'account_id': acc, - 'type': 'in_invoice', + 'type': 'purchase', 'partner_id': exp.employee_id.address_home_id.id, 'company_id': company_id, - 'origin': exp.name, - 'invoice_line': lines, - 'currency_id': exp.currency_id.id, - 'payment_term': payment_term_id, - 'fiscal_position': exp.employee_id.address_home_id.property_account_position.id + 'line_ids': lines, + 'amount': total, + 'journal_id': journal.id, } - if payment_term_id: - to_update = invoice_obj.onchange_payment_term_date_invoice(cr, uid, [], payment_term_id, None) - if to_update: - inv.update(to_update['value']) - journal = False - if exp.journal_id: - inv['journal_id']=exp.journal_id.id - journal = exp.journal_id - else: - journal_id = invoice_obj._get_journal(cr, uid, context={'type': 'in_invoice', 'company_id': company_id}) - if journal_id: - inv['journal_id'] = journal_id - journal = account_journal.browse(cr, uid, journal_id) if journal and not journal.analytic_journal_id: - analytic_journal_ids = analytic_journal_obj.search(cr, uid, [('type','=','purchase')]) + analytic_journal_ids = analytic_journal_obj.search(cr, uid, [('type','=','purchase')], context=context) if analytic_journal_ids: - account_journal.write(cr, uid, [journal.id],{'analytic_journal_id':analytic_journal_ids[0]}) - inv_id = invoice_obj.create(cr, uid, inv, {'type': 'in_invoice'}) - invoice_obj.button_compute(cr, uid, [inv_id], {'type': 'in_invoice'}, set_total=True) - self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'}) - res = inv_id - return res + account_journal.write(cr, uid, [journal.id], {'analytic_journal_id': analytic_journal_ids[0]}, context=context) + voucher_id = voucher_obj.create(cr, uid, voucher, context=context) + wkf_service.trg_validate(uid, 'account.voucher', voucher_id, 'proforma_voucher', cr) + self.write(cr, uid, [exp.id], {'voucher_id': voucher_id, 'state': 'done'}, context=context) + return True + + def action_view_receipt(self, cr, uid, ids, context=None): + ''' + This function returns an action that display existing receipt of given expense ids. + ''' + assert len(ids) == 1, 'This option should only be used for a single id at a time' + voucher_id = self.browse(cr, uid, ids[0], context=context).voucher_id.id + res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_voucher', 'view_purchase_receipt_form') + result = { + 'name': _('Expense Receipt'), + 'view_type': 'form', + 'view_mode': 'form', + 'view_id': res and res[1] or False, + 'res_model': 'account.voucher', + 'type': 'ir.actions.act_window', + 'nodestroy': True, + 'target': 'current', + 'res_id': voucher_id, + } + return result hr_expense_expense() diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml index a810b87dad0..9978490b29a 100644 --- a/addons/hr_expense/hr_expense_view.xml +++ b/addons/hr_expense/hr_expense_view.xml @@ -31,7 +31,7 @@ - + @@ -42,7 +42,7 @@ hr.expense.expense.tree hr.expense.expense - + @@ -64,9 +64,10 @@