diff --git a/modules/EcommerceFinance/View/SupplierReconciliationPageData.php b/modules/EcommerceFinance/View/SupplierReconciliationPageData.php
index bf55d097..a9f24d0c 100644
--- a/modules/EcommerceFinance/View/SupplierReconciliationPageData.php
+++ b/modules/EcommerceFinance/View/SupplierReconciliationPageData.php
@@ -23,6 +23,9 @@ class SupplierReconciliationPageData
/** 退货明细行较高:每页 10 行,并在页面内固定表头滚动查看。 */
private const RETURN_PER_PAGE = 10;
+ /** 财务端比对明细:每页 15 条。 */
+ private const COMPARISON_PER_PAGE = 15;
+
public function __construct(
private readonly SupplierPayableOverrideStore $store,
private readonly SupplierStatementStore $statements,
@@ -109,17 +112,31 @@ class SupplierReconciliationPageData
}
// 选中厂家时页面切换成「比对明细」视图(列表不再渲染,避免 100+ 家时往下翻)。
- $selected = trim((string) request()->query('supplier', ''));
+ $supplierQuery = trim((string) request()->query('supplier', ''));
+ $selected = in_array($supplierQuery, $supplierNames, true) ? $supplierQuery : '';
+ $supplierNotFound = $supplierQuery !== '' && $selected === '';
$detail = null;
- if ($selected !== '' && in_array($selected, $supplierNames, true)) {
+ if ($selected !== '') {
$detail = $this->detail($mine, $selected, $companyId);
- } else {
- $selected = '';
+ $totalRows = count($detail['rows']);
+ $pages = max((int) ceil($totalRows / self::COMPARISON_PER_PAGE), 1);
+ $currentPage = min(max((int) request()->query('compare_page', 1), 1), $pages);
+ $detail['total_rows'] = $totalRows;
+ $detail['page'] = $currentPage;
+ $detail['pages'] = $pages;
+ $detail['per_page'] = self::COMPARISON_PER_PAGE;
+ $detail['rows'] = array_slice(
+ $detail['rows'],
+ ($currentPage - 1) * self::COMPARISON_PER_PAGE,
+ self::COMPARISON_PER_PAGE,
+ );
}
return [
'view' => 'business.pages.supplier-reconciliation.finance-data',
'supplierNames' => $supplierNames,
+ 'supplierQuery' => $supplierQuery,
+ 'supplierNotFound' => $supplierNotFound,
'uploads' => $uploads,
'months' => $months,
'month' => $month,
diff --git a/resources/views/business/pages/supplier-reconciliation/finance-data.blade.php b/resources/views/business/pages/supplier-reconciliation/finance-data.blade.php
index cfbf4cfc..70c39654 100644
--- a/resources/views/business/pages/supplier-reconciliation/finance-data.blade.php
+++ b/resources/views/business/pages/supplier-reconciliation/finance-data.blade.php
@@ -36,6 +36,9 @@
.supplier-reconciliation-page .sr-summary-cell { padding: 10px 12px; border: 1px solid var(--vip-border, #D8D2C7); border-radius: 8px; display: grid; gap: 4px; }
.supplier-reconciliation-page .sr-summary-cell b { font-size: 1.125rem; font-weight: 600; }
.supplier-reconciliation-page .sr-summary-cell span { color: var(--vip-content-muted, #595959); font-size: .75rem; }
+ .supplier-reconciliation-page .sr-comparison-scroll { max-height: min(62vh, 560px); overflow: auto; scrollbar-gutter: stable; }
+ .supplier-reconciliation-page .sr-comparison-scroll thead th { position: sticky; top: 0; z-index: 4; box-shadow: 0 1px 0 var(--vip-border, #D8D2C7); }
+ .supplier-reconciliation-page .sr-pagination { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; color: var(--vip-content-muted, #595959); font-size: .8125rem; }
.supplier-reconciliation-page .sr-robot-config { padding: 10px 12px; border: 1px solid #EFEAE0; border-radius: 8px; background: #FCFAF5; }
.supplier-reconciliation-page .sr-robot-config summary { cursor: pointer; color: #5B5343; font-size: .8125rem; font-weight: 600; }
.supplier-reconciliation-page .sr-robot-form { display: grid; grid-template-columns: minmax(260px, 2fr) minmax(180px, 1fr) auto; gap: 10px; align-items: end; margin-top: 12px; }
@@ -82,12 +85,17 @@
@@ -97,6 +105,11 @@
厂家登录账号管理
@endcanany
+ @if ($supplierNotFound)
+
+ 未找到名为「{{ $supplierQuery }}」的供应商,请输入完整名称并从搜索建议中选择。
+
+ @endif
比对口径:按「采购单号 + 日期 + 条码」核对,同一采购单号、日期和条码的多行先合并数量与金额。
数量、金额一致 → 正常结算;厂家多(我方没有)→ 不结算、退回修改;
@@ -251,7 +264,7 @@
按口径结算合计{{ number_format($detail['summary']['settleAmount'], 2) }}
差异金额{{ number_format($detail['summary']['differenceAmount'], 2) }}
-
@php $returnAccounting = $financeReturnPanel['accounting_summary']; @endphp
diff --git a/tests/Feature/Business/SupplierReconciliationTest.php b/tests/Feature/Business/SupplierReconciliationTest.php
index 3b831da5..0084e0c0 100644
--- a/tests/Feature/Business/SupplierReconciliationTest.php
+++ b/tests/Feature/Business/SupplierReconciliationTest.php
@@ -47,6 +47,7 @@ class SupplierReconciliationTest extends FeatureTestCase
->assertSeeText('厂家多')
->assertSeeText('取金额低的一方结算')
->assertSeeText('采购单号 + 日期 + 条码')
+ ->assertSee('list="finance-supplier-options"', false)
->assertSeeText('对账人')
->assertSeeText('下载对账单模板');
}
@@ -114,11 +115,101 @@ class SupplierReconciliationTest extends FeatureTestCase
->assertSeeText('厂家多 1.00')
->assertSeeText('厂家多 ¥128.00')
->assertSeeText('编号-ZSJ-1N1097V01')
+ ->assertSee('sr-comparison-scroll', false)
+ ->assertSeeText('每页 15 条')
->assertSeeText('ZSJ-3')
->assertSeeText('ZSJ-2')
->assertDontSeeText('各厂家上传情况');
}
+ public function test_finance_supplier_filter_requires_an_exact_searchable_name(): void
+ {
+ $store = app(SupplierPayableOverrideStore::class);
+ $companyId = (int) $this->company->id;
+ foreach (['晨丰', '鸣飞家具'] as $supplier) {
+ $store->save('final:supplier:payment-reconciliation', 'supplier:' . $supplier, [
+ 'summary_month' => '2026-09',
+ 'purchaseNo' => 'PO-' . $supplier,
+ 'date' => '2026-09-30',
+ 'supplier' => $supplier,
+ 'skuCode' => 'A-1',
+ 'qty' => '1',
+ 'unitPrice' => '10',
+ 'amount' => '10',
+ 'reconciler' => '张三',
+ ], $companyId, null);
+ }
+
+ $pageUrl = route('business.pages.show', [
+ 'company_id' => $companyId,
+ 'section' => 'supplier-reconciliation',
+ 'page' => 'finance-data',
+ ]);
+
+ $this->loginAs()->get($pageUrl . '?supplier=' . urlencode('晨丰'))
+ ->assertOk()
+ ->assertSeeText('晨丰 · 比对明细')
+ ->assertSee('value="晨丰"', false);
+
+ $this->loginAs()->get($pageUrl . '?supplier=' . urlencode('不存在的厂家'))
+ ->assertOk()
+ ->assertSeeText('未找到名为「不存在的厂家」的供应商')
+ ->assertDontSeeText('晨丰 · 比对明细')
+ ->assertDontSeeText('鸣飞家具 · 比对明细');
+ }
+
+ public function test_finance_comparison_rows_are_paginated_fifteen_per_page(): void
+ {
+ $companyId = (int) $this->company->id;
+ $store = app(SupplierPayableOverrideStore::class);
+ $statementRows = [];
+ for ($i = 1; $i <= 20; $i++) {
+ $purchaseNo = sprintf('PO-%02d', $i);
+ $barcode = sprintf('A-%02d', $i);
+ $store->save('final:supplier:payment-reconciliation', 'compare:' . $i, [
+ 'summary_month' => '2026-09',
+ 'purchaseNo' => $purchaseNo,
+ 'date' => '2026-09-30',
+ 'supplier' => '晨丰',
+ 'skuCode' => $barcode,
+ 'qty' => '1',
+ 'unitPrice' => '10',
+ 'amount' => '10',
+ 'reconciler' => '张三',
+ ], $companyId, null);
+ $statementRows[] = array_merge(
+ $this->statementRow($barcode . 'N1097V01', '10.00', '1', '10.00'),
+ ['purchaseNo' => $purchaseNo],
+ );
+ }
+ $this->seedStatement('晨丰', $statementRows);
+
+ $pageUrl = fn (int $page): string => route('business.pages.show', [
+ 'company_id' => $companyId,
+ 'section' => 'supplier-reconciliation',
+ 'page' => 'finance-data',
+ 'supplier' => '晨丰',
+ 'compare_page' => $page,
+ ]);
+
+ $this->loginAs()->get($pageUrl(1))
+ ->assertOk()
+ ->assertSeeText('第 1 / 2 页')
+ ->assertSeeText('每页 15 条')
+ ->assertSeeText('A-01')
+ ->assertSeeText('A-15')
+ ->assertDontSeeText('A-16')
+ ->assertDontSeeText('A-20');
+
+ $this->loginAs()->get($pageUrl(2))
+ ->assertOk()
+ ->assertSeeText('第 2 / 2 页')
+ ->assertSeeText('A-16')
+ ->assertSeeText('A-20')
+ ->assertDontSeeText('A-15')
+ ->assertDontSeeText('A-01');
+ }
+
public function test_supplier_page_shows_uploads_and_return_rows(): void
{
$this->seedStatement('晨丰', [