Skip to content

Commit

Permalink
[12.0][IMP] hr_expense_invoice
Browse files Browse the repository at this point in the history
From expense sheet, add action "Create Invoice" from multiple expenses
Change the way reference invoice_id is checked.
- No more onchange invoice_id that set values to expense
- Instead, check amount on expense and invoice during post entry
- Change the way to allow reconcile with > 2 account move lines
kittiu authored and Saran440 committed Oct 7, 2021
1 parent 09bebb9 commit d3bbfad
Showing 15 changed files with 393 additions and 90 deletions.
1 change: 1 addition & 0 deletions hr_expense_invoice/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import models
from . import wizard
3 changes: 2 additions & 1 deletion hr_expense_invoice/__manifest__.py
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@

{
'name': 'Supplier invoices on HR expenses',
'version': '12.0.1.1.0',
'version': '12.0.1.2.0',
'category': 'Human Resources',
'author': 'Tecnativa, '
'Odoo Community Association (OCA)',
@@ -15,6 +15,7 @@
],
'data': [
'views/hr_expense_views.xml',
'wizard/expense_create_invoice.xml',
],
'installable': True,
}
2 changes: 2 additions & 0 deletions hr_expense_invoice/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from . import hr_expense
from . import hr_expense_sheet
from . import account_payment
from . import account_move_line
15 changes: 15 additions & 0 deletions hr_expense_invoice/models/account_move_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2019 Kitti U. <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, models


class AccountMoveLine(models.Model):
_inherit = 'account.move.line'

@api.multi
def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
if self._context.get('use_hr_expense_invoice'):
self = self.filtered(lambda l: not l.reconciled)
res = super().reconcile(writeoff_acc_id=writeoff_acc_id,
writeoff_journal_id=writeoff_journal_id)
return res
17 changes: 17 additions & 0 deletions hr_expense_invoice/models/account_payment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2019 Kitti U. <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, _
from odoo.exceptions import ValidationError


class AccountPayment(models.Model):
_inherit = 'account.payment'

def action_validate_invoice_payment(self):
# Do not allow register payment for invoices from expenses
expenses = self.env['hr.expense'].sudo().\
search([('invoice_id', 'in', self.invoice_ids.ids)])
if expenses:
raise ValidationError(_("Register payment on expense's "
"invoice is not allowed"))
return super(AccountPayment, self).action_validate_invoice_payment()
39 changes: 9 additions & 30 deletions hr_expense_invoice/models/hr_expense.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,8 @@
# Copyright 2017 Vicent Cubells <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.exceptions import UserError


class HrExpense(models.Model):
@@ -13,38 +14,16 @@ class HrExpense(models.Model):
string='Vendor Bill',
domain="[('type', '=', 'in_invoice'), ('state', '=', 'open')]",
oldname='invoice',
copy=False,
)

@api.onchange('invoice_id')
def onchange_invoice_id(self):
self.date = self.invoice_id.date_invoice
self.name = self.invoice_id.number
self.reference = self.invoice_id.number or self.invoice_id.reference
self.analytic_account_id = False
self.unit_amount = self.invoice_id.residual
self.quantity = 1.0
self.total_amount = self.unit_amount
self.description = self.invoice_id.reference

def _check_vals(self, vals):
if vals.get('invoice_id'):
# Rewrite values because readonly fields are not stored
invoice = self.env['account.invoice'].browse(vals['invoice_id'])
vals['date'] = invoice.date_invoice
vals['analytic_account_id'] = False
vals['unit_amount'] = invoice.residual
vals['total_amount'] = invoice.residual
vals['quantity'] = 1.0

@api.model
def create(self, vals):
self._check_vals(vals)
return super(HrExpense, self).create(vals)

@api.multi
def write(self, vals):
self._check_vals(vals)
return super(HrExpense, self).write(vals)
@api.constrains('invoice_id')
def _check_invoice_id(self):
for expense in self: # Only non binding expense
if not expense.sheet_id and expense.invoice_id and \
expense.invoice_id.state != 'open':
raise UserError(_("Vendor bill state must be Open"))

@api.multi
def _get_account_move_line_values(self):
99 changes: 72 additions & 27 deletions hr_expense_invoice/models/hr_expense_sheet.py
Original file line number Diff line number Diff line change
@@ -2,44 +2,89 @@
# Copyright 2017 Vicent Cubells <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, api, models
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from odoo.tools import float_compare


