allowPage(); $filters = [ 'store_name' => '淘系微信-测试店', 'bill_item' => '交易收入', 'start' => '2026-08-12', 'end' => '2026-09-10', 'keyword' => '260909', ]; $repository = Mockery::mock(TaobaoWechatSettlementBillRepository::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' => [ 'taobao_order_no' => '260909-047741735284017', 'entry_type' => '收入', ], ]], 'summary' => ['income' => 91.64, 'expense' => 0.0, 'balance' => 91.64, 'count' => 41], ]); $this->app->instance(TaobaoWechatSettlementBillRepository::class, $repository); $alipayRepository = Mockery::mock(TaobaoAlipaySettlementBillRepository::class); $alipayRepository->shouldReceive('summary')->once()->with($filters)->andReturn([ 'income' => 320.0, 'expense' => 25.0, 'opening_balance' => 230.0, 'opening_balance_date' => '2026-08-31', 'opening_balance_source' => 'previous_period', 'balance' => 525.0, 'count' => 12, ]); $this->app->instance(TaobaoAlipaySettlementBillRepository::class, $alipayRepository); $this->withExceptionHandling() ->loginAs() ->getJson(route('ecommerce-finance.account-bills.index', [ 'bill_type' => 'taobao-wechat', ...$filters, 'page' => 2, 'limit' => 20, ])) ->assertOk() ->assertJsonPath('data.0.payload.entry_type', '收入') ->assertJsonPath('columns.title', '淘系微信账户') ->assertJsonPath('summary.count', 41) ->assertJsonPath('summary.opening_balance', 230) ->assertJsonPath('summary.opening_balance_date', '2026-08-31') ->assertJsonPath('summary.opening_balance_source', 'previous_period') ->assertJsonPath('summary.balance', 525) ->assertJsonPath('summary.balance_source', 'taobao_alipay'); } public function test_filter_options_and_selected_column_export_use_the_same_mirror(): void { $this->allowPage(); $repository = Mockery::mock(TaobaoWechatSettlementBillRepository::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' => ['entry_type' => '收入'], ]]); $this->app->instance(TaobaoWechatSettlementBillRepository::class, $repository); $this->loginAs() ->withHeader('X-Requested-With', 'XMLHttpRequest') ->get(route('ecommerce-finance.account-bills.filter-options', ['bill_type' => 'taobao-wechat'])) ->assertOk() ->assertExactJson([ 'shops' => ['淘系微信-A店', '淘系微信-B店'], 'bill_items' => ['交易收入', '技术服务费'], 'ledger_remarks' => [], ]); $response = $this->loginAs()->get(route('ecommerce-finance.account-bills.export', [ 'bill_type' => 'taobao-wechat', 'store_name' => '淘系微信-A店', 'sort' => 'record_code', 'dir' => 'asc', 'columns' => 'payload.entry_type,record_code,store_name', ])); $response->assertOk() ->assertHeader('content-type', 'text/csv; charset=UTF-8') ->assertDownload('taobao-wechat_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(TaobaoWechatSettlementBillRepository::class); $repository->shouldReceive('paginateAccountBills')->once()->andThrow( new RuntimeException('taobao_wechat_authoritative_bills_not_ready') ); $this->app->instance(TaobaoWechatSettlementBillRepository::class, $repository); $this->loginAs() ->withHeader('X-Requested-With', 'XMLHttpRequest') ->get(route('ecommerce-finance.account-bills.index', ['bill_type' => 'taobao-wechat'])) ->assertStatus(503) ->assertJsonPath('message', '淘系微信完整账单尚未完成首次导入,当前不会回退到不完整的聚水潭辅助表。'); } public function test_invalid_date_range_is_rejected_before_reading_the_mirror(): void { $this->allowPage(); $repository = Mockery::mock(TaobaoWechatSettlementBillRepository::class); $repository->shouldNotReceive('paginateAccountBills'); $this->app->instance(TaobaoWechatSettlementBillRepository::class, $repository); $this->withExceptionHandling() ->loginAs() ->getJson(route('ecommerce-finance.account-bills.index', [ 'bill_type' => 'taobao-wechat', 'start' => '2026-09-10', 'end' => '2026-08-12', ])) ->assertStatus(422); } private function allowPage(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); } }