user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingPayableWorkflowMirrorRepository::class);
$repository->shouldReceive('paginate')->once()->andReturn([
'data' => [[
'process_instance_id' => 'PROC-TEST-001',
'workflow' => [
'businessId' => '202607031324000376786',
'title' => '应付申请流程',
'status' => 'COMPLETED',
'result' => 'agree',
'originatorUserId' => 'user-1',
'originatorUserName' => '扬灵',
'originatorDeptName' => '财务部',
'createAt' => '2026-08-28 10:00:00',
'finishTime' => '2026-08-28 11:00:00',
],
'detail' => ['类型' => '快递运费', '用途' => '拉到江西仓的车费', '付款金额' => '1200'],
'fields' => [
'应付明细' => [],
'付款明细' => [['付款金额' => '1200']],
'附件' => '',
'未签字版合同附件或其他明细附件' => [[
'spaceId' => '369334649',
'fileName' => '测试对账附件.xlsx',
'fileId' => '123456',
'fileType' => 'xlsx',
]],
],
'tasks' => [['status' => 'COMPLETED']],
]],
'total' => 1,
'status' => 'succeeded',
'last_synced_at' => '2026-08-28 11:05:00',
]);
$this->app->instance(DingPayableWorkflowMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'payable-cloud-warehouse-vehicle-fee',
]))->assertOk()
->assertSee('应付云仓车费')
->assertSee('钉钉审批编号')
->assertSee('202607031324000376786')
->assertSee('扬灵')
->assertSee('已完成')
->assertSee('拉到江西仓的车费')
->assertSee('用途同时包含“仓”“车费”')
->assertDontSeeText('全部表单字段')
->assertDontSeeText('付款明细')
->assertDontSeeText('审批任务')
->assertSee('测试对账附件.xlsx')
->assertSee('process_instance_id=PROC-TEST-001', false)
->assertSee('file_id=123456', false)
->assertSee('ding-payable-attachment"', false)
->assertSee('data-ding-payable-row', false)
->assertSee('is-clickable', false)
->assertSee('is-active', false)
->assertSee('镜像状态:succeeded');
}
public function test_cloud_warehouse_vehicle_fee_page_uses_ding_talk_rejection_result(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingPayableWorkflowMirrorRepository::class);
$repository->shouldReceive('paginate')->once()->andReturn([
'data' => [[
'process_instance_id' => 'PROC-TEST-REJECTED',
'workflow' => [
'businessId' => '202608291446000258827',
'title' => '应付申请流程',
'status' => 'COMPLETED',
'result' => 'refuse',
'originatorUserId' => 'user-1',
'originatorUserName' => '扬灵',
],
'detail' => ['类型' => '快递运费', '用途' => '拉到江西仓的车费'],
'fields' => [],
'tasks' => [],
]],
'total' => 1,
'status' => 'succeeded',
'last_synced_at' => '2026-08-31 13:42:04',
]);
$this->app->instance(DingPayableWorkflowMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'payable-cloud-warehouse-vehicle-fee',
]))->assertOk()
->assertSee('
已拒绝 | ', false)
->assertDontSee('已完成 | ', false)
->assertSee('已拒绝');
}
public function test_cloud_warehouse_vehicle_fee_page_keeps_an_unavailable_mirror_readable(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingPayableWorkflowMirrorRepository::class);
$repository->shouldReceive('paginate')->andThrow(new \RuntimeException('not ready'));
$this->app->instance(DingPayableWorkflowMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'payable-cloud-warehouse-vehicle-fee',
]))->assertOk()
->assertSee('钉钉应付流程镜像尚未完成同步,请稍后重试。')
->assertSee('镜像读取失败,请稍后重试。');
}
public function test_cloud_warehouse_vehicle_fee_page_passes_workflow_time_and_status_filters(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingPayableWorkflowMirrorRepository::class);
$repository->shouldReceive('paginate')->once()->with(
[
'process_instance_id' => '',
'created_from' => '2026-07-01',
'finished_to' => '2026-07-31',
'status' => 'COMPLETED',
],
1,
Mockery::type('int'),
)->andReturn([
'data' => [],
'total' => 0,
'status' => 'succeeded',
'last_synced_at' => '2026-08-29 10:11:24',
]);
$this->app->instance(DingPayableWorkflowMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'payable-cloud-warehouse-vehicle-fee',
'created_from' => '2026-07-01',
'finished_to' => '2026-07-31',
'status' => 'completed',
]))->assertOk()
->assertSee('流程时间(创建时间至完成时间)')
->assertSee('2026-07-01')
->assertSee('2026-07-31')
->assertSee('状态')
->assertSee('全部状态');
}
public function test_attachment_download_streams_dingtalk_file(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
config([
'dingtalk.client_id' => 'test-client-id',
'dingtalk.client_secret' => 'test-client-secret',
]);
$repository = Mockery::mock(DingPayableWorkflowMirrorRepository::class);
$repository->shouldReceive('attachment')
->once()
->with('PROC-TEST-001', '123456')
->andReturn(['fileName' => '测试对账附件.xlsx', 'fileType' => 'xlsx', 'fileSize' => 9]);
$this->app->instance(DingPayableWorkflowMirrorRepository::class, $repository);
Http::fake([
'oapi.dingtalk.com/*' => Http::response(['access_token' => 'test-token']),
'api.dingtalk.com/*' => Http::response([
'success' => true,
'result' => ['downloadUri' => 'https://fake-oss.example.com/file'],
]),
'fake-oss.example.com/*' => Http::response('file-bytes', 200, ['Content-Type' => 'application/octet-stream']),
]);
$response = $this->loginAs()->get(route('business.courier-payables.ding-payable-attachment.download', [
'process_instance_id' => 'PROC-TEST-001',
'file_id' => '123456',
]));
$response->assertOk()
->assertHeader('content-type', 'application/octet-stream');
$this->assertSame('file-bytes', $response->streamedContent());
$this->assertStringContainsString(
rawurlencode('测试对账附件.xlsx'),
(string) $response->headers->get('content-disposition'),
);
}
public function test_attachment_download_rejects_unknown_file(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingPayableWorkflowMirrorRepository::class);
$repository->shouldReceive('attachment')->once()->andReturn(null);
$this->app->instance(DingPayableWorkflowMirrorRepository::class, $repository);
Http::fake();
$this->loginAs()
->withExceptionHandling()
->withHeaders(['X-Requested-With' => 'XMLHttpRequest'])
->get(route('business.courier-payables.ding-payable-attachment.download', [
'process_instance_id' => 'PROC-UNKNOWN',
'file_id' => '999',
]))
->assertNotFound();
Http::assertNothingSent();
}
}