class HrExpenseSheet(models.Model):
_inherit = "hr.expense.sheet"

invoice_count = fields.Integer(
compute='_compute_invoice_count',
)
invoice_fully_created = fields.Boolean(
compute='_compute_invoice_count',
)

@api.multi
def action_sheet_move_create(self):
DecimalPrecision = self.env['decimal.precision']
precision = DecimalPrecision.precision_get('Product Price')
expense_line_ids = \
self.mapped('expense_line_ids').filtered('invoice_id')
self._validate_expense_invoice(expense_line_ids)
res = super(HrExpenseSheet, self).action_sheet_move_create()
move_lines = self.env['account.move'].search(
[('ref', 'in', self.mapped('name'))],
).mapped('line_ids')
for line in expense_line_ids:
partner = line.invoice_id.partner_id.commercial_partner_id
c_move_lines = move_lines.filtered(
lambda x:
x.partner_id == partner and
x.debit == line.invoice_id.residual and
not x.reconciled
)
if len(c_move_lines) > 1:
c_move_lines = c_move_lines[0]
residual = line.invoice_id.residual
c_move_lines |= line.invoice_id.move_id.line_ids.filtered(
lambda x:
x.account_id == line.invoice_id.account_id and
float_compare(x.credit, residual, precision) == 0)
if len(c_move_lines) != 2:
raise UserError(
_('Cannot reconcile supplier invoice payable with '
'generated line. Please check amounts and see '
'if the invoice is already added or paid. '
'Invoice: %s') % line.invoice_id.number)
for sheet in self:
move_lines = res[sheet.id].line_ids
expense_line_ids = self.expense_line_ids.filtered('invoice_id')
c_move_lines = self.env['account.move.line']
for line in expense_line_ids:
partner = line.invoice_id.partner_id.commercial_partner_id
c_move_lines |= move_lines.filtered(
lambda x:
x.partner_id == partner and x.debit and not x.reconciled)
c_move_lines |= line.invoice_id.move_id.line_ids.filtered(
lambda x:
x.account_id == line.invoice_id.account_id
and x.credit and not x.reconciled)
c_move_lines.reconcile()
return res

@api.multi
def _compute_invoice_count(self):
Invoice = self.env['account.invoice']
can_read = Invoice.check_access_rights('read', raise_exception=False)
for sheet in self:
sheet.invoice_count = can_read and \
len(sheet.expense_line_ids.mapped('invoice_id')) or 0
sheet.invoice_fully_created = \
not any(self.mapped('expense_line_ids').
filtered(lambda l: not l.invoice_id))

@api.model
def _validate_expense_invoice(self, expense_lines):
DecimalPrecision = self.env['decimal.precision']
precision = DecimalPrecision.precision_get('Product Price')
invoices = expense_lines.mapped('invoice_id')
if not invoices:
return
# All invoices must confirmed
if any(invoices.filtered(lambda i: i.state != 'open')):
raise UserError(_('Vendor bill state must be Open'))
expense_amount = sum(expense_lines.mapped('total_amount'))
invoice_amount = sum(invoices.mapped('residual'))
# Expense amount must equal invoice amount
if float_compare(expense_amount, invoice_amount, precision) != 0:
raise UserError(
_('Vendor bill amount mismatch!\nPlease make sure amount in '
'vendor bills equal to amount of its expense lines'))

@api.multi
def action_view_invoices(self):
self.ensure_one()
action = {
'name': _('Invoices'),
'type': 'ir.actions.act_window',
'res_model': 'account.invoice',
'target': 'current',
}
invoice_ids = self.expense_line_ids.mapped('invoice_id').ids
view = self.env.ref('account.invoice_supplier_form')
if len(invoice_ids) == 1:
invoice = invoice_ids[0]
action['res_id'] = invoice
action['view_mode'] = 'form'
action['views'] = [(view.id, 'form')]
else:
action['view_mode'] = 'tree,form'
action['domain'] = [('id', 'in', invoice_ids)]
return action
6 changes: 6 additions & 0 deletions hr_expense_invoice/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
This module should be used when a supplier invoice is paid by an employee. It
allows to set a supplier invoice for each expense line, adding the
corresponding journal items to transfer the debt to the employee.

