parent
48b433be4d
commit
965b70df7e
@ -142,7 +142,9 @@ class ProcessDetails extends Controller
|
||||
/** @return array<string,string> */
|
||||
private function filters(Request $request): array
|
||||
{
|
||||
$status = trim((string) $request->query('status', ''));
|
||||
$status = DingProcessDetailMirrorRepository::normalizeStatusFilter(
|
||||
$request->query('status', ''),
|
||||
);
|
||||
[$paidStart, $paidEnd] = ProcessDetailsPageData::normalizePaidDateRange(
|
||||
$request->query('paid_start'),
|
||||
$request->query('paid_end'),
|
||||
|
||||
@ -34,6 +34,11 @@ class DingProcessDetailMirrorRepository
|
||||
'流程终止' => '不涉及付款',
|
||||
];
|
||||
|
||||
public const LEGACY_STATUS_MAP = [
|
||||
'已终止' => '已撤销',
|
||||
'已撤回' => '已撤销',
|
||||
];
|
||||
|
||||
private readonly ConnectionInterface $connection;
|
||||
|
||||
private ?bool $paymentSourceStateColumn = null;
|
||||
@ -65,6 +70,13 @@ class DingProcessDetailMirrorRepository
|
||||
return in_array($paymentState, self::PAYMENT_STATES, true) ? $paymentState : '';
|
||||
}
|
||||
|
||||
public static function normalizeStatusFilter(mixed $value): string
|
||||
{
|
||||
$status = trim((string) $value);
|
||||
|
||||
return self::LEGACY_STATUS_MAP[$status] ?? $status;
|
||||
}
|
||||
|
||||
public function datasetKey(): string
|
||||
{
|
||||
return self::DATASET_PREFIX . $this->scope();
|
||||
@ -304,6 +316,9 @@ class DingProcessDetailMirrorRepository
|
||||
'status' => 'status',
|
||||
] as $filter => $column) {
|
||||
$value = trim((string) ($filters[$filter] ?? ''));
|
||||
if ($filter === 'status') {
|
||||
$value = self::normalizeStatusFilter($value);
|
||||
}
|
||||
if ($value !== '') {
|
||||
$query->where('ding_process_detail_mirror.' . $column, $value);
|
||||
}
|
||||
@ -344,7 +359,8 @@ class DingProcessDetailMirrorRepository
|
||||
return "CASE
|
||||
WHEN {$sourceState} = 'not_applicable'
|
||||
OR {$cashierState} = 'terminated'
|
||||
OR (({$cashierState} IS NULL OR {$cashierState} = '') AND {$workflowStatus} = '已终止')
|
||||
OR (({$cashierState} IS NULL OR {$cashierState} = '')
|
||||
AND {$workflowStatus} IN ('已撤销', '已终止'))
|
||||
THEN '不涉及付款'
|
||||
WHEN {$cashierState} = 'completed'
|
||||
OR (({$cashierState} IS NULL OR {$cashierState} = '')
|
||||
|
||||
@ -1661,7 +1661,7 @@ class DingProcessDetailMirrorSynchronizer
|
||||
'RUNNING' => '审批中',
|
||||
'COMPLETED' => '已完成',
|
||||
'REJECTED' => '已拒绝',
|
||||
'WITHDRAWN' => '已终止',
|
||||
'WITHDRAWN' => '已撤销',
|
||||
'FAILED' => '失败',
|
||||
'EXPIRED' => '已过期',
|
||||
'DELETED' => '已删除',
|
||||
|
||||
@ -117,7 +117,9 @@ class ProcessDetailsPageData
|
||||
/** @return array<string,string> */
|
||||
private function filters(Request $request): array
|
||||
{
|
||||
$status = trim((string) $request->query('status', ''));
|
||||
$status = DingProcessDetailMirrorRepository::normalizeStatusFilter(
|
||||
$request->query('status', ''),
|
||||
);
|
||||
[$paidStart, $paidEnd] = self::normalizePaidDateRange(
|
||||
$request->query('paid_start'),
|
||||
$request->query('paid_end'),
|
||||
|
||||
@ -29,6 +29,11 @@
|
||||
'不涉及付款' => 'is-neutral',
|
||||
default => 'is-neutral',
|
||||
};
|
||||
$paymentSummaryLabel = match ($filters['payment_state'] ?? '') {
|
||||
'已付款' => '已确认付款合计',
|
||||
'付款待核实' => '待核实付款明细合计',
|
||||
default => '付款明细合计',
|
||||
};
|
||||
$pageUrl ??= route('business.pages.show', [
|
||||
'section' => 'process-data',
|
||||
'page' => 'oa-process',
|
||||
@ -736,12 +741,13 @@
|
||||
<div class="item"><span>明细行数</span><b>{{ number_format((int) ($summary['rows'] ?? 0)) }}</b></div>
|
||||
<div class="item"><span>审批单数</span><b>{{ number_format((int) ($summary['approvals'] ?? 0)) }}</b></div>
|
||||
<div class="item"><span>金额合计</span><b>{{ $money($summary['amount_total'] ?? null) }}</b></div>
|
||||
<div class="item"><span>付款合计</span><b>{{ $money($summary['bank_amount_total'] ?? null) }}</b></div>
|
||||
<div class="item"><span>{{ $paymentSummaryLabel }}</span><b>{{ $money($summary['bank_amount_total'] ?? null) }}</b></div>
|
||||
</div>
|
||||
|
||||
<p class="process-details-note">
|
||||
金额合计按审批费用明细汇总,付款合计按结构化付款明细汇总,同一笔付款不会因关联多条费用而重复计算。
|
||||
付款状态按钉钉“出纳打款”节点判断;付款差额按“费用合计 − 已付款合计”单独计算,不参与付款状态判断。
|
||||
金额合计按审批费用明细汇总,付款明细合计按审批表单中的结构化付款明细汇总,不等同于出纳已确认付款;
|
||||
同一笔付款不会因关联多条费用而重复计算。
|
||||
付款状态按钉钉“出纳打款”节点判断;付款差额按“费用合计 − 付款明细合计”单独计算,不参与付款状态判断。
|
||||
@if (in_array('付款待核实', $paymentStates, true))
|
||||
“付款待核实”表示尚未取得可靠的出纳节点任务,不能据此推断已付款或待付款。
|
||||
@endif
|
||||
@ -767,7 +773,7 @@
|
||||
<th class="num">金额(元)</th>
|
||||
<th>银行科目编码</th>
|
||||
<th>银行科目名称</th>
|
||||
<th class="num">付款金额</th>
|
||||
<th class="num">付款明细金额</th>
|
||||
<th class="num">付款差额</th>
|
||||
<th>付款方式</th>
|
||||
<th>打款时间</th>
|
||||
|
||||
@ -75,6 +75,7 @@ class ProcessDetailsPageTest extends FeatureTestCase
|
||||
'section' => 'process-data',
|
||||
'page' => 'oa-process',
|
||||
'per_page' => 100,
|
||||
'payment_state' => '付款待核实',
|
||||
]))->assertOk()
|
||||
->assertSee('流程数据')
|
||||
->assertSee('OA流程')
|
||||
@ -92,12 +93,13 @@ class ProcessDetailsPageTest extends FeatureTestCase
|
||||
->assertSee('name="payment_state"', false)
|
||||
->assertSee('审批状态')
|
||||
->assertSee('付款状态')
|
||||
->assertSee('待核实付款明细合计')
|
||||
->assertSee('已付款')
|
||||
->assertSee('付款待核实')
|
||||
->assertSee('付款金额')
|
||||
->assertSee('付款明细金额')
|
||||
->assertSee('付款差额')
|
||||
->assertSee('0.20')
|
||||
->assertSee('付款合计')
|
||||
->assertDontSee('<span>付款合计</span>', false)
|
||||
->assertDontSee('银行付款合计')
|
||||
->assertSee('流程数据已超过 15 分钟未成功同步')
|
||||
->assertSee('202601011200000000001')
|
||||
|
||||
@ -75,6 +75,27 @@ class DingProcessDetailMirrorRepositoryTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function test_legacy_revoked_status_filter_is_normalized(): void
|
||||
{
|
||||
DB::connection('business_mirror')
|
||||
->table('ding_process_detail_mirror')
|
||||
->where('business_id', '202602031200000000002')
|
||||
->update(['status' => '已撤销']);
|
||||
|
||||
$this->assertSame(
|
||||
2,
|
||||
$this->repository->paginate(['status' => '已终止'], 1, 50)['total'],
|
||||
);
|
||||
$this->assertSame(
|
||||
2,
|
||||
$this->repository->paginate(['status' => '已撤回'], 1, 50)['total'],
|
||||
);
|
||||
$this->assertSame(
|
||||
2,
|
||||
$this->repository->paginate(['status' => '已撤销'], 1, 50)['total'],
|
||||
);
|
||||
}
|
||||
|
||||
public function test_paginate_applies_paid_date_range_status_and_keyword_filters(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
|
||||
@ -178,6 +178,36 @@ class DingProcessDetailMirrorSynchronizerTest extends TestCase
|
||||
$this->assertSame('不涉及付款', $row->payment_state);
|
||||
}
|
||||
|
||||
public function test_terminated_workflow_is_labelled_revoked(): void
|
||||
{
|
||||
$this->query->workflows = [[
|
||||
'processInstanceId' => 'process-cashier-terminated',
|
||||
'processCode' => 'PROC-ACF09A14-B094-46B1-A9E2-8BB1F4E1FDCF',
|
||||
'businessId' => '202610191640000251028',
|
||||
'title' => '雁兰提交的应付申请流程',
|
||||
'status' => 'TERMINATED',
|
||||
'result' => 'AGREE',
|
||||
'originatorUserId' => 'user-1',
|
||||
'joinAt' => '2026-10-19 16:40:00',
|
||||
]];
|
||||
$this->query->scheme = [
|
||||
'收款费用明细' => json_encode([[
|
||||
'rowValue' => [
|
||||
['label' => '代发订单金额', 'value' => '1.00'],
|
||||
],
|
||||
]], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
|
||||
'收款合计' => 1,
|
||||
'付款明细' => '[]',
|
||||
];
|
||||
|
||||
$this->synchronizer->sync(true);
|
||||
|
||||
$row = app(DingProcessDetailMirrorRepository::class)
|
||||
->paginate(['business_id' => '202610191640000251028'], 1, 50)['data'][0];
|
||||
$this->assertSame('已撤销', $row->status);
|
||||
$this->assertSame('不涉及付款', $row->payment_state);
|
||||
}
|
||||
|
||||
public function test_running_workflow_detects_cashier_returned_from_official_tasks(): void
|
||||
{
|
||||
$this->query->workflows = [[
|
||||
|
||||
@ -178,6 +178,47 @@ class ProcessDetailsPageDataTest extends TestCase
|
||||
$this->assertStringNotContainsString('payment_state', $data['processRows']->url(2));
|
||||
}
|
||||
|
||||
public function test_legacy_terminated_status_is_normalized_to_revoked(): void
|
||||
{
|
||||
$repository = $this->createMock(DingProcessDetailMirrorRepository::class);
|
||||
$repository->expects($this->once())
|
||||
->method('filterOptions')
|
||||
->with($this->callback(static fn (array $filters): bool =>
|
||||
$filters['status'] === '已撤销'
|
||||
))
|
||||
->willReturn([
|
||||
'document_types' => [],
|
||||
'statuses' => ['已撤销'],
|
||||
'payment_states' => [],
|
||||
]);
|
||||
$repository->expects($this->once())
|
||||
->method('paginate')
|
||||
->with($this->callback(static fn (array $filters): bool =>
|
||||
$filters['status'] === '已撤销'
|
||||
), 1, 50)
|
||||
->willReturn([
|
||||
'data' => [],
|
||||
'total' => 0,
|
||||
'summary' => [
|
||||
'rows' => 0,
|
||||
'approvals' => 0,
|
||||
'amount_total' => 0,
|
||||
'bank_amount_total' => 0,
|
||||
],
|
||||
'status' => 'succeeded',
|
||||
'last_synced_at' => null,
|
||||
'scope' => 'personal_permission',
|
||||
]);
|
||||
|
||||
$data = (new ProcessDetailsPageData($repository))->page(Request::create(
|
||||
'/1/workspace/process-data/oa-process',
|
||||
'GET',
|
||||
['status' => '已终止'],
|
||||
));
|
||||
|
||||
$this->assertSame('已撤销', $data['filters']['status']);
|
||||
}
|
||||
|
||||
#[DataProvider('perPageProvider')]
|
||||
public function test_page_size_only_accepts_supported_options(mixed $requested, int $expected): void
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user