From 8f9ca3e29e21596cd317d976edaf454cd0f33bf1 Mon Sep 17 00:00:00 2001 From: "Julien Alardot (jual)" Date: Thu, 27 Apr 2023 08:31:00 +0000 Subject: [PATCH] [IMP] hr_expense: Split outstanding lines per expense When an expense sheet move is created, there is only one outstanding line, but it is usual to pay separately. That makes the reconciliation step harder. This makes sure one outstanding line is created per invoice_line and then if only one payment is made it can go through the same process as a batch payment Task-3292188 closes odoo/odoo#120450 Related: odoo/upgrade#4625 Signed-off-by: Laurent Smet --- addons/hr_expense/models/hr_expense.py | 59 +++++++++++-------- .../src/components/expense_dashboard.xml | 3 +- addons/hr_expense/static/src/views/list.js | 12 ++++ addons/hr_expense/static/src/views/list.xml | 4 ++ addons/hr_expense/views/hr_expense_views.xml | 38 +++++++----- 5 files changed, 77 insertions(+), 39 deletions(-) diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 4efbe0a6a8b..4231d9b894e 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -88,13 +88,18 @@ class HrExpense(models.Model): ("company_account", "Company") ], default='own_account', tracking=True, states={'done': [('readonly', True)], 'approved': [('readonly', True)], 'reported': [('readonly', True)]}, string="Paid By") attachment_number = fields.Integer('Number of Attachments', compute='_compute_attachment_number') - state = fields.Selection([ - ('draft', 'To Submit'), - ('reported', 'Submitted'), - ('approved', 'Approved'), - ('done', 'Paid'), - ('refused', 'Refused') - ], compute='_compute_state', string='Status', copy=False, index=True, readonly=True, store=True, default='draft') + state = fields.Selection( + selection=[ + ('draft', 'To Report'), + ('reported', 'To Submit'), + ('submitted', 'Submitted'), + ('approved', 'Approved'), + ('done', 'Done'), + ('refused', 'Refused') + ], + string='Status', default='draft', index=True, copy=False, + compute='_compute_state', readonly=True, store=True, + ) sheet_id = fields.Many2one('hr.expense.sheet', string="Expense Report", domain="[('employee_id', '=', employee_id), ('company_id', '=', company_id)]", readonly=True, copy=False) sheet_is_editable = fields.Boolean(compute='_compute_sheet_is_editable') approved_by = fields.Many2one('res.users', string='Approved By', related='sheet_id.user_id', tracking=False) @@ -152,16 +157,18 @@ class HrExpense(models.Model): @api.depends('sheet_id', 'sheet_id.account_move_id', 'sheet_id.state') def _compute_state(self): for expense in self: - if not expense.sheet_id or expense.sheet_id.state == 'draft': - expense.state = "draft" - elif expense.sheet_id.state == "cancel": - expense.state = "refused" - elif expense.sheet_id.state == "approve" or expense.sheet_id.state == "post": - expense.state = "approved" + if not expense.sheet_id: + expense.state = 'draft' + elif expense.sheet_id.state == 'draft': + expense.state = 'reported' + elif expense.sheet_id.state == 'cancel': + expense.state = 'refused' + elif expense.sheet_id.state in {'approve', 'post'}: + expense.state = 'approved' elif not expense.sheet_id.account_move_id: - expense.state = "reported" + expense.state = 'submitted' else: - expense.state = "done" + expense.state = 'done' @api.depends('quantity', 'unit_amount', 'tax_ids') def _compute_amount(self): @@ -213,7 +220,7 @@ class HrExpense(models.Model): def _compute_is_editable(self): is_account_manager = self.env.user.has_group('account.group_account_user') or self.env.user.has_group('account.group_account_manager') for expense in self: - if expense.state == 'draft' or expense.sheet_id.state in ['draft', 'submit']: + if expense.state in {'draft', 'reported'} or expense.sheet_id.state in {'draft', 'submit'}: expense.is_editable = True elif expense.sheet_id.state == 'approve': expense.is_editable = is_account_manager @@ -229,7 +236,7 @@ class HrExpense(models.Model): def _compute_is_ref_editable(self): is_account_manager = self.env.user.has_group('account.group_account_user') or self.env.user.has_group('account.group_account_manager') for expense in self: - if expense.state == 'draft' or expense.sheet_id.state in ['draft', 'submit']: + if expense.state in {'draft', 'reported'} or expense.sheet_id.state in {'draft', 'submit'}: expense.is_ref_editable = True else: expense.is_ref_editable = is_account_manager @@ -478,8 +485,7 @@ class HrExpense(models.Model): if not expenses: raise UserError(_('You have no expense to report')) - else: - return expenses.action_submit_expenses() + return expenses.action_submit_expenses() def action_submit_expenses(self): context_vals = self._get_default_expense_sheet_values() @@ -582,19 +588,22 @@ class HrExpense(models.Model): @api.model def get_expense_dashboard(self): expense_state = { - 'draft': { - 'description': _('to report'), + 'to_submit': { + 'description': _('to submit'), 'amount': 0.0, + 'tooltip': _("Expenses that need to be submitted to the approver."), 'currency': self.env.company.currency_id.id, }, - 'reported': { + 'submitted': { 'description': _('under validation'), 'amount': 0.0, + 'tooltip': _("Expenses from which the report has been submitted to the approver and is waiting for approval."), 'currency': self.env.company.currency_id.id, }, 'approved': { 'description': _('to be reimbursed'), 'amount': 0.0, + 'tooltip': _("Expenses from which the report is approved or posted. The payment still needs to be done."), 'currency': self.env.company.currency_id.id, } } @@ -605,9 +614,11 @@ class HrExpense(models.Model): [ ('employee_id', 'in', self.env.user.employee_ids.ids), ('payment_mode', '=', 'own_account'), - ('state', 'in', ['draft', 'reported', 'approved']) + ('state', 'in', ('draft', 'reported', 'submitted', 'approved')) ], ['state', 'currency_id'], ['total_amount:sum']) for state, currency, total_amount_sum in expenses: + if state in {'draft', 'reported'}: # Fusion the two states into only one "To Submit" state + state = 'to_submit' currency = currency or target_currency amount = currency._convert( total_amount_sum, target_currency, self.env.company, fields.Date.today()) @@ -800,7 +811,7 @@ class HrExpenseSheet(models.Model): product_ids = fields.Many2many('product.product', compute='_compute_product_ids', search='_search_product_ids', string='Categories') expense_number = fields.Integer(compute='_compute_expense_number', string='Number of Expenses') state = fields.Selection([ - ('draft', 'Draft'), + ('draft', 'To Submit'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), diff --git a/addons/hr_expense/static/src/components/expense_dashboard.xml b/addons/hr_expense/static/src/components/expense_dashboard.xml index 8d4a36e7726..b9a9e85e3af 100644 --- a/addons/hr_expense/static/src/components/expense_dashboard.xml +++ b/addons/hr_expense/static/src/components/expense_dashboard.xml @@ -5,7 +5,8 @@ -
+
diff --git a/addons/hr_expense/static/src/views/list.js b/addons/hr_expense/static/src/views/list.js index a8ad43b9f56..1340c32ccbc 100644 --- a/addons/hr_expense/static/src/views/list.js +++ b/addons/hr_expense/static/src/views/list.js @@ -34,6 +34,11 @@ export class ExpenseListController extends ListController { return records.length && records.every(record => record.data.state === 'draft') && this.isExpenseSheet; } + displayCreateReport() { + const records = this.model.root.selection; + return !this.isExpenseSheet && (records.length === 0 || records.some(record => record.data.state === "draft")) + } + displayApprove() { const records = this.model.root.selection; return this.userIsExpenseTeamApprover && records.length && records.every(record => record.data.state === 'submit') && this.isExpenseSheet; @@ -70,6 +75,13 @@ export class ExpenseListController extends ListController { this.render(true); } + async action_show_expenses_to_submit () { + const records = this.model.root.selection; + const res = await this.orm.call(this.model.rootParams.resModel, 'get_expenses_to_submit', [records.map((record) => record.resId)]); + if (res) { + await this.actionService.doAction(res, {}); + } + } } patch(ExpenseListController.prototype, 'expense_list_controller_upload', ExpenseDocumentUpload); diff --git a/addons/hr_expense/static/src/views/list.xml b/addons/hr_expense/static/src/views/list.xml index 92b4559274b..f0d4a4b115f 100644 --- a/addons/hr_expense/static/src/views/list.xml +++ b/addons/hr_expense/static/src/views/list.xml @@ -7,6 +7,10 @@ + diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 4799dfc43d3..cf1e13c1ed5 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -45,9 +45,6 @@ hr.expense -
-
@@ -69,12 +66,15 @@ - + - + - +
@@ -117,7 +117,10 @@
-
@@ -629,8 +632,10 @@ - - + + @@ -708,7 +713,11 @@ data-hotkey="c" attrs="{'invisible': [('state', '!=', 'post')]}" groups="account.group_account_readonly,account.group_account_invoice"/> - + + +
@@ -763,7 +772,7 @@ widget="many2many" mode="tree,kanban" domain="[ - ('state', '=', 'draft'), + ('state', '=', 'reported'), ('employee_id', '=', employee_id), ('company_id', '=', company_id), ('payment_mode', '=?', payment_mode), @@ -934,6 +943,8 @@ + + @@ -999,8 +1010,7 @@ hr.expense.sheet tree,kanban,form,pivot,graph,activity - [('state', '!=', 'cancel')] - {'search_default_my_reports': 1} + {'search_default_my_reports': 1, 'search_default_not_refused_reports': 1}

No expense report found. Let's create one!