There are 2 ways to reference expense to invoice.

1. On expense, directly select one invoice.
2. On expense report, use button "Create Vendor Bill" to create one invoice
for multiple expenses.
11 changes: 11 additions & 0 deletions hr_expense_invoice/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
**Reference one invoice to an expense**

* Create an expense sheet.
* Add an expense line to sheet with an invoice_id selected or create one new.
* Process expense sheet.
* On paying expense sheet, you are reconciling supplier invoice too.

**Create one invoice to multiple expenses**

* Create an expense sheet with one or multiple expense lines
* After approved, click button "Create Vendor Bill"
* Select multiple expense to create an invoice, and process it.
* New invoice will be create and link to the selected expense lines.
* Validate newly create invoice.
* On paying expense sheet, you are reconciling supplier invoice(s) too.
146 changes: 131 additions & 15 deletions hr_expense_invoice/tests/test_hr_expense_invoice.py
Original file line number Diff line number Diff line change
@@ -2,14 +2,16 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo.tests import common
from odoo.tests.common import Form
from odoo.exceptions import UserError, ValidationError


class TestHrExpenseInvoice(common.SavepointCase):
@classmethod
def setUpClass(cls):
super(TestHrExpenseInvoice, cls).setUpClass()

partner = cls.env['res.partner'].create({
cls.partner = cls.env['res.partner'].create({
'name': 'Test partner',
'supplier': True,
})
@@ -18,10 +20,12 @@ def setUpClass(cls):
})
receivable = cls.env.ref('account.data_account_type_receivable').id
expenses = cls.env.ref('account.data_account_type_expenses').id
invoice_account = cls.env['account.account'].search(
cls.invoice_account = cls.env['account.account'].search(
[('user_type_id', '=', receivable)], limit=1).id
invoice_line_account = cls.env['account.account'].search(
cls.invoice_line_account = cls.env['account.account'].search(
[('user_type_id', '=', expenses)], limit=1).id
cls.cash_journal = cls.env['account.journal'].search(
[('type', '=', 'cash')], limit=1)
product = cls.env['product.product'].create({
'name': 'Product test',
'type': 'service',
@@ -31,15 +35,15 @@ def setUpClass(cls):
'address_home_id': employee_home.id,
})
cls.invoice = cls.env['account.invoice'].create({
'partner_id': partner.id,
'account_id': invoice_account,
'partner_id': cls.partner.id,
'account_id': cls.invoice_account,
'type': 'in_invoice',
'invoice_line_ids': [(0, 0, {
'product_id': product.id,
'quantity': 1.0,
'price_unit': 100.0,
'name': 'product that cost 100',
'account_id': invoice_line_account,
'account_id': cls.invoice_line_account,
})]
})
cls.invoice2 = cls.invoice.copy({
@@ -48,7 +52,7 @@ def setUpClass(cls):
'quantity': 1.0,
'price_unit': 100.0,
'name': 'product that cost 100',
'account_id': invoice_line_account,
'account_id': cls.invoice_line_account,
})]
})
cls.sheet = cls.env['hr.expense.sheet'].create({
@@ -62,6 +66,20 @@ def setUpClass(cls):
'unit_amount': 50.0,
})
cls.expense2 = cls.expense.copy()
cls.expense3 = cls.expense.copy()

def _register_payment(self, sheet):
ctx = {'active_model': 'hr.expense.sheet',
'active_id': self.sheet.id,
'active_ids': [self.sheet.id],
'default_amount': self.sheet.total_amount,
'partner_id': self.sheet.address_id.id, }
with Form(self.env['hr.expense.sheet.register.payment.wizard'].
with_context(ctx)) as f:
f.journal_id = self.cash_journal
wizard = f.save()
wizard.expense_post_payment()
self.assertEqual(self.sheet.state, 'done')

def test_0_hr_test_no_invoice(self):
# There is not expense lines in sheet
@@ -78,18 +96,31 @@ def test_0_hr_test_no_invoice(self):
self.sheet.action_sheet_move_create()
self.assertEqual(self.sheet.state, 'post')
self.assertTrue(self.sheet.account_move_id)
# We make payment on expense sheet
self._register_payment(self.sheet)

