From a9ad59ae949ac620e9d6e72b427bd9a61dce5217 Mon Sep 17 00:00:00 2001 From: chenxi Date: Mon, 21 Sep 2026 00:07:42 +0800 Subject: [PATCH] fix: match inventory report product codes to catalog --- ...dPartyWarehouseStorageMirrorRepository.php | 49 ++++++++++--------- ...tyWarehouseStorageMirrorRepositoryTest.php | 7 +-- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php b/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php index d17ec729..7459c33e 100644 --- a/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php @@ -317,11 +317,19 @@ class ThirdPartyWarehouseStorageMirrorRepository ->where('generation', $inventoryGeneration) ->where('qty', '!=', 0) ->whereNotNull('sku_code')->where('sku_code', '!=', '') - ->select(['wms_co_id', 'wh_id', 'sku_code', 'captured_at']) + ->select(['wms_co_id', 'wh_id', 'sku_code as inventory_sku_code', 'captured_at']) ->selectRaw('qty AS snapshot_qty'); $candidates = $this->connection->query()->fromSub($inventoryDimensions, 'pairs') - ->select(['pairs.wms_co_id', 'pairs.sku_code', 'pairs.wh_id']); - $this->containsFilter($candidates, 'pairs.sku_code', $filters['product_code'] ?? ''); + ->joinSub($latestProducts, 'latest_product', 'latest_product.sku_id', '=', 'pairs.inventory_sku_code') + ->join(self::PRODUCT_TABLE . ' as s', function ($join): void { + $join->on('s.sku_id', '=', 'latest_product.sku_id') + ->on('s.autoid', '=', 'latest_product.autoid'); + }) + ->select([ + 'pairs.wms_co_id', 'pairs.wh_id', 'pairs.inventory_sku_code', + 's.sku_id as sku_code', 's.category', + ]); + $this->containsFilter($candidates, 's.sku_id', $filters['product_code'] ?? ''); $warehouseFilter = trim((string) ($filters['warehouse_provider'] ?? '')); if ($warehouseFilter !== '') { $warehouseIds = $this->connection->query()->fromSub( @@ -336,17 +344,12 @@ class ThirdPartyWarehouseStorageMirrorRepository } $categoryFilter = trim((string) ($filters['category'] ?? '')); if ($categoryFilter !== '') { - $categorySkus = $this->connection->table(self::PRODUCT_TABLE) - ->where('sku_type', 'normal') - ->whereNotNull('sku_id')->where('sku_id', '!=', '') - ->select('sku_id')->distinct(); - $this->containsFilter($categorySkus, 'category', $categoryFilter); - $candidates->whereIn('pairs.sku_code', $categorySkus); + $this->containsFilter($candidates, 's.category', $categoryFilter); } $total = $includeTotal ? (clone $candidates)->count() : 0; $pagePairs = (clone $candidates) - ->orderBy('pairs.wms_co_id')->orderBy('pairs.wh_id')->orderBy('pairs.sku_code') + ->orderBy('pairs.wms_co_id')->orderBy('pairs.wh_id')->orderBy('s.sku_id') ->forPage($page, $perPage); $pairRows = (clone $pagePairs)->get(); if ($pairRows->isEmpty()) { @@ -359,24 +362,25 @@ class ThirdPartyWarehouseStorageMirrorRepository } $dimensionedPagePairs = $this->connection->query()->fromSub($pagePairs, 'pairs') - ->leftJoinSub($latestProducts, 'latest_product', 'latest_product.sku_id', '=', 'pairs.sku_code') - ->leftJoin(self::PRODUCT_TABLE . ' as s', function ($join): void { - $join->on('s.sku_id', '=', 'latest_product.sku_id') - ->on('s.autoid', '=', 'latest_product.autoid'); - }) ->leftJoinSub($latestWarehouses, 'latest_warehouse', 'latest_warehouse.wms_co_id', '=', 'pairs.wms_co_id') ->leftJoin(self::WAREHOUSE_TABLE . ' as w', function ($join): void { $join->on('w.wms_co_id', '=', 'latest_warehouse.wms_co_id') ->on('w.Id', '=', 'latest_warehouse.Id'); }) - ->select(['pairs.wms_co_id', 'pairs.wh_id', 'pairs.sku_code', 's.category', 'w.name as warehouse_name']); + ->select([ + 'pairs.wms_co_id', 'pairs.wh_id', 'pairs.inventory_sku_code', + 'pairs.sku_code', 'pairs.category', 'w.name as warehouse_name', + ]); $anchors = $this->connection->query()->fromSub($dimensionedPagePairs, 'pairs') ->leftJoinSub(clone $inventoryDimensions, 'inventory', function ($join): void { $join->on('inventory.wms_co_id', '=', 'pairs.wms_co_id') - ->on('inventory.sku_code', '=', 'pairs.sku_code') + ->on('inventory.inventory_sku_code', '=', 'pairs.inventory_sku_code') ->on('inventory.wh_id', '=', 'pairs.wh_id'); }) - ->select(['pairs.wms_co_id', 'pairs.wh_id', 'pairs.sku_code', 'pairs.category', 'pairs.warehouse_name']) + ->select([ + 'pairs.wms_co_id', 'pairs.wh_id', 'pairs.inventory_sku_code', + 'pairs.sku_code', 'pairs.category', 'pairs.warehouse_name', + ]) ->selectRaw('COALESCE(inventory.snapshot_qty, 0) AS snapshot_qty') ->selectRaw('COALESCE(inventory.captured_at, ?) AS snapshot_at', [$anchorAt]); @@ -384,9 +388,9 @@ class ThirdPartyWarehouseStorageMirrorRepository ->where('h.occurred_at', '>=', $rangeStart) ->where('h.occurred_at', '<', $rangeEnd) ->whereIn('h.wms_co_id', $pairRows->pluck('wms_co_id')->unique()->values()->all()) - ->whereIn('i.sku_code', $pairRows->pluck('sku_code')->unique()->values()->all()) + ->whereIn('i.sku_code', $pairRows->pluck('inventory_sku_code')->unique()->values()->all()) ->select([ - 'h.wms_co_id', 'h.io_type', 'h.occurred_at', 'i.sku_code', + 'h.wms_co_id', 'h.io_type', 'h.occurred_at', 'i.sku_code as inventory_sku_code', 'i.qty as movement_qty', 'i.qty_type as movement_qty_type', ]) ->selectRaw('COALESCE(NULLIF(h.wh_id, 0), 1) AS wh_id'); @@ -404,10 +408,11 @@ class ThirdPartyWarehouseStorageMirrorRepository ->leftJoinSub($movements, 'mv', function ($join): void { $join->on('mv.wms_co_id', '=', 'a.wms_co_id') ->on('mv.wh_id', '=', 'a.wh_id') - ->on('mv.sku_code', '=', 'a.sku_code'); + ->on('mv.inventory_sku_code', '=', 'a.inventory_sku_code'); }) ->groupBy([ - 'a.wms_co_id', 'a.wh_id', 'a.sku_code', 'a.category', 'a.warehouse_name', + 'a.wms_co_id', 'a.wh_id', 'a.inventory_sku_code', 'a.sku_code', + 'a.category', 'a.warehouse_name', 'a.snapshot_qty', 'a.snapshot_at', ]) ->select(['a.wms_co_id', 'a.wh_id', 'a.sku_code', 'a.category', 'a.warehouse_name']) diff --git a/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php b/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php index 760faee5..287f439a 100644 --- a/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php +++ b/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php @@ -280,11 +280,12 @@ class ThirdPartyWarehouseStorageMirrorRepositoryTest extends TestCase $this->assertSame(-20.0, (float) $result['data'][0]->sales_out); } - public function test_inventory_report_excludes_zero_only_dirty_product_codes(): void + public function test_inventory_report_uses_only_product_codes_matched_to_the_operations_replica_catalog(): void { $this->markReady(); + $this->insertProduct('SKU-STOCK'); $this->insertInventory('SKU-STOCK', 2, '2026-09-01 00:00:00'); - $this->insertInventory('【商品描述不是商品编码】', 0, '2026-09-01 00:00:00'); + $this->insertInventory('LEGACY-CODE-NOT-IN-CATALOG', 7, '2026-09-01 00:00:00'); $result = $this->repository->paginateMonthlyInventoryReport([ 'month' => '2026-08', @@ -296,7 +297,7 @@ class ThirdPartyWarehouseStorageMirrorRepositoryTest extends TestCase $this->assertSame(1, $result['total']); $this->assertSame(['SKU-STOCK'], $codes); - $this->assertNotContains('【商品描述不是商品编码】', $codes); + $this->assertNotContains('LEGACY-CODE-NOT-IN-CATALOG', $codes); } public function test_third_party_page_remains_ready_when_only_the_all_warehouse_dimension_is_missing(): void -- 2.47.2