From 990cbac9453535c05c419cb2646fdf4f93a53f60 Mon Sep 17 00:00:00 2001 From: yueyang <2495544247@qq.com> Date: Sat, 24 Oct 2026 17:48:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B7=A8=E6=9C=88=E5=B7=AE?= =?UTF-8?q?=E9=A2=9D=E7=AD=9B=E9=80=89=E9=87=8D=E5=A4=8D=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E9=A1=B5=E9=9D=A2=E7=BC=93=E6=85=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Business/Pages.php | 79 ++++++++++++++----- .../PerformanceDifferenceRemarkTest.php | 20 +++++ 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index f3cf6b39..eb300e7e 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -160,6 +160,8 @@ class Pages extends Controller '订单实付按应付/已付核对', ]; + private const RECEIPT_CROSS_MONTH_REMARK = '差额=跨月收款(合并已对平)'; + /** 业绩明细「异常标注」筛选项,取值与同步命令写入的标注一致。 */ private const RECEIPT_REMARK_OPTIONS = [ '账单无单号', @@ -341,7 +343,7 @@ class Pages extends Controller $effective = 'COALESCE(difference, ' . self::receiptNetSql() . ')'; // 同一交易单号在相邻月份的收入/优惠/冲减合并后与订单实付对平时, // 这是最能解释单月差额的原因,必须排在其它逐月规则之前。 - $rules['差额=跨月收款(合并已对平)'] = 'cross_month_balanced = 1'; + $rules[self::RECEIPT_CROSS_MONTH_REMARK] = 'cross_month_balanced = 1'; $rules[$label . '未匹配'] = "match_status = '未匹配'"; $rules[$label . '取数为0(种菜)'] = "match_status = '已匹配' AND planting_amount IS NOT NULL AND ABS(COALESCE($column, 0)) < 0.005"; $rules[$label . '取数为0'] = "match_status = '已匹配' AND planting_amount IS NULL AND ABS(COALESCE($column, 0)) < 0.005"; @@ -658,7 +660,7 @@ class Pages extends Controller } if ((int) ($row->cross_month_balanced ?? 0) === 1) { - return '差额=跨月收款(合并已对平)'; + return self::RECEIPT_CROSS_MONTH_REMARK; } if (($row->match_status ?? '') === '未匹配') { @@ -1398,11 +1400,12 @@ class Pages extends Controller $platformFilter = $this->receiptOrderMatchFilterValues(request('platform')); $storeFilter = $this->receiptOrderMatchFilterValues(request('store_name')); [$statusFilter, $remarkFilter] = $this->receiptOrderMatchStatusFilters(request('match_status'), request('remark')); + $crossMonthOnly = self::isReceiptCrossMonthOnlyFilter($remarkFilter); $unmatchedReasonFilter = $this->receiptOrderMatchFilterValues(request('unmatched_reason')); $differenceState = $this->receiptOrderMatchFilterValues(request('difference_state')); $orderNo = trim((string) request('online_order_no', '')); // 闭包里要用 $this->applyReceiptRemarkFilter()(差异原因备注按公式判定),所以不能声明为 static - $applyFilters = function ($query) use ($platformFilter, $storeFilter, $orderNo, $statusFilter, $remarkFilter, $unmatchedReasonFilter, $differenceState): void { + $applyFilters = function ($query) use ($platformFilter, $storeFilter, $orderNo, $statusFilter, $remarkFilter, $crossMonthOnly, $unmatchedReasonFilter, $differenceState): void { if ($platformFilter !== []) { $query->whereIn('platform', $platformFilter); } @@ -1423,7 +1426,7 @@ class Pages extends Controller $this->applyReceiptUnmatchedReasonFilter($query, $unmatchedReasonFilter); } if ($differenceState !== []) { - $this->applyReceiptDifferenceStateFilter($query, $differenceState); + $this->applyReceiptDifferenceStateFilter($query, $differenceState, $crossMonthOnly); } }; @@ -1458,7 +1461,7 @@ class Pages extends Controller ")->first(); $total = (int) ($agg->total ?? 0); $differenceTotal = (float) ($agg->difference_base ?? 0); - if ((int) ($agg->basis_fallback_rows ?? 0) > 0) { + if (! $crossMonthOnly && (int) ($agg->basis_fallback_rows ?? 0) > 0) { $adjustmentQuery = $base(); $applyFilters($adjustmentQuery); $adjustment = $adjustmentQuery @@ -1614,7 +1617,9 @@ class Pages extends Controller } $query = $this->receiptOrderMatchFilteredQuery($month); - $total = $this->receiptOrderMatchMetricTotal($query, $metric); + [, $remarkFilter] = $this->receiptOrderMatchStatusFilters(null, request('remark')); + $crossMonthOnly = self::isReceiptCrossMonthOnlyFilter($remarkFilter); + $total = $this->receiptOrderMatchMetricTotal($query, $metric, $crossMonthOnly); $connection = (string) config('upstream_mirrors.connection', 'business_mirror'); $detailMetric = $definition['metric']; @@ -1757,6 +1762,7 @@ class Pages extends Controller request('match_status'), request('remark'), ); + $crossMonthOnly = self::isReceiptCrossMonthOnlyFilter($remarkFilter); $unmatchedReasonFilter = $this->receiptOrderMatchFilterValues(request('unmatched_reason')); $differenceState = $this->receiptOrderMatchFilterValues(request('difference_state')); $orderNo = trim((string) request('online_order_no', '')); @@ -1785,13 +1791,13 @@ class Pages extends Controller $this->applyReceiptUnmatchedReasonFilter($query, $unmatchedReasonFilter); } if ($differenceState !== []) { - $this->applyReceiptDifferenceStateFilter($query, $differenceState); + $this->applyReceiptDifferenceStateFilter($query, $differenceState, $crossMonthOnly); } return $query; } - private function receiptOrderMatchMetricTotal($query, string $metric): float + private function receiptOrderMatchMetricTotal($query, string $metric, bool $crossMonthOnly = false): float { if ($metric !== 'difference') { return round((float) (clone $query)->sum($metric), 2); @@ -1803,7 +1809,7 @@ class Pages extends Controller . "SUM(CASE WHEN remark IN ({$fallbackSql}) THEN 1 ELSE 0 END) as basis_fallback_rows" )->first(); $total = (float) ($row->difference_base ?? 0); - if ((int) ($row->basis_fallback_rows ?? 0) > 0) { + if (! $crossMonthOnly && (int) ($row->basis_fallback_rows ?? 0) > 0) { $adjustment = (clone $query) ->whereIn('remark', self::RECEIPT_BASIS_FALLBACK_REMARKS) ->selectRaw('SUM(' . self::receiptFallbackDifferenceAdjustmentSql() . ') as adjustment') @@ -1827,20 +1833,44 @@ class Pages extends Controller } $labels = self::receiptDifferenceRemarkLabels(); + $crossMonthSelected = in_array(self::RECEIPT_CROSS_MONTH_REMARK, $remarkFilter, true); $indexes = []; foreach ($remarkFilter as $label) { + if ($label === self::RECEIPT_CROSS_MONTH_REMARK) { + continue; + } $index = array_search($label, $labels, true); if ($index !== false) { $indexes[] = (int) $index + 1; } } - if ($indexes === []) { + if (! $crossMonthSelected && $indexes === []) { return; } - $query->whereRaw( - self::receiptDifferenceRemarkIndexSql() . ' IN (' . implode(', ', array_unique($indexes)) . ')' - ); + if ($crossMonthSelected && $indexes === []) { + $query->where('cross_month_balanced', 1); + + return; + } + + $query->where(function ($group) use ($crossMonthSelected, $indexes): void { + if ($crossMonthSelected) { + $group->where('cross_month_balanced', 1); + } + if ($indexes !== []) { + $condition = self::receiptDifferenceRemarkIndexSql() + . ' IN (' . implode(', ', array_unique($indexes)) . ')'; + $crossMonthSelected ? $group->orWhereRaw($condition) : $group->whereRaw($condition); + } + }); + } + + /** @param list $remarkFilter */ + private static function isReceiptCrossMonthOnlyFilter(array $remarkFilter): bool + { + return count($remarkFilter) === 1 + && $remarkFilter[0] === self::RECEIPT_CROSS_MONTH_REMARK; } /** @@ -1876,7 +1906,7 @@ class Pages extends Controller * @param \Illuminate\Database\Query\Builder $query * @param string|list $state */ - private function applyReceiptDifferenceStateFilter($query, $state): void + private function applyReceiptDifferenceStateFilter($query, $state, bool $crossMonthOnly = false): void { $states = is_array($state) ? $state : [$state]; $has = in_array('有差额', $states, true); @@ -1884,7 +1914,11 @@ class Pages extends Controller if ($has === $none) { return; } - $effective = self::receiptEffectiveDifferenceSql(); + // 已限定跨月第一优先级时,跨月行不会再落入口径兜底归零, + // “有/无差额”可直接使用默认差额,避免整串原因 CASE 重复执行。 + $effective = $crossMonthOnly + ? self::receiptDefaultDifferenceSql() + : self::receiptEffectiveDifferenceSql(); $query->whereRaw($has ? 'ABS(' . $effective . ') > 0.005' : 'ABS(' . $effective . ') <= 0.005'); } @@ -1897,6 +1931,9 @@ class Pages extends Controller */ private function receiptDifferenceStateFacet(string $month, array $filters, string $orderNo): array { + $crossMonthOnly = self::isReceiptCrossMonthOnlyFilter( + array_values((array) ($filters['remark'] ?? [])) + ); $query = DB::connection((string) config('upstream_mirrors.connection', 'business_mirror')) ->table('store_receipt_order_match_rows') ->where('report_month', $month) @@ -1920,7 +1957,9 @@ class Pages extends Controller $query->where('online_order_no', 'like', '%' . addcslashes($orderNo, '%_\\') . '%'); } - $effective = self::receiptEffectiveDifferenceSql(); + $effective = $crossMonthOnly + ? self::receiptDefaultDifferenceSql() + : self::receiptEffectiveDifferenceSql(); $row = $query->selectRaw('SUM(CASE WHEN ABS(' . $effective . ') > 0.005 THEN 1 ELSE 0 END) as has_diff, ' . 'SUM(CASE WHEN ABS(' . $effective . ') <= 0.005 THEN 1 ELSE 0 END) as none_diff')->first(); @@ -2229,6 +2268,9 @@ class Pages extends Controller */ private function receiptOrderMatchFacet(string $month, string $column, array $filters, string $orderNo, array $order = []): array { + $crossMonthOnly = self::isReceiptCrossMonthOnlyFilter( + array_values((array) ($filters['remark'] ?? [])) + ); $query = DB::connection((string) config('upstream_mirrors.connection', 'business_mirror')) ->table('store_receipt_order_match_rows') ->where('report_month', $month); @@ -2249,7 +2291,7 @@ class Pages extends Controller } // 差额(有/无):按页面差额口径过滤 if ($key === 'difference_state') { - $this->applyReceiptDifferenceStateFilter($query, (array) $values); + $this->applyReceiptDifferenceStateFilter($query, (array) $values, $crossMonthOnly); continue; } $query->whereIn($key, $values); @@ -2413,6 +2455,7 @@ class Pages extends Controller } $statusFilter = $this->receiptOrderMatchFilterValues($request->query('match_status')); [$statusValues, $remarkValues] = $this->receiptOrderMatchStatusFilters($statusFilter, $request->query('remark')); + $crossMonthOnly = self::isReceiptCrossMonthOnlyFilter($remarkValues); if ($statusValues !== []) { $query->whereIn('match_status', $statusValues); } @@ -2425,7 +2468,7 @@ class Pages extends Controller } $differenceState = $this->receiptOrderMatchFilterValues($request->query('difference_state')); if ($differenceState !== []) { - $this->applyReceiptDifferenceStateFilter($query, $differenceState); + $this->applyReceiptDifferenceStateFilter($query, $differenceState, $crossMonthOnly); } // 「订单实付含已退款部分」「订单实付未减联合营销费」判定所需证据列(与页面同一口径;仅对应平台负差额行会真正计算子查询)。 diff --git a/tests/Unit/EcommerceFinance/PerformanceDifferenceRemarkTest.php b/tests/Unit/EcommerceFinance/PerformanceDifferenceRemarkTest.php index 0ed24aeb..bc5048aa 100644 --- a/tests/Unit/EcommerceFinance/PerformanceDifferenceRemarkTest.php +++ b/tests/Unit/EcommerceFinance/PerformanceDifferenceRemarkTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit\EcommerceFinance; use App\Http\Controllers\Business\Pages; +use Illuminate\Support\Facades\DB; use ReflectionMethod; use Tests\TestCase; @@ -74,6 +75,25 @@ class PerformanceDifferenceRemarkTest extends TestCase ])); } + public function test_cross_month_filters_use_direct_columns_without_recomputing_reason_case(): void + { + $controller = app(Pages::class); + $query = DB::connection('business_mirror')->table('store_receipt_order_match_rows'); + + $remarkFilter = new ReflectionMethod(Pages::class, 'applyReceiptRemarkFilter'); + $remarkFilter->setAccessible(true); + $remarkFilter->invoke($controller, $query, ['差额=跨月收款(合并已对平)']); + + $this->assertStringContainsString('cross_month_balanced', $query->toSql()); + $this->assertStringNotContainsString('CASE WHEN', $query->toSql()); + + $differenceFilter = new ReflectionMethod(Pages::class, 'applyReceiptDifferenceStateFilter'); + $differenceFilter->setAccessible(true); + $differenceFilter->invoke($controller, $query, ['有差额'], true); + + $this->assertStringNotContainsString('CASE WHEN', $query->toSql()); + } + public function test_pinduoduo_partial_refund_marks_platform_payment_discount(): void { $this->assertSame('差额=平台支付立减', $this->remark([ -- 2.47.2