def test_1_hr_test_invoice(self):
# There is not expense lines in sheet
# There is no expense lines in sheet
self.assertEqual(len(self.sheet.expense_line_ids), 0)
# We add an expense
self.expense.unit_amount = 100.0
self.sheet.expense_line_ids = [(6, 0, [self.expense.id])]
self.assertEqual(len(self.sheet.expense_line_ids), 1)
# We add invoice to expense
self.invoice.action_invoice_open()
self.expense.invoice_id = self.invoice.id
self.expense.onchange_invoice_id()
self.assertAlmostEqual(self.expense.total_amount, 100.0)
self.invoice.action_invoice_open() # residual = 100.0
self.expense.invoice_id = self.invoice
# Test that invoice can't register payment by itself
ctx = {'active_ids': [self.invoice.id],
'active_id': self.invoice.id,
'active_model': 'account.invoice'}
PaymentWizard = self.env['account.payment']
view_id = ('account.view_account_payment_invoice_form')
with Form(PaymentWizard.with_context(ctx), view=view_id) as f:
f.amount = 100.0
f.journal_id = self.cash_journal
payment = f.save()
with self.assertRaises(ValidationError):
payment.action_validate_invoice_payment()
# We approve sheet
self.sheet.approve_expense_sheets()
self.assertEqual(self.sheet.state, 'approve')
@@ -101,11 +132,15 @@ def test_1_hr_test_invoice(self):
self.assertTrue(self.sheet.account_move_id)
# Invoice is now paid
self.assertEqual(self.invoice.state, 'paid')
# We make payment on expense sheet
self._register_payment(self.sheet)

def test_2_hr_test_multi_invoices(self):
# There is not expense lines in sheet
# There is no expense lines in sheet
self.assertEqual(len(self.sheet.expense_line_ids), 0)
# We add 2 expenses
self.expense.unit_amount = 100.0
self.expense2.unit_amount = 100.0
self.sheet.expense_line_ids = [(6, 0, [self.expense.id,
self.expense2.id])]
self.assertEqual(len(self.sheet.expense_line_ids), 2)
@@ -114,8 +149,6 @@ def test_2_hr_test_multi_invoices(self):
self.invoice2.action_invoice_open()
self.expense.invoice_id = self.invoice.id
self.expense2.invoice_id = self.invoice2.id
self.expense.onchange_invoice_id()
self.expense2.onchange_invoice_id()
self.assertAlmostEqual(self.expense.total_amount, 100.0)
self.assertAlmostEqual(self.expense2.total_amount, 100.0)
# We approve sheet
@@ -129,3 +162,86 @@ def test_2_hr_test_multi_invoices(self):
self.assertTrue(self.sheet.account_move_id)
# Invoice is now paid
self.assertEqual(self.invoice.state, 'paid')
# We make payment on expense sheet
self._register_payment(self.sheet)

