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 @@
-
+
+
-
+
@@ -79,7 +80,7 @@
-
+
@@ -105,14 +106,21 @@
-
-
+
+
+
+
+
+
+
-
-
+
+
diff --git a/addons/hr_expense/hr_expense_workflow.xml b/addons/hr_expense/hr_expense_workflow.xml
index 328a07879a4..2e085071207 100644
--- a/addons/hr_expense/hr_expense_workflow.xml
+++ b/addons/hr_expense/hr_expense_workflow.xml
@@ -32,14 +32,6 @@
expense_accept()
-
-
- paid
- function
- expense_paid()
- True
-
-
refused
@@ -47,12 +39,11 @@
expense_canceled()
-
+
- invoice
- subflow
-
- action_invoice_create()
+ done
+ function
+ action_receipt_create()
@@ -91,15 +82,8 @@
-
- invoice
-
-
-
-
-
-
- subflow.paid
+
+ done
diff --git a/addons/hr_expense/report/hr_expense_report.py b/addons/hr_expense/report/hr_expense_report.py
index adef11f5269..72bf710ec4f 100644
--- a/addons/hr_expense/report/hr_expense_report.py
+++ b/addons/hr_expense/report/hr_expense_report.py
@@ -39,11 +39,10 @@ class hr_expense_report(osv.osv):
'product_id':fields.many2one('product.product', 'Product', readonly=True),
'journal_id': fields.many2one('account.journal', 'Force Journal', readonly=True),
'product_qty':fields.float('Qty', readonly=True),
- 'invoiced':fields.integer('# of Invoiced Lines', readonly=True),
'employee_id': fields.many2one('hr.employee', "Employee's Name", readonly=True),
'date_confirm': fields.date('Confirmation Date', readonly=True),
'date_valid': fields.date('Validation Date', readonly=True),
- 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
+ 'voucher_id': fields.many2one('account.voucher', 'Receipt', readonly=True),
'department_id':fields.many2one('hr.department','Department', readonly=True),
'company_id':fields.many2one('res.company', 'Company', readonly=True),
'user_id':fields.many2one('res.users', 'Validation User', readonly=True),
@@ -60,8 +59,7 @@ class hr_expense_report(osv.osv):
('draft', 'Draft'),
('confirm', 'Waiting confirmation'),
('accepted', 'Accepted'),
- ('invoiced', 'Invoiced'),
- ('paid', 'Reimbursed'),
+ ('done', 'Done'),
('cancelled', 'Cancelled')],
'Status', readonly=True),
}
@@ -78,8 +76,7 @@ class hr_expense_report(osv.osv):
s.currency_id,
to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_confirm,
to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_valid,
- s.invoice_id,
- count(s.invoice_id) as invoiced,
+ s.voucher_id,
s.user_valid as user_id,
s.department_id,
to_char(date_trunc('day',s.create_date), 'YYYY') as year,
@@ -109,7 +106,7 @@ class hr_expense_report(osv.osv):
to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY'),
l.product_id,
l.analytic_account,
- s.invoice_id,
+ s.voucher_id,
s.currency_id,
s.user_valid,
s.department_id,
diff --git a/addons/hr_expense/report/hr_expense_report_view.xml b/addons/hr_expense/report/hr_expense_report_view.xml
index 890568abc44..1cd1e10c5ed 100644
--- a/addons/hr_expense/report/hr_expense_report_view.xml
+++ b/addons/hr_expense/report/hr_expense_report_view.xml
@@ -6,13 +6,13 @@
hr.expense.report.tree
hr.expense.report
-
+
-
+
@@ -22,7 +22,6 @@
-
@@ -50,7 +49,7 @@
-
+
diff --git a/addons/hr_expense/test/expense_process.yml b/addons/hr_expense/test/expense_process.yml
index 5c8a9d53f7c..eae63c460fc 100644
--- a/addons/hr_expense/test/expense_process.yml
+++ b/addons/hr_expense/test/expense_process.yml
@@ -17,33 +17,20 @@
!assert {model: hr.expense.expense, id: sep_expenses, severity: error, string: Expense should be in Approved state}:
- state == 'accepted'
-
- I make Invoice for the expense.
+ I make Receipt for the expense.
-
- !python {model: hr.expense.expense}: |
- self.invoice(cr, uid, [ref('sep_expenses')])
+ !workflow {model: hr.expense.expense, action: done, ref: sep_expenses}
-
- I check invoice details.
+ I check receipt details.
-
!python {model: hr.expense.expense}: |
sep_expenses = self.browse(cr, uid, ref("sep_expenses"), context=context)
- assert sep_expenses.state == 'invoiced', "Expense should be in 'Invoiced' state."
- assert sep_expenses.invoice_id, "Expense should have link of Invoice."
- assert sep_expenses.invoice_id.currency_id == sep_expenses.currency_id,"Invoice currency is not correspond with supplier invoice currency"
- assert sep_expenses.invoice_id.origin == sep_expenses.name,"Invoice origin is not correspond with supplier invoice"
- assert sep_expenses.invoice_id.type == 'in_invoice', "Invoice type is not supplier invoice"
- assert sep_expenses.invoice_id.amount_total == sep_expenses.amount,"Invoice total amount is not correspond with supplier invoice total"
- assert len(sep_expenses.invoice_id.invoice_line) == len(sep_expenses.line_ids),"Lines of Invoice and supplier invoice Line are not correspond"
- #TODO: check invoice line details with Expenses lines
--
- I pay the expenses.
--
- !python {model: hr.expense.expense}: |
- self.expense_paid(cr, uid, [ref('sep_expenses')])
--
- I check that state of expenses is 'Paid'.
--
- !assert {model: hr.expense.expense, id: sep_expenses, severity: error, string: Expense should be in Paid state}:
- - state == 'paid'
+ assert sep_expenses.state == 'done', "Expense should be in 'Done' state."
+ assert sep_expenses.voucher_id, "Expense should have link of Purchase Receipt."
+ assert sep_expenses.voucher_id.type == 'purchase', "Receipt type is not purchase receipt."
+ assert sep_expenses.voucher_id.amount == sep_expenses.amount,"Receipt total amount is not correspond with expense total."
+ assert len(sep_expenses.voucher_id.line_dr_ids) == len(sep_expenses.line_ids),"Lines of Receipt and expense line are not correspond."
+
-
I duplicate the expenses and cancel duplicated.
-