feat: OA流程付款状态直接筛选已付款

移除包含已付款审批开关,付款状态直接筛选待付款和已付款。
This commit is contained in:
yueyang 2026-11-13 14:04:52 +08:00
parent f71fa8f669
commit efa4193fbd
6 changed files with 24 additions and 165 deletions

View File

@ -510,78 +510,6 @@ class DingProcessDetailMirrorRepository
{
$query->where('ding_process_detail_mirror.source_scope', $this->scope());
if (array_key_exists('include_paid', $filters) && $filters['include_paid'] !== '1') {
$query->whereNotExists(function (Builder $paidApproval): void {
$paidApproval
->selectRaw('1')
->from('ding_process_detail_mirror as paid_approval')
->whereColumn(
'paid_approval.business_id',
'ding_process_detail_mirror.business_id',
)
->whereColumn(
'paid_approval.source_scope',
'ding_process_detail_mirror.source_scope',
)
->where('paid_approval.cashier_status', 'completed');
});
if ($this->structuredPaymentsEnabled()) {
$query->whereNotExists(function (Builder $fullyPaidApproval): void {
$fullyPaidApproval
->select('payment_fully_paid.business_id')
->from('ding_process_payment_mirror as payment_fully_paid')
->whereColumn(
'payment_fully_paid.business_id',
'ding_process_detail_mirror.business_id',
)
->whereColumn(
'payment_fully_paid.source_scope',
'ding_process_detail_mirror.source_scope',
)
->groupBy(
'payment_fully_paid.business_id',
'payment_fully_paid.source_scope',
)
->havingRaw(
'SUM(COALESCE(payment_fully_paid.amount, 0)) > 0',
)
->havingRaw(
'SUM(COALESCE(payment_fully_paid.amount, 0)) + 0.01 >= ('
. 'SELECT SUM(COALESCE(detail_fully_paid.amount, 0)) '
. 'FROM ding_process_detail_mirror AS detail_fully_paid '
. 'WHERE detail_fully_paid.business_id = ding_process_detail_mirror.business_id '
. 'AND detail_fully_paid.source_scope = ding_process_detail_mirror.source_scope'
. ')',
);
});
} else {
$query->whereNotExists(function (Builder $fullyPaidApproval): void {
$fullyPaidApproval
->select('fully_paid_approval.business_id')
->from('ding_process_detail_mirror as fully_paid_approval')
->whereColumn(
'fully_paid_approval.business_id',
'ding_process_detail_mirror.business_id',
)
->whereColumn(
'fully_paid_approval.source_scope',
'ding_process_detail_mirror.source_scope',
)
->groupBy(
'fully_paid_approval.business_id',
'fully_paid_approval.source_scope',
)
->havingRaw(
'SUM(COALESCE(fully_paid_approval.bank_amount, 0)) > 0',
)
->havingRaw(
'SUM(COALESCE(fully_paid_approval.bank_amount, 0)) + 0.01 '
. '>= SUM(COALESCE(fully_paid_approval.amount, 0))',
);
});
}
}
$paidStart = $this->filterDate($filters['paid_start'] ?? '');
$paidEnd = $this->filterDate($filters['paid_end'] ?? '');
if ($paidStart === '' && $paidEnd === '') {

View File

@ -232,7 +232,6 @@ class ProcessDetailsPageData
'current_approver_name' => mb_substr(trim((string) $request->query('current_approver_name', '')), 0, 100),
'waiting_bucket' => $waitingBucket,
'keyword' => mb_substr(trim((string) $request->query('keyword', '')), 0, 100),
'include_paid' => $request->boolean('include_paid') ? '1' : '',
];
}

View File