def test_3_hr_test_expense_create_invoice(self):
# There is no expense lines in sheet
self.assertEqual(len(self.sheet.expense_line_ids), 0)
# We add 3 expenses
self.sheet.expense_line_ids = [(6, 0, [self.expense.id,
self.expense2.id,
self.expense3.id,
])]
self.assertEqual(len(self.sheet.expense_line_ids), 3)
# We create 1st invoice from expense 1, 2
ctx = {'active_id': self.sheet.id,
'active_ids': [self.sheet.id],
'active_model': 'hr.expense.sheet'}
vals = {'expense_ids': [(6, 0, [self.expense.id, self.expense2.id])]}
Wizard = self.env['hr.expense.create.invoice']
with self.assertRaises(UserError):
wizard = Wizard.with_context(ctx).create(vals)
self.sheet.approve_expense_sheets()
wizard = Wizard.with_context(ctx).create(vals)
invoice = wizard.create_invoice()
# A new invoice is created
self.assertEqual([invoice.id],
self.sheet.expense_line_ids.mapped('invoice_id').ids)
self.assertEqual(self.sheet.invoice_count, 1)
res = self.sheet.action_view_invoices()
# Click on View Invoice button link to the correct invoice
self.assertEqual(res['res_id'], invoice.id)
# We create 2nd invoice from expense 3
ctx = {'active_id': self.sheet.id,
'active_ids': [self.sheet.id],
'active_model': 'hr.expense.sheet'}
vals = {'expense_ids': [(6, 0, [self.expense3.id])]}
Wizard = self.env['hr.expense.create.invoice']
self.sheet.approve_expense_sheets()
wizard = Wizard.with_context(ctx).create(vals)
invoice2 = wizard.create_invoice()
# Now there are 2 invoices
self.assertItemsEqual(
[invoice.id, invoice2.id],
self.sheet.expense_line_ids.mapped('invoice_id').ids)
self.sheet.invalidate_cache() # Make sure invoice_count is recalc
self.assertEqual(self.sheet.invoice_count, 2)
# We can't post entry now, we must validate invoice first
with self.assertRaises(UserError):
self.sheet.action_sheet_move_create()
# Validate Invoice
invoice.partner_id = self.partner
invoice.account_id = self.invoice_account
invoice2.partner_id = self.partner
invoice2.account_id = self.invoice_account
invoice.action_invoice_open()
invoice2.action_invoice_open()
self.sheet.action_sheet_move_create()
self.assertEqual(self.sheet.state, 'post')
self.assertTrue(self.sheet.account_move_id)
# Invoice are now paid
self.assertEqual(invoice.state, 'paid')
self.assertEqual(invoice2.state, 'paid')
# We make payment on expense sheet
self._register_payment(self.sheet)
# Click on View Invoice button link to the correct invoice
res = self.sheet.action_view_invoices()
self.assertEqual(res['view_mode'], 'tree,form')

def test_4_hr_expense_constraint(self):
# Only invoice with status open is allowed
with self.assertRaises(ValidationError):
self.expense.write({'invoice_id': self.invoice.id})
# We add an expense, total_amount now = 50.0
self.sheet.expense_line_ids = [(6, 0, [self.expense.id])]
# We add invoice to expense
self.invoice.action_invoice_open() # residual = 100.0
self.expense.invoice_id = self.invoice
# Amount must equal, expense vs invoice
expense_line_ids = \
self.sheet.mapped('expense_line_ids').filtered('invoice_id')
with self.assertRaises(UserError):
self.sheet._validate_expense_invoice(expense_line_ids)
self.expense.write({'unit_amount': 100.0}) # set to 100.0
self.sheet._validate_expense_invoice(expense_line_ids)
49 changes: 32 additions & 17 deletions hr_expense_invoice/views/hr_expense_views.xml
Original file line number Diff line number Diff line change
@@ -17,24 +17,39 @@
'quantity': quantity, 'date_invoice': date,
'account_id': account_id, 'invoice_line_tax_ids': tax_ids, }], }" />
</field>
<xpath expr="//field[@name='date']" position="attributes">
<attribute name="attrs">{'readonly': [('invoice_id', '!=', False)]}</attribute>
</xpath>
<xpath expr="//field[@name='analytic_account_id']" position="attributes">
<attribute name="attrs">{'readonly': [('invoice_id', '!=', False)]}</attribute>
</xpath>
<xpath expr="//field[@name='product_uom_id']" position="attributes">
<attribute name="attrs">{'readonly': [('invoice_id', '!=', False)]}</attribute>
</xpath>
<xpath expr="//field[@name='unit_amount']" position="attributes">
<attribute name="attrs">{'readonly': [('invoice_id', '!=', False)]}</attribute>
</xpath>
<xpath expr="//field[@name='total_amount']" position="attributes">
<attribute name="attrs">{'readonly': [('invoice_id', '!=', False)]}</attribute>
</xpath>
<xpath expr="//field[@name='quantity']" position="attributes">
<attribute name="attrs">{'readonly': [('invoice_id', '!=', False)]}</attribute>
</field>
</record>

<record id="action_expense_create_invoice" model="ir.actions.act_window">
<field name="name">Create Vendor Bill</field>
<field name="res_model">hr.expense.create.invoice</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>

