Create a new expense report
diff --git a/addons/hr_expense/views/res_config_settings_views.xml b/addons/hr_expense/views/res_config_settings_views.xml
index db7fb1bb799..b2c6be6d814 100644
--- a/addons/hr_expense/views/res_config_settings_views.xml
+++ b/addons/hr_expense/views/res_config_settings_views.xml
@@ -37,8 +37,10 @@
-
-
+
+
+
+
diff --git a/addons/hr_expense/wizard/hr_expense_approve_duplicate.py b/addons/hr_expense/wizard/hr_expense_approve_duplicate.py
index 680a02763eb..02de0115a41 100644
--- a/addons/hr_expense/wizard/hr_expense_approve_duplicate.py
+++ b/addons/hr_expense/wizard/hr_expense_approve_duplicate.py
@@ -29,6 +29,8 @@ class HrExpenseApproveDuplicate(models.TransientModel):
def action_approve(self):
self.sheet_ids._do_approve()
+ return {'type': 'ir.actions.act_window_close'}
def action_refuse(self):
- self.sheet_ids.refuse_sheet(_('Duplicate Expense'))
+ self.sheet_ids._do_refuse(_('Duplicate Expense'))
+ return {'type': 'ir.actions.act_window_close'}
diff --git a/addons/hr_expense/wizard/hr_expense_refuse_reason.py b/addons/hr_expense/wizard/hr_expense_refuse_reason.py
index f457c039cae..bce989fc8d6 100644
--- a/addons/hr_expense/wizard/hr_expense_refuse_reason.py
+++ b/addons/hr_expense/wizard/hr_expense_refuse_reason.py
@@ -5,41 +5,21 @@ from odoo import api, fields, models
class HrExpenseRefuseWizard(models.TransientModel):
- """This wizard can be launched from an he.expense (an expense line)
- or from an hr.expense.sheet (En expense report)
- 'hr_expense_refuse_model' must be passed in the context to differentiate
- the right model to use.
- """
+ """ Wizard to specify reason on expense sheet refusal """
_name = "hr.expense.refuse.wizard"
_description = "Expense Refuse Reason Wizard"
reason = fields.Char(string='Reason', required=True)
- hr_expense_ids = fields.Many2many('hr.expense')
- hr_expense_sheet_id = fields.Many2one('hr.expense.sheet')
+ sheet_ids = fields.Many2many('hr.expense.sheet')
@api.model
def default_get(self, fields):
- res = super(HrExpenseRefuseWizard, self).default_get(fields)
- active_ids = self.env.context.get('active_ids', [])
- refuse_model = self.env.context.get('hr_expense_refuse_model')
- if refuse_model == 'hr.expense':
- res.update({
- 'hr_expense_ids': active_ids,
- 'hr_expense_sheet_id': False,
- })
- elif refuse_model == 'hr.expense.sheet':
- res.update({
- 'hr_expense_sheet_id': active_ids[0] if active_ids else False,
- 'hr_expense_ids': [],
- })
+ res = super().default_get(fields)
+ if 'sheet_ids' in fields:
+ res['sheet_ids'] = self.env.context.get('active_ids', [])
return res
- def expense_refuse_reason(self):
- self.ensure_one()
- if self.hr_expense_ids:
- self.hr_expense_ids.refuse_expense(self.reason)
- if self.hr_expense_sheet_id:
- self.hr_expense_sheet_id.refuse_sheet(self.reason)
-
+ def action_refuse(self):
+ self.sheet_ids._do_refuse(self.reason)
return {'type': 'ir.actions.act_window_close'}
diff --git a/addons/hr_expense/wizard/hr_expense_refuse_reason_views.xml b/addons/hr_expense/wizard/hr_expense_refuse_reason_views.xml
index 45e6ba9d116..095007d194a 100644
--- a/addons/hr_expense/wizard/hr_expense_refuse_reason_views.xml
+++ b/addons/hr_expense/wizard/hr_expense_refuse_reason_views.xml
@@ -6,11 +6,10 @@
diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index 7220d0881d8..612e6cd64c2 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -894,6 +894,11 @@ class Project(models.Model):
def _get_profitability_sequence_per_invoice_type(self):
return {}
+ def _get_already_included_profitability_invoice_line_ids(self):
+ # To be extended to avoid account.move.line overlap between
+ # profitability reports.
+ return []
+
def _get_user_values(self):
return {
'is_project_user': self.user_has_groups('project.group_project_user'),
diff --git a/addons/project_hr_expense/models/project.py b/addons/project_hr_expense/models/project.py
index 2f7e6b5c17f..5efdc4e6311 100644
--- a/addons/project_hr_expense/models/project.py
+++ b/addons/project_hr_expense/models/project.py
@@ -68,11 +68,22 @@ class Project(models.Model):
sequence_per_invoice_type['expenses'] = 11
return sequence_per_invoice_type
+ def _get_already_included_profitability_invoice_line_ids(self):
+ # As both purchase orders and expenses (paid by employee) create vendor bills,
+ # we need to make sure they are exclusive in the profitability report.
+ move_line_ids = super()._get_already_included_profitability_invoice_line_ids()
+ query = self.env['account.move.line']._search([
+ ('move_id.expense_sheet_id', '!=', False),
+ ('id', 'not in', move_line_ids),
+ ])
+ query.order = None
+ return move_line_ids + list(query)
+
def _get_expenses_profitability_items(self, with_action=True):
if not self.analytic_account_id:
return {}
can_see_expense = with_action and self.user_has_groups('hr_expense.group_hr_expense_team_approver')
- query = self.env['hr.expense']._search([('is_refused', '=', False), ('state', 'in', ['approved', 'done'])])
+ query = self.env['hr.expense']._search([('state', 'in', ['approved', 'done'])])
query.order = None
query.add_where('hr_expense.analytic_distribution ? %s', [str(self.analytic_account_id.id)])
query_string, query_param = query.select('array_agg(id) as ids', 'SUM(untaxed_amount) as untaxed_amount')
diff --git a/addons/project_hr_expense/tests/test_project_profitability.py b/addons/project_hr_expense/tests/test_project_profitability.py
index 4338bfd12a8..d6499428f77 100644
--- a/addons/project_hr_expense/tests/test_project_profitability.py
+++ b/addons/project_hr_expense/tests/test_project_profitability.py
@@ -27,7 +27,7 @@ class TestProjectHrExpenseProfitabilityCommon(TestExpenseCommon):
'No data should be found since the sheet is not approved yet.',
)
- expense_sheet.approve_expense_sheets()
+ expense_sheet.action_approve_expense_sheets()
self.assertEqual(expense_sheet.state, 'approve')
return expense_sheet
@@ -65,7 +65,7 @@ class TestProjectHrExpenseProfitability(TestProjectProfitabilityCommon, TestProj
},
)
- expense_sheet.refuse_sheet('Test cancel expense')
+ expense_sheet._do_refuse('Test cancel expense')
self.assertDictEqual(
self.project._get_profitability_items(False),
self.project_profitability_items_empty,
diff --git a/addons/project_purchase/models/project.py b/addons/project_purchase/models/project.py
index f4eaaa3bc31..e7a51393183 100644
--- a/addons/project_purchase/models/project.py
+++ b/addons/project_purchase/models/project.py
@@ -134,7 +134,7 @@ class Project(models.Model):
**pol,
'invoice_lines': self.env['purchase.order.line'].browse(pol['id']).invoice_lines, # One2Many cannot be queried, they are not columns
} for pol in self._cr.dictfetchall()]
- purchase_order_line_invoice_line_ids = []
+ purchase_order_line_invoice_line_ids = self._get_already_included_profitability_invoice_line_ids()
if purchase_order_line_read:
amount_invoiced = amount_to_invoice = 0.0
purchase_order_line_ids = []
diff --git a/addons/project_sale_expense/models/project_project.py b/addons/project_sale_expense/models/project_project.py
index 911b570bba5..d2ad52329d1 100644
--- a/addons/project_sale_expense/models/project_project.py
+++ b/addons/project_sale_expense/models/project_project.py
@@ -13,7 +13,7 @@ class Project(models.Model):
if not self.analytic_account_id:
return {}
can_see_expense = with_action and self.user_has_groups('hr_expense.group_hr_expense_team_approver')
- query = self.env['hr.expense']._search([('is_refused', '=', False), ('state', 'in', ['approved', 'done'])])
+ query = self.env['hr.expense']._search([('state', 'in', ['approved', 'done'])])
query.add_where('hr_expense.analytic_distribution ? %s', [str(self.analytic_account_id.id)])
query.order = None
query_string, query_param = query.select('sale_order_id', 'product_id', 'array_agg(id) as ids', 'SUM(untaxed_amount) as untaxed_amount')
diff --git a/addons/project_sale_expense/tests/test_project_profitability.py b/addons/project_sale_expense/tests/test_project_profitability.py
index 4d6303bdacc..112f82ddfb4 100644
--- a/addons/project_sale_expense/tests/test_project_profitability.py
+++ b/addons/project_sale_expense/tests/test_project_profitability.py
@@ -118,7 +118,7 @@ class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestPr
{'id': 'expenses', 'sequence': expense_sequence, 'billed': -280.0, 'to_bill': 0.0},
)
- expense_sheet.refuse_sheet('Test Cancel Expense')
+ expense_sheet._do_refuse('Test Cancel Expense')
expense_profitability = self.project._get_expenses_profitability_items(False)
self.assertDictEqual(
expense_profitability.get('revenues', {}),
diff --git a/addons/sale_expense/tests/test_reinvoice.py b/addons/sale_expense/tests/test_reinvoice.py
index 0f5ffafc85d..eeb02c29999 100644
--- a/addons/sale_expense/tests/test_reinvoice.py
+++ b/addons/sale_expense/tests/test_reinvoice.py
@@ -82,7 +82,7 @@ class TestReInvoice(TestExpenseCommon, TestSaleCommon):
],
})
- expense_sheet.approve_expense_sheets()
+ expense_sheet.action_approve_expense_sheets()
expense_sheet.action_sheet_move_create()
self.assertRecordValues(sale_order.order_line, [
diff --git a/addons/sale_expense/tests/test_sale_expense.py b/addons/sale_expense/tests/test_sale_expense.py
index ea46f31b114..0313bfb3e47 100644
--- a/addons/sale_expense/tests/test_sale_expense.py
+++ b/addons/sale_expense/tests/test_sale_expense.py
@@ -8,7 +8,7 @@ from odoo.tests import tagged
@tagged('post_install', '-at_install')
class TestSaleExpense(TestExpenseCommon, TestSaleCommon):
-
+
def test_sale_expense(self):
""" Test the behaviour of sales orders when managing expenses """
@@ -32,7 +32,7 @@ class TestSaleExpense(TestExpenseCommon, TestSaleCommon):
# create some expense and validate it (expense at cost)
# Submit to Manager
sheet = self.env['hr.expense.sheet'].create({
- 'name': 'Expense for John Smith',
+ 'name': 'Expense for John Smith 1',
'employee_id': self.expense_employee.id,
'journal_id': self.company_data['default_journal_purchase'].id,
})
@@ -46,7 +46,7 @@ class TestSaleExpense(TestExpenseCommon, TestSaleCommon):
'sale_order_id': so.id,
})
# Approve
- sheet.approve_expense_sheets()
+ sheet.action_approve_expense_sheets()
# Create Expense Entries
sheet.action_sheet_move_create()
# expense should now be in sales order
@@ -70,7 +70,7 @@ class TestSaleExpense(TestExpenseCommon, TestSaleCommon):
})
# Submit to Manager
sheet = self.env['hr.expense.sheet'].create({
- 'name': 'Expense for John Smith',
+ 'name': 'Expense for John Smith 2',
'employee_id': self.expense_employee.id,
'journal_id': self.company_data['default_journal_purchase'].id,
})
@@ -86,7 +86,7 @@ class TestSaleExpense(TestExpenseCommon, TestSaleCommon):
'sale_order_id': so.id,
})
# Approve
- sheet.approve_expense_sheets()
+ sheet.action_approve_expense_sheets()
# Create Expense Entries
sheet.action_sheet_move_create()
# expense should now be in sales order
diff --git a/addons/sale_expense/views/hr_expense_sheet_views.xml b/addons/sale_expense/views/hr_expense_sheet_views.xml
index d768aef6931..2256b87172d 100644
--- a/addons/sale_expense/views/hr_expense_sheet_views.xml
+++ b/addons/sale_expense/views/hr_expense_sheet_views.xml
@@ -6,7 +6,7 @@
hr.expense.sheet
-