user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $repository = Mockery::mock(DingProcessDetailMirrorRepository::class); $repository->shouldReceive('paginate')->once() ->with(Mockery::type('array'), 1, 100) ->andReturn([ 'data' => [ (object) [ 'business_id' => '202601011200000000001', 'line_no' => 1, 'originator_name' => '张三', 'status' => '已完成', 'payment_state' => '已付款', 'cashier_status' => 'completed', 'current_task_name' => '财务用途审批', 'current_task_started_at' => now()->subMinutes(90)->startOfMinute()->format('Y-m-d H:i:s'), 'cashier_task_name' => '出纳打款', 'cashier_task_started_at' => now()->subHours(3)->startOfMinute()->format('Y-m-d H:i:s'), 'payment_fact_amount' => 0, 'payment_difference' => 0.20, 'document_type' => '公摊日常报销', 'purpose' => '办公用品采购', 'account_code' => '5602.04', 'account_name' => '管理费用_办公费', 'amount' => 100.00, 'bank_account_code' => '1002.01.001', 'bank_account_name' => '华夏银行-测试', 'bank_amount' => 100.00, 'payment_method' => '银行卡', 'paid_at' => '2026-01-02 00:00', ], ], 'total' => 1, 'summary' => [ 'rows' => 1, 'approvals' => 1, 'amount_total' => 100.0, 'bank_amount_total' => 100.0, ], 'status' => 'succeeded', 'last_synced_at' => '2026-10-26 15:00:00', 'sync_stale' => true, 'sync_stale_after_minutes' => 15, 'scope' => 'personal_permission', ]); $repository->shouldReceive('filterOptions')->once()->andReturn([ 'document_types' => ['公摊日常报销'], 'statuses' => ['已完成'], 'payment_states' => [ '待付款', '已付款', '付款待核实', '不涉及付款', ], ]); $this->app->instance(DingProcessDetailMirrorRepository::class, $repository); $processSection = collect(\App\Utilities\BusinessNavigation::all()) ->firstWhere('slug', 'process-data'); $this->assertContains( 'oa-process', array_column($processSection['pages'] ?? [], 'slug'), ); $this->loginAs()->get(route('business.pages.show', [ 'section' => 'process-data', 'page' => 'oa-process', 'per_page' => 100, 'payment_state' => '付款待核实', ]))->assertOk() ->assertSee('流程数据') ->assertSee('OA流程') ->assertSee('审批编号') ->assertSee('发起人') ->assertSee('单据类型') ->assertSee('打款时间') ->assertSee('name="paid_start"', false) ->assertSee('name="paid_end"', false) ->assertSee('placeholder="开始日期"', false) ->assertSee('placeholder="结算日期"', false) ->assertSee('aria-label="选择打款开始日期"', false) ->assertSee('aria-label="选择打款结算日期"', false) ->assertSee('CaiWuDatePicker.setup', false) ->assertSee('name="payment_state"', false) ->assertSee('审批状态') ->assertSee('付款状态') ->assertSee('当前节点') ->assertSee('节点进入时间') ->assertSee('已等待') ->assertSee('出纳节点') ->assertSee('财务用途审批') ->assertSee('1小时30分钟') ->assertSee('出纳打款 · 已完成') ->assertSee('待核实付款明细合计') ->assertSee('已付款') ->assertSee('付款待核实') ->assertSee('付款明细金额') ->assertSee('付款差额') ->assertSee('0.20') ->assertDontSee('付款合计', false) ->assertDontSee('银行付款合计') ->assertSee('流程数据已超过 15 分钟未成功同步') ->assertSee('202601011200000000001') ->assertSee('张三') ->assertSee('管理费用_办公费') ->assertSee('100.00') ->assertSee('当前账号权限范围') ->assertSee('同一笔付款不会因关联多条费用而重复计算') ->assertSee('name="per_page"', false) ->assertSee('value="50"', false) ->assertSee('value="100" selected', false) ->assertSee('value="200"', false) ->assertSee('导出'); } public function test_page_stays_readable_when_mirror_is_not_ready(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $repository = Mockery::mock(DingProcessDetailMirrorRepository::class); $repository->shouldReceive('filterOptions')->once()->andThrow(new RuntimeException('not ready')); $repository->shouldReceive('scope')->once()->andReturn('personal_permission'); $this->app->instance(DingProcessDetailMirrorRepository::class, $repository); $this->loginAs()->get(route('business.pages.show', [ 'section' => 'process-data', 'page' => 'oa-process', ]))->assertOk() ->assertSee('OA流程镜像尚未完成导入') ->assertSee('当前账号权限范围'); } public function test_legacy_process_details_url_redirects_to_oa_process(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $this->loginAs()->get( '/1/workspace/financial-operations/process-details?status=' . urlencode('已完成'), )->assertRedirect(route('business.pages.show', [ 'section' => 'process-data', 'page' => 'oa-process', 'status' => '已完成', ])); } public function test_legacy_process_details_export_url_redirects_to_oa_process(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $this->loginAs()->get( '/1/workspace/financial-operations/process-details/export?status=' . urlencode('已完成'), )->assertRedirect(route('business.process-details.export', [ 'status' => '已完成', ])); } public function test_export_streams_filtered_csv(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $repository = Mockery::mock(DingProcessDetailMirrorRepository::class); $repository->shouldReceive('ready')->once()->andReturn(true); $repository->shouldReceive('exportRows')->once()->andReturnUsing(function (): \Generator { yield [ 'business_id' => '202601011200000000001', 'originator_name' => '张三', 'status' => '已完成', 'payment_state' => '已付款', 'payment_difference' => 0.20, 'document_type' => '公摊日常报销', 'purpose' => '办公用品采购', 'line_no' => 1, 'account_code' => '5602.04', 'account_name' => '管理费用_办公费', 'amount' => '100.00', 'bank_account_code' => '1002.01.001', 'bank_account_name' => '华夏银行-测试', 'bank_amount' => '100.00', 'payment_method' => '银行卡', 'paid_at' => '2026-01-02 00:00', ]; }); $this->app->instance(DingProcessDetailMirrorRepository::class, $repository); $response = $this->loginAs()->get(route('business.process-details.export', [ 'status' => '已完成', 'payment_state' => '已付款', ])); $response->assertOk(); $content = $response->streamedContent(); $this->assertStringContainsString('审批编号', $content); $this->assertStringContainsString('202601011200000000001', $content); $this->assertStringContainsString('张三', $content); $this->assertStringContainsString('管理费用_办公费', $content); $this->assertStringContainsString('付款状态', $content); $this->assertStringContainsString('已付款', $content); $this->assertStringContainsString('付款差额', $content); $this->assertStringContainsString('0.20', $content); } }