<record id="hr_expense_sheet_form_view_inherit_sale_expense" model="ir.ui.view">
<field name="name">hr.expense.sheet.form.inherit.sale.expense</field>
<field name="model">hr.expense.sheet</field>
<field name="inherit_id" ref="hr_expense.view_hr_expense_sheet_form"/>
<field name="arch" type="xml">
<button name="action_sheet_move_create" position="after">
<field name="invoice_fully_created" invisible="1"/>
<button name="%(action_expense_create_invoice)d" type="action"
string="Create Vendor Bill" groups="account.group_account_invoice" class="oe_highlight"
attrs="{'invisible': ['|', ('state', '!=', 'approve'), ('invoice_fully_created', '=', True)]}"/>
</button>
<xpath expr="//field[@name='expense_line_ids']/tree/field[@name='name']" position="after">
<field name="invoice_id"/>
</xpath>
<div class="oe_button_box" position="inside">
<button class="oe_stat_button" icon="fa-book"
name="action_view_invoices" type="object"
attrs="{'invisible': [('invoice_count', '=', 0)]}"
groups="account.group_account_invoice">
<field name="invoice_count" widget="statinfo" string="Invoices"/>
</button>
</div>
</field>
</record>

2 changes: 2 additions & 0 deletions hr_expense_invoice/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import expense_create_invoice
from . import hr_expense_sheet_register_payment
60 changes: 60 additions & 0 deletions hr_expense_invoice/wizard/expense_create_invoice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2019 Kitti U. <kittiu@ecosoft.co.th>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _
from odoo.exceptions import UserError


class HRExpenseCreateInvoice(models.TransientModel):
_name = 'hr.expense.create.invoice'
_description = 'Create invoice from expense report'

expense_ids = fields.Many2many(
comodel_name='hr.expense',
string='Expenses',
domain=lambda self: self._domain_expense_ids(),
required=True,
)

@api.model
def view_init(self, fields):
active_id = self._context.get('active_id')
sheet = self.env['hr.expense.sheet'].browse(active_id)
if sheet.state != 'approve':
raise UserError(_('This action is allowed only in Approved sate'))
return super().view_init(fields)

@api.model
def _domain_expense_ids(self):
active_id = self._context.get('active_id')
sheet = self.env['hr.expense.sheet'].browse(active_id)
domain = [('id', 'in', sheet.expense_line_ids.ids),
('invoice_id', '=', False)]
return domain

@api.multi
def create_invoice(self):
"""Use information from selected invoice to create invoice."""
self.ensure_one()
expenses = self.expense_ids.filtered(lambda l: not l.invoice_id)
if not expenses:
raise UserError(_('No valid expenses to create invoice'))
expense = expenses[0]
invoice_lines = [
(0, 0, {'product_id': x.product_id.id,
'name': x.name,
'price_unit': x.unit_amount,
'quantity': x.quantity,
'date_invoice': x.date,
'account_id': x.account_id.id,
'invoice_line_tax_ids': [(6, 0, x.tax_ids.ids)], })
for x in expenses
]
invoice_vals = {
'type': 'in_invoice',
'journal_type': 'purchase',
'reference': expense.reference,
'date_invoice': expense.date,
'invoice_line_ids': invoice_lines, }
invoice = self.env['account.invoice'].create(invoice_vals)
self.expense_ids.write({'invoice_id': invoice.id})
return invoice
21 changes: 21 additions & 0 deletions hr_expense_invoice/wizard/expense_create_invoice.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>

<record model="ir.ui.view" id="hr_expense_create_invoice_form">
<field name="name">hr.expense.create.invoice.form</field>
<field name="model">hr.expense.create.invoice</field>
<field name="arch" type="xml">
<form string="Create Vendor Bill from Expense">
<separator string="Please select expense line to create new vendor bill:"/>
<group>
<field name="expense_ids" nolabel="1" options="{'no_create': True}"/>
</group>
<footer>
<button name="create_invoice" string="Create Invoice" type="object" class="btn-primary"/>
<button class="btn-secondary" special="cancel" string="Cancel" />
</footer>
</form>
</field>
</record>

</odoo>
12 changes: 12 additions & 0 deletions hr_expense_invoice/wizard/hr_expense_sheet_register_payment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2019 Kitti U. <kittiu@ecosoft.co.th>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, models


class HrExpenseSheetRegisterPaymentWizard(models.TransientModel):
_inherit = 'hr.expense.sheet.register.payment.wizard'

@api.multi
def expense_post_payment(self):
self = self.with_context(use_hr_expense_invoice=True)
return super().expense_post_payment()

0 comments on commit d3bbfad

Please sign in to comment.