From 8dccf0cd9aef679bcce4e51261c3f31dbf08ebd0 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Wed, 6 May 2026 10:35:26 +0700 Subject: [PATCH] fix: improve expense payment computation logic and allow manual overrides in payment wizard --- models/hr_expense_payment_wizard.py | 2 +- models/hr_expense_realization.py | 7 ++++++- models/hr_expense_sheet.py | 16 +++++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/models/hr_expense_payment_wizard.py b/models/hr_expense_payment_wizard.py index 72dd7bb..bbfaf8a 100644 --- a/models/hr_expense_payment_wizard.py +++ b/models/hr_expense_payment_wizard.py @@ -6,7 +6,7 @@ class HrExpensePaymentWizard(models.TransientModel): _description = 'Expense Payment Wizard' expense_sheet_id = fields.Many2one('hr.expense.sheet', required=True) - amount = fields.Monetary(string='Payment Amount', required=True, readonly=True) + amount = fields.Monetary(string='Payment Amount', required=True) currency_id = fields.Many2one('res.currency', related='expense_sheet_id.currency_id') company_id = fields.Many2one('res.company', related='expense_sheet_id.company_id') diff --git a/models/hr_expense_realization.py b/models/hr_expense_realization.py index 1752ec5..0db513f 100644 --- a/models/hr_expense_realization.py +++ b/models/hr_expense_realization.py @@ -155,7 +155,12 @@ class HrExpenseRealization(models.Model): # Credit for Advance clearing # We clear the remaining advance amount in the first realization for an expense. - original_paid = self.expense_id.total_amount + # Use proportional share of actual amount paid if payment was different from requested total + sheet = self.expense_id.sheet_id + if sheet.total_amount: + original_paid = self.expense_id.total_amount * (sheet.amount_paid / sheet.total_amount) + else: + original_paid = 0.0 # Find already cleared advance amount from previous posted moves targeting this expense and advance account cleared_before = sum(self.env['account.move.line'].search([ diff --git a/models/hr_expense_sheet.py b/models/hr_expense_sheet.py index eb97e75..c469171 100644 --- a/models/hr_expense_sheet.py +++ b/models/hr_expense_sheet.py @@ -81,7 +81,7 @@ class HrExpenseSheet(models.Model): help="Total amount paid by the finance team (Total - Residual)." ) - @api.depends('account_move_ids.line_ids.matched_debit_ids', 'account_move_ids.line_ids.matched_credit_ids', 'total_amount', 'amount_residual') + @api.depends('account_move_ids.line_ids.matched_debit_ids', 'account_move_ids.line_ids.matched_credit_ids', 'account_move_ids.payment_id.amount', 'account_move_ids.payment_id.state', 'total_amount', 'amount_residual') def _compute_amount_paid(self): for sheet in self: total_paid = 0.0 @@ -120,6 +120,13 @@ class HrExpenseSheet(models.Model): total_paid += abs(counterpart.balance) seen_move_line_ids.add(counterpart.id) + # Direct payments linked to moves (handles non-reconciled company advances) + for move in sheet.account_move_ids: + if move.payment_id and move.payment_id.state not in ('draft', 'cancel'): + if move.payment_id.id not in seen_payment_ids: + total_paid += move.payment_id.amount + seen_payment_ids.add(move.payment_id.id) + # If no bank transactions found but report is clearly paid/partially paid, # fall back to standard calculation for non-bank flows (e.g. manual journal reconciliation) if not total_paid and sheet.total_amount != sheet.amount_residual: @@ -169,11 +176,14 @@ class HrExpenseSheet(models.Model): payments = moves.mapped('payment_id') unmatched_payments = payments.filtered(lambda p: not p.is_matched) - if unmatched_payments: + if sheet.amount_paid < sheet.total_amount: + sheet.payment_state = 'partial' + elif unmatched_payments: sheet.payment_state = 'in_payment' else: sheet.payment_state = 'paid' - sheet.amount_residual = 0. + + sheet.amount_residual = max(0.0, sheet.total_amount - sheet.amount_paid) else: sheet.payment_state = 'reversed' sheet.amount_residual = sum(sheet.account_move_ids.mapped('amount_residual'))