From 8162afa969b8c88b0ef274498a68373ed93eed79 Mon Sep 17 00:00:00 2001 From: renshen <1005457301@qq.com> Date: Tue, 20 Oct 2026 17:03:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E5=AF=B9=E8=B4=A6?= =?UTF-8?q?=EF=BC=9A=E8=B4=A2=E5=8A=A1=E6=AF=94=E5=AF=B9=E7=AD=9B=E9=80=89?= =?UTF-8?q?=E4=B8=8E=E5=88=86=E9=A1=B5=E4=BC=98=E5=8C=96=20(#406)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 财务端增加供应商精确搜索、比对明细吸顶表头和每页15条分页。 --- .../View/SupplierReconciliationPageData.php | 25 ++++- .../finance-data.blade.php | 60 +++++++++++- .../Business/SupplierReconciliationTest.php | 91 +++++++++++++++++++ 3 files changed, 167 insertions(+), 9 deletions(-) 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) + + @endif

比对口径:按「采购单号 + 日期 + 条码」核对,同一采购单号、日期和条码的多行先合并数量与金额。 数量、金额一致 → 正常结算;厂家多(我方没有)→ 不结算、退回修改; @@ -251,7 +264,7 @@

按口径结算合计{{ number_format($detail['summary']['settleAmount'], 2) }}
差异金额{{ number_format($detail['summary']['differenceAmount'], 2) }}
-
+
@@ -361,6 +374,43 @@
+
+ @php + $comparisonPageCount = max((int) ($detail['pages'] ?? 1), 1); + $comparisonPageCurrent = min(max((int) ($detail['page'] ?? 1), 1), $comparisonPageCount); + $comparisonPageStart = max(1, min($comparisonPageCurrent - 4, $comparisonPageCount - 8)); + $comparisonPageEnd = min($comparisonPageCount, $comparisonPageStart + 8); + $comparisonPageUrl = fn (int $number): string => route('business.pages.show', array_merge(request()->query(), [ + 'section' => $section['slug'], + 'page' => $page['slug'], + 'compare_page' => $number, + ])); + @endphp + + 共 {{ number_format((int) ($detail['total_rows'] ?? 0)) }} 条 + · 每页 {{ number_format((int) ($detail['per_page'] ?? 15)) }} 条 + · 第 {{ $comparisonPageCurrent }} / {{ $comparisonPageCount }} 页 + + @if ($comparisonPageCurrent > 1) + 上一页 + @endif + @if ($comparisonPageStart > 1) + 1 + @if ($comparisonPageStart > 2) … @endif + @endif + @for ($number = $comparisonPageStart; $number <= $comparisonPageEnd; $number++) + {{ $number }} + @endfor + @if ($comparisonPageEnd < $comparisonPageCount) + @if ($comparisonPageEnd < $comparisonPageCount - 1) … @endif + {{ $comparisonPageCount }} + @endif + @if ($comparisonPageCurrent < $comparisonPageCount) + 下一页 + @endif +
@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('晨丰', [