diff --git a/modules/EcommerceFinance/Repositories/TaobaoAlipaySettlementBillRepository.php b/modules/EcommerceFinance/Repositories/TaobaoAlipaySettlementBillRepository.php index b02654ea..b05f87a5 100644 --- a/modules/EcommerceFinance/Repositories/TaobaoAlipaySettlementBillRepository.php +++ b/modules/EcommerceFinance/Repositories/TaobaoAlipaySettlementBillRepository.php @@ -262,6 +262,7 @@ class TaobaoAlipaySettlementBillRepository ->where('alipay_account', '<>', '') ->when($store !== '', fn (Builder $query) => $query->where('store_name', $store)) ->select([ + 'store_name', 'alipay_account', 'occurred_at', 'source_row_number', @@ -308,6 +309,7 @@ class TaobaoAlipaySettlementBillRepository ->where('alipay_account', '<>', '') ->when($store !== '', fn (Builder $query) => $query->where('store_name', $store)) ->select([ + 'store_name', 'alipay_account', 'occurred_at', 'source_row_number', diff --git a/modules/EcommerceFinance/Services/StoreReceivingBalanceOverviewService.php b/modules/EcommerceFinance/Services/StoreReceivingBalanceOverviewService.php index 8672a0ba..beef5537 100644 --- a/modules/EcommerceFinance/Services/StoreReceivingBalanceOverviewService.php +++ b/modules/EcommerceFinance/Services/StoreReceivingBalanceOverviewService.php @@ -372,15 +372,37 @@ class StoreReceivingBalanceOverviewService throw new RuntimeException('店铺期末余额明细暂时无法读取,请稍后重试。', 0, $exception); } - $platformRows = []; + $rowsByStore = []; foreach ($rows as $row) { $storeName = trim((string) ($row['store_name'] ?? '')); $storeName = $storeName !== '' ? $storeName : '未标注店铺'; - $opening = is_numeric($row['opening_balance'] ?? null) - ? round((float) $row['opening_balance'], 2) - : null; - $income = round((float) ($row['income'] ?? 0), 2); - $expense = round((float) ($row['expense'] ?? 0), 2); + $rowsByStore[$storeName] ??= [ + 'opening_balance' => null, + 'income' => 0.0, + 'expense' => 0.0, + ]; + if (is_numeric($row['opening_balance'] ?? null)) { + $rowsByStore[$storeName]['opening_balance'] = round( + (float) ($rowsByStore[$storeName]['opening_balance'] ?? 0) + + (float) $row['opening_balance'], + 2, + ); + } + $rowsByStore[$storeName]['income'] = round( + (float) $rowsByStore[$storeName]['income'] + (float) ($row['income'] ?? 0), + 2, + ); + $rowsByStore[$storeName]['expense'] = round( + (float) $rowsByStore[$storeName]['expense'] + (float) ($row['expense'] ?? 0), + 2, + ); + } + + $platformRows = []; + foreach ($rowsByStore as $storeName => $row) { + $opening = $row['opening_balance']; + $income = $row['income']; + $expense = $row['expense']; $amount = $opening === null ? null : round($opening + $income - $expense, 2); diff --git a/tests/Unit/EcommerceFinance/StoreReceivingBalanceOverviewServiceTest.php b/tests/Unit/EcommerceFinance/StoreReceivingBalanceOverviewServiceTest.php index db7f23ea..1d55ae6c 100644 --- a/tests/Unit/EcommerceFinance/StoreReceivingBalanceOverviewServiceTest.php +++ b/tests/Unit/EcommerceFinance/StoreReceivingBalanceOverviewServiceTest.php @@ -284,6 +284,42 @@ class StoreReceivingBalanceOverviewServiceTest extends TestCase $this->assertFalse($unavailable['available']); } + public function test_balance_store_detail_merges_duplicate_store_rows_without_creating_zero_opening(): void + { + $service = new class extends StoreReceivingBalanceOverviewService { + public function __construct() + { + } + + protected function fetchPlatformStoreBalances(string $platform, string $start, string $end): array + { + return [[ + 'store_name' => '重复店铺', + 'opening_balance' => null, + 'income' => 5.0, + 'expense' => 0.0, + ], [ + 'store_name' => '重复店铺', + 'opening_balance' => null, + 'income' => 0.0, + 'expense' => 2.0, + ]]; + } + }; + + $result = $service->balanceStoreDetail('2026-10'); + $rows = array_values(array_filter( + $result['details'], + static fn (array $row): bool => $row['store_name'] === '重复店铺', + )); + + $this->assertCount(1, $rows); + $this->assertNull($rows[0]['opening_balance']); + $this->assertSame(5.0, $rows[0]['income']); + $this->assertSame(2.0, $rows[0]['expense']); + $this->assertNull($rows[0]['amount']); + } + public function test_monthly_closing_returns_july_to_october_for_all_platforms(): void { $monthOrdinal = ['2026-07' => 1, '2026-08' => 2, '2026-09' => 3, '2026-10' => 4];