@ -267,10 +267,6 @@
grid-column: span 5;
}
.process-details-field.is-paid-toggle {
grid-column: span 3;
}
.process-details-field.is-business {
grid-column: span 3;
}
@ -314,61 +310,6 @@
box-shadow: 0 0 0 3px rgba(133, 102, 50, .2);
}
.process-details-paid-scope {
display: inline-flex;
align-items: center;
gap: .5rem;
height: 2.125rem;
padding: 0 .65rem;
border: 1px solid var(--vip-border, #D8D2C7);
border-radius: .5rem;
background: #fff;
color: var(--vip-content-ink, #171717) !important;
font-size: .8125rem !important;
font-weight: 500 !important;
cursor: pointer;
}
.process-details-paid-scope input {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.process-details-paid-scope__box {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1rem;
height: 1rem;
border: 1px solid var(--vip-border, #D8D2C7);
border-radius: .25rem;
background: #fff;
color: #fff;
}
.process-details-paid-scope__box .material-icons-outlined {
font-size: .8rem;
opacity: 0;
}
.process-details-paid-scope input:checked + .process-details-paid-scope__box {
border-color: var(--vip-content-accent-dark, #856632);
background: var(--vip-content-accent-dark, #856632);
}
.process-details-paid-scope input:checked + .process-details-paid-scope__box .material-icons-outlined {
opacity: 1;
}
.process-details-paid-scope input:focus-visible + .process-details-paid-scope__box {
outline: none;
box-shadow: 0 0 0 3px rgba(133, 102, 50, .2);
}
.process-details-combobox {
position: relative;
display: flex;
@ -1000,7 +941,6 @@
.process-details-field.is-date-range,
.process-details-field.is-waiting,
.process-details-field.is-keyword,
.process-details-field.is-paid-toggle,
.process-details-actions {
grid-column: span 6;
}
@ -1056,7 +996,6 @@
.process-details-field.is-approver,
.process-details-field.is-date-range,
.process-details-field.is-waiting,
.process-details-field.is-paid-toggle,
.process-details-field.is-keyword,
.process-details-actions {
grid-column: 1;
@ -1317,22 +1256,6 @@
@endforeach
</select>
</div>
<div class="process-details-field is-paid-toggle">
<span class="process-details-field__label">展示范围</span>
<label class="process-details-paid-scope" for="process-details-include-paid">
<input
id="process-details-include-paid"
type="checkbox"
name="include_paid"
value="1"
@checked(($filters['include_paid'] ?? '') === '1')
>
<span class="process-details-paid-scope__box" aria-hidden="true">
<span class="material-icons-outlined">check</span>
</span>
<span>包含已付款审批</span>
</label>
</div>
<div class="process-details-field">
<label for="process-details-originator">发起人</label>
<input id="process-details-originator" type="search" name="originator_name" value="{{ $filters['originator_name'] ?? '' }}" placeholder="支持包含查询">
@ -1390,7 +1313,7 @@
</summary>
<div class="process-details-note-content">
<p>
页面默认仅展示现金付款节点尚未完成的审批;需核对历史全量时,可勾选“包含已付款审批”。
付款状态可直接选择“待付款”或“已付款”:待付款只展示尚未完成付款的审批,已付款只展示已有付款事实的审批。
应付金额按审批费用明细汇总;实际付款按审批表单中的结构化付款明细汇总。
未产生付款记录的审批,其实际付款按 0 元展示,因此应付金额与实际付款金额不要求相等。
打款日期以审批单为准:审批单在所选期间有打款记录时,整张审批单的明细和付款金额都计入汇总;

View File

@ -104,9 +104,9 @@ class ProcessDetailsPageTest extends FeatureTestCase
->assertSee('name="waiting_bucket"', false)
->assertSee('value="1_2d" selected', false)
->assertSee('2天以上', false)
->assertSee('name="include_paid"', false)
->assertSee('包含已付款审批')
->assertSee('页面默认仅展示现金付款节点尚未完成的审批')
->assertDontSee('name="include_paid"', false)
->assertDontSee('包含已付款审批')
->assertSee('付款状态可直接选择“待付款”或“已付款”')
->assertDontSee('name="entry_start"', false)
->assertDontSee('name="entry_end"', false)
->assertSee('placeholder="开始日期"', false)
@ -119,6 +119,7 @@ class ProcessDetailsPageTest extends FeatureTestCase
->assertSee('name="payment_state"', false)
->assertSee('审批状态')
->assertSee('付款状态')
->assertSee('<option value="已付款"', false)
->assertSee('当前审批人')
->assertSee('进入时间')
->assertSee('已等待')

View File

@ -126,15 +126,21 @@ class DingProcessDetailMirrorRepositoryTest extends TestCase
);
}
public function test_paid_approvals_are_excluded_unless_explicitly_included(): void
public function test_payment_status_filter_splits_paid_and_pending_approvals(): void
{
DB::connection('business_mirror')
->table('ding_process_detail_mirror')
->where('business_id', '202601011200000000001')
->update(['cashier_status' => 'pending']);
$all = $this->repository->paginate([], 1, 50);
$this->assertSame(4, $all['total']);
$this->assertSame(2, $all['summary']['approvals']);
$this->assertEqualsWithDelta(650.5, $all['summary']['amount_total'], 0.001);
$this->assertEqualsWithDelta(350.5, $all['summary']['bank_amount_total'], 0.001);
$pending = $this->repository->paginate([
'include_paid' => '',
'payment_state' => '待付款',
], 1, 50);
$this->assertSame(2, $pending['total']);
$this->assertSame(2, $pending['summary']['rows']);
@ -142,14 +148,16 @@ class DingProcessDetailMirrorRepositoryTest extends TestCase
$this->assertEqualsWithDelta(500, $pending['summary']['amount_total'], 0.001);
$this->assertEqualsWithDelta(200, $pending['summary']['bank_amount_total'], 0.001);
$all = $this->repository->paginate([
'include_paid' => '1',
$paid = $this->repository->paginate([
'payment_state' => '已付款',
], 1, 50);
$this->assertSame(4, $all['total']);
$this->assertSame(2, $all['summary']['approvals']);
$this->assertSame(2, $paid['total']);
$this->assertSame(1, $paid['summary']['approvals']);
$this->assertEqualsWithDelta(150.5, $paid['summary']['amount_total'], 0.001);
$this->assertEqualsWithDelta(150.5, $paid['summary']['bank_amount_total'], 0.001);
$approvalSummary = $this->repository->paginateApprovals([
'include_paid' => '',
'payment_state' => '待付款',
], 1, 50);
$this->assertSame(1, $approvalSummary['total']);
$this->assertSame(2, $approvalSummary['summary']['rows']);

View File

@ -53,9 +53,9 @@ class ProcessDetailsPageDataTest extends TestCase
$this->assertStringContainsString('<th class="num">应付金额</th>', $content);
$this->assertStringContainsString('<th class="num">实际付款</th>', $content);
$this->assertStringContainsString('<th>进入时间</th>', $content);
$this->assertStringContainsString('name="include_paid"', $content);
$this->assertStringContainsString('包含已付款审批', $content);
$this->assertStringContainsString('页面默认仅展示现金付款节点尚未完成的审批', $content);
$this->assertStringNotContainsString('name="include_paid"', $content);
$this->assertStringNotContainsString('包含已付款审批', $content);
$this->assertStringContainsString('付款状态可直接选择“待付款”或“已付款”', $content);
$this->assertStringNotContainsString('process-details-approval-link', $content);
$this->assertStringNotContainsString(
"route('business.process-details.detail'",
@ -141,7 +141,7 @@ class ProcessDetailsPageDataTest extends TestCase
$this->assertSame('已付款', $data['filters']['payment_state']);
$this->assertSame('王五', $data['filters']['current_approver_name']);
$this->assertSame('12_24h', $data['filters']['waiting_bucket']);
$this->assertSame('', $data['filters']['include_paid']);
$this->assertArrayNotHasKey('include_paid', $data['filters']);
$this->assertSame(
[
'待付款',