diff --git a/app/Http/Controllers/Admin/PlatformOrderController.php b/app/Http/Controllers/Admin/PlatformOrderController.php index d577e45..e600ebb 100644 --- a/app/Http/Controllers/Admin/PlatformOrderController.php +++ b/app/Http/Controllers/Admin/PlatformOrderController.php @@ -651,8 +651,14 @@ class PlatformOrderController extends Controller if ((float) $data['amount'] > 0 && in_array($order->payment_status, ['paid', 'partially_refunded'], true)) { $paidAmount = (float) ($order->paid_amount ?? 0); - // 退款总额 >= 已付金额 => 视为已退款;否则视为部分退款 - if ($paidAmount > 0 && $totalRefunded >= $paidAmount) { + // 退款总额 + 容差 >= 已付金额 => 视为已退款;否则视为部分退款(与 refund_inconsistent 口径一致) + $tol = (float) config('saasshop.amounts.tolerance', 0.01); + $tolCents = (int) round($tol * 100); + $tolCents = max(1, $tolCents); + $paidCents = (int) round($paidAmount * 100); + $refundCents = (int) round($totalRefunded * 100); + + if ($paidCents > 0 && ($refundCents + $tolCents) >= $paidCents) { $order->payment_status = 'refunded'; $order->refunded_at = $order->refunded_at ?: now(); } else { @@ -680,9 +686,14 @@ class PlatformOrderController extends Controller return redirect()->back()->with('warning', '当前订单已是已退款状态,无需重复操作。'); } - // 安全阀:仅允许在“退款总额已达到/超过已付金额”时标记为已退款 + // 安全阀:仅允许在“退款总额已达到/超过已付金额 + 容差”时标记为已退款 $refundTotal = (float) $this->refundTotalForOrder($order); - if (round($refundTotal * 100) + 1 < round($paidAmount * 100)) { + + $tol = (float) config('saasshop.amounts.tolerance', 0.01); + $tolCents = (int) round($tol * 100); + $tolCents = max(1, $tolCents); + + if (round($refundTotal * 100) + $tolCents < round($paidAmount * 100)) { return redirect()->back()->with('warning', '退款总额尚未达到已付金额,无法标记为已退款。请先核对/补齐退款记录。'); } @@ -724,13 +735,18 @@ class PlatformOrderController extends Controller return redirect()->back()->with('warning', '当前订单已是部分退款状态,无需重复操作。'); } - // 安全阀:部分退款需要“退款总额>0 且未达到已付金额” + // 安全阀:部分退款需要“退款总额>0 且 未达到已付金额 + 容差” $refundTotal = (float) $this->refundTotalForOrder($order); + + $tol = (float) config('saasshop.amounts.tolerance', 0.01); + $tolCents = (int) round($tol * 100); + $tolCents = max(1, $tolCents); + if (round($refundTotal * 100) <= 0) { return redirect()->back()->with('warning', '退款总额为 0,无法标记为部分退款。'); } - if (round($refundTotal * 100) + 1 >= round($paidAmount * 100)) { - return redirect()->back()->with('warning', '退款总额已达到/超过已付金额,建议标记为已退款。'); + if (round($refundTotal * 100) + $tolCents >= round($paidAmount * 100)) { + return redirect()->back()->with('warning', '退款总额已达到/超过已付金额(考虑容差),建议标记为已退款。'); } $now = now(); diff --git a/tests/Feature/AdminPlatformOrderRefundReceiptAccumulateToFullyRefundedTest.php b/tests/Feature/AdminPlatformOrderRefundReceiptAccumulateToFullyRefundedTest.php index dcc25ef..ac60fe0 100644 --- a/tests/Feature/AdminPlatformOrderRefundReceiptAccumulateToFullyRefundedTest.php +++ b/tests/Feature/AdminPlatformOrderRefundReceiptAccumulateToFullyRefundedTest.php @@ -68,11 +68,11 @@ class AdminPlatformOrderRefundReceiptAccumulateToFullyRefundedTest extends TestC $order->refresh(); $this->assertSame('partially_refunded', $order->payment_status); - // 第二次退款 20 -> 累计 30,应推进到 refunded + // 第二次退款 20.01 -> 累计 30.01(超过 tol=0.01),应推进到 refunded $this->post('/admin/platform-orders/' . $order->id . '/add-refund-receipt', [ 'type' => 'refund', 'channel' => 'wechat', - 'amount' => 20, + 'amount' => 20.01, 'refunded_at' => now()->addMinute()->format('Y-m-d H:i:s'), 'note' => '第二次退款', ])->assertRedirect(); @@ -83,6 +83,6 @@ class AdminPlatformOrderRefundReceiptAccumulateToFullyRefundedTest extends TestC $this->assertNotNull($order->refunded_at); $refundSummaryTotal = (float) data_get($order->meta, 'refund_summary.total_amount'); - $this->assertSame(30.0, $refundSummaryTotal); + $this->assertSame(30.01, $refundSummaryTotal); } }