allowPage(); $filters = [ 'store_name' => '拼多多-测试店', 'bill_item' => '交易收入', 'start' => '2026-08-12', 'end' => '2026-09-10', 'keyword' => '260909', ]; $repository = Mockery::mock(PinduoduoSettlementBillRepository::class); $repository->shouldReceive('paginateAccountBills')->once()->with( $filters, 'occurred_at', 'desc', 2, 20, )->andReturn([ 'current_page' => 2, 'last_page' => 3, 'per_page' => 20, 'total' => 41, 'data' => [[ 'id' => '14424200', 'record_code' => '380425163199', 'store_name' => '拼多多-测试店', 'income' => 91.64, 'expense' => 0.0, 'payload' => [ 'merchant_order_no' => '260909-047741735284017', 'finance_type' => '交易收入', 'type' => '拼多多货款账户', ], ]], 'summary' => ['income' => 91.64, 'expense' => 0.0, 'balance' => 91.64, 'count' => 41], ]); $this->app->instance(PinduoduoSettlementBillRepository::class, $repository); $this->withExceptionHandling() ->loginAs() ->getJson(route('ecommerce-finance.account-bills.index', [ 'bill_type' => 'pinduoduo', ...$filters, 'page' => 2, 'limit' => 20, ])) ->assertOk() ->assertJsonPath('data.0.payload.type', '拼多多货款账户') ->assertJsonPath('columns.title', '拼多多账户') ->assertJsonPath('summary.count', 41); } public function test_filter_options_and_selected_column_export_use_the_same_mirror(): void { $this->allowPage(); $repository = Mockery::mock(PinduoduoSettlementBillRepository::class); $repository->shouldReceive('accountBillFilterOptions')->once()->andReturn([ 'shops' => ['拼多多-A店', '拼多多-B店'], 'bill_items' => ['交易收入', '技术服务费'], ]); $repository->shouldReceive('exportAccountBills')->once()->with( ['store_name' => '拼多多-A店'], 'record_code', 'asc', )->andReturn([[ 'record_code' => '380425163199', 'store_name' => '拼多多-A店', 'payload' => ['finance_type' => '交易收入'], ]]); $this->app->instance(PinduoduoSettlementBillRepository::class, $repository); $this->loginAs() ->withHeader('X-Requested-With', 'XMLHttpRequest') ->get(route('ecommerce-finance.account-bills.filter-options', ['bill_type' => 'pinduoduo'])) ->assertOk() ->assertExactJson([ 'shops' => ['拼多多-A店', '拼多多-B店'], 'bill_items' => ['交易收入', '技术服务费'], 'ledger_remarks' => [], ]); $response = $this->loginAs()->get(route('ecommerce-finance.account-bills.export', [ 'bill_type' => 'pinduoduo', 'store_name' => '拼多多-A店', 'sort' => 'record_code', 'dir' => 'asc', 'columns' => 'payload.finance_type,record_code,store_name', ])); $response->assertOk() ->assertHeader('content-type', 'text/csv; charset=UTF-8') ->assertDownload('pinduoduo_account_bills.csv'); $csv = $response->streamedContent(); $this->assertStringContainsString('财务类型,记录编码,店铺', $csv); $this->assertStringContainsString('交易收入,"=""380425163199""",拼多多-A店', $csv); } public function test_unready_mirror_returns_a_clear_retryable_response(): void { $this->allowPage(); $repository = Mockery::mock(PinduoduoSettlementBillRepository::class); $repository->shouldReceive('paginateAccountBills')->once()->andThrow( new RuntimeException('pinduoduo_authoritative_bills_not_ready') ); $this->app->instance(PinduoduoSettlementBillRepository::class, $repository); $this->loginAs() ->withHeader('X-Requested-With', 'XMLHttpRequest') ->get(route('ecommerce-finance.account-bills.index', ['bill_type' => 'pinduoduo'])) ->assertStatus(503) ->assertJsonPath('message', '拼多多完整货款账单尚未完成首次导入,当前不会回退到不完整的聚水潭辅助表。'); } public function test_invalid_date_range_is_rejected_before_reading_the_mirror(): void { $this->allowPage(); $repository = Mockery::mock(PinduoduoSettlementBillRepository::class); $repository->shouldNotReceive('paginateAccountBills'); $this->app->instance(PinduoduoSettlementBillRepository::class, $repository); $this->withExceptionHandling() ->loginAs() ->getJson(route('ecommerce-finance.account-bills.index', [ 'bill_type' => 'pinduoduo', 'start' => '2026-09-10', 'end' => '2026-08-12', ])) ->assertStatus(422); } private function allowPage(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); } }