diff --git a/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php b/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php index db1613a5..e1686a83 100644 --- a/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageMirrorRepository.php @@ -60,6 +60,9 @@ class ThirdPartyWarehouseStorageMirrorRepository public const START_DATE = '2026-08-01'; + /** Historical monthly report range can start one month before the flow page. */ + private const INVENTORY_REPORT_START_DATE = '2026-07-01'; + /** @var array */ private const MOVEMENT_COLUMNS = [ 'processing_in' => '加工进仓', @@ -299,10 +302,7 @@ class ThirdPartyWarehouseStorageMirrorRepository } $page = max($page, 1); $perPage = min(max($perPage, 1), 5000); - $period = $this->period([ - 'dimension' => 'month', - 'period' => $filters['month'] ?? '', - ]); + $period = $this->inventoryReportPeriod((string) ($filters['month'] ?? '')); $authoritative = $this->monthlyInventoryAuthoritativeQuery($period, $filters); if ($authoritative !== null) { $total = $includeTotal ? (clone $authoritative)->count() : 0; @@ -1316,6 +1316,31 @@ class ThirdPartyWarehouseStorageMirrorRepository ]; } + /** @return array{dimension:string,value:string,start:string,end:string,days:int} */ + private function inventoryReportPeriod(string $month): array + { + try { + $start = preg_match('/^\d{4}-\d{2}$/', trim($month)) === 1 + ? Carbon::createFromFormat('!Y-m', trim($month))->startOfMonth() + : now()->startOfMonth(); + } catch (Throwable) { + $start = now()->startOfMonth(); + } + + $minimum = Carbon::parse(self::INVENTORY_REPORT_START_DATE)->startOfMonth(); + if ($start->lt($minimum)) { + $start = $minimum; + } + + return [ + 'dimension' => 'month', + 'value' => $start->format('Y-m'), + 'start' => $start->format('Y-m-d 00:00:00'), + 'end' => $start->copy()->addMonth()->format('Y-m-d 00:00:00'), + 'days' => $start->daysInMonth, + ]; + } + /** @param array $state */ private function stateAnchor(array $state): string { diff --git a/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageUpstreamRepository.php b/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageUpstreamRepository.php index 6b623dd7..d80c3e97 100644 --- a/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageUpstreamRepository.php +++ b/modules/EcommerceFinance/Repositories/ThirdPartyWarehouseStorageUpstreamRepository.php @@ -12,7 +12,7 @@ class ThirdPartyWarehouseStorageUpstreamRepository /** 三方仓页面仍固定读取该仓;共享流水镜像覆盖全部仓库。 */ public const WAREHOUSE_ID = 15045364; - public const START_DATE = '2026-08-01'; + public const START_DATE = '2026-07-01'; private ?UpstreamReadOnlyQuery $query = null; diff --git a/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php b/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php index 4c8b2c1a..b71ffe18 100644 --- a/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php +++ b/tests/Unit/EcommerceFinance/ThirdPartyWarehouseStorageMirrorRepositoryTest.php @@ -400,22 +400,30 @@ class ThirdPartyWarehouseStorageMirrorRepositoryTest extends TestCase $this->assertFalse($this->repository->allWarehousesReady()); } - public function test_periods_before_the_supported_start_date_are_clamped(): void + public function test_monthly_inventory_report_supports_july_without_changing_flow_page_cutoff(): void { $this->markReady(); $this->insertProduct('SKU-CUTOFF'); - $this->insertInventory('SKU-CUTOFF', 2, '2026-08-02 00:00:00'); + $this->insertInventory('SKU-CUTOFF', 10, '2026-08-01 00:00:00'); + $this->insertMovement('SKU-CUTOFF', '采购进仓', 2, 1, '2026-07-15 12:00:00'); $this->insertMovement('SKU-CUTOFF', '采购进仓', 2, 1, '2026-08-01 12:00:00'); $day = $this->repository->paginate($this->filters([ 'dimension' => 'day', 'period' => '2026-07-01', ]), 1, 25)['data'][0]; - $month = $this->repository->paginate($this->filters(['period' => '2026-07']), 1, 100)['data']; + $inventoryReport = $this->repository->paginateMonthlyInventoryReport([ + 'month' => '2026-07', + 'product_code' => 'SKU-CUTOFF', + 'warehouse_provider' => '', + 'category' => '', + ], 1, 25); $this->assertSame('2026-08-01', $day['date']); - $this->assertSame('2026-08-01', $month[0]['date']); - $this->assertSame('2026-08-31', $month[30]['date']); + $this->assertSame(1, $inventoryReport['total']); + $this->assertSame('2026-07-01', $inventoryReport['data'][0]->month); + $this->assertSame(8.0, (float) $inventoryReport['data'][0]->opening_quantity); + $this->assertSame(10.0, (float) $inventoryReport['data'][0]->closing_quantity); } public function test_product_catalog_must_finish_initial_sync_before_product_fields_are_exposed(): void