From f4d8a15aedf95643cbc9e7d18df0825d9031c07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E6=9C=A8?= <594326199@qq.com> Date: Sun, 20 Sep 2026 15:50:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E9=80=80=E4=BB=B6?= =?UTF-8?q?=E8=B4=B9=E5=AF=BC=E5=85=A5=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在其他附加费汇总后增加退件费页面,复用现有导入、分页和导出流程。导入模板按业务附件保留11列结构并清除真实明细,单次导入上限设为500,000行。 --- app/Business/CourierPayableSummaryStore.php | 2 + .../Business/CourierPayableSummaries.php | 5 + app/Http/Controllers/Business/Pages.php | 2 +- config/business_navigation.php | 1 + .../return-fee-import-template.xlsx | Bin 0 -> 5846 bytes .../courier-payables/summary-table.blade.php | 5 + routes/admin.php | 6 +- .../CourierPayableSummaryPagesTest.php | 198 +++++++++++++++++- 8 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 resources/templates/courier-payables/return-fee-import-template.xlsx diff --git a/app/Business/CourierPayableSummaryStore.php b/app/Business/CourierPayableSummaryStore.php index 66af5a21..c7f7aaba 100644 --- a/app/Business/CourierPayableSummaryStore.php +++ b/app/Business/CourierPayableSummaryStore.php @@ -16,6 +16,7 @@ class CourierPayableSummaryStore public const PAGES = [ 'freight-difference' => '运费差', 'other-surcharge-summary' => '其他附加费汇总', + 'return-fee' => '退件费', ]; private const MAX_COLUMNS = 200; @@ -23,6 +24,7 @@ class CourierPayableSummaryStore private const MAX_ROWS_BY_PAGE = [ 'freight-difference' => 500_000, 'other-surcharge-summary' => 500_000, + 'return-fee' => 500_000, ]; private const MAX_CELL_LENGTH = 10000; diff --git a/app/Http/Controllers/Business/CourierPayableSummaries.php b/app/Http/Controllers/Business/CourierPayableSummaries.php index 610f4d14..fa29b02f 100644 --- a/app/Http/Controllers/Business/CourierPayableSummaries.php +++ b/app/Http/Controllers/Business/CourierPayableSummaries.php @@ -24,6 +24,11 @@ class CourierPayableSummaries extends Controller 'ascii_name' => 'other_surcharge_import_template.xlsx', 'download_name' => '其他附加费导入模板.xlsx', ], + 'return-fee' => [ + 'path' => 'templates/courier-payables/return-fee-import-template.xlsx', + 'ascii_name' => 'return_fee_import_template.xlsx', + 'download_name' => '退件费导入模板.xlsx', + ], ]; public function __construct(private readonly CourierPayableSummaryStore $store) diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index cf8c957e..73900ff9 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -658,7 +658,7 @@ class Pages extends Controller 'payable-cloud-warehouse-vehicle-fee' => $this->contentForDingPayableWorkflows(), 'online-daifa-price' => $this->contentForOnlineDaifaPrice(), 'courier-surcharge' => $this->contentForCourierSurcharge(), - 'freight-difference', 'other-surcharge-summary' => $this->contentForCourierPayableSummary($page), + 'freight-difference', 'other-surcharge-summary', 'return-fee' => $this->contentForCourierPayableSummary($page), 'withdrawal-management' => $this->contentForWithdrawalManagement(), 'bill-offset' => app(\Modules\EcommerceFinance\View\BillOffsetPageData::class)->page(), default => null, diff --git a/config/business_navigation.php b/config/business_navigation.php index d51c89b9..951e74a3 100644 --- a/config/business_navigation.php +++ b/config/business_navigation.php @@ -541,6 +541,7 @@ $navigation = [ ['slug' => 'courier-payables-report', 'title' => '快递应付表'], ['slug' => 'freight-difference', 'title' => '运费差'], ['slug' => 'other-surcharge-summary', 'title' => '其他附加费汇总'], + ['slug' => 'return-fee', 'title' => '退件费'], ], ], [ diff --git a/resources/templates/courier-payables/return-fee-import-template.xlsx b/resources/templates/courier-payables/return-fee-import-template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e65abf208ca2411cfe7470fe7964744c54034199 GIT binary patch literal 5846 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 literal 0 HcmV?d00001 diff --git a/resources/views/business/pages/courier-payables/summary-table.blade.php b/resources/views/business/pages/courier-payables/summary-table.blade.php index ee28f64b..3447f559 100644 --- a/resources/views/business/pages/courier-payables/summary-table.blade.php +++ b/resources/views/business/pages/courier-payables/summary-table.blade.php @@ -10,6 +10,11 @@ 'template_name' => '其他附加费导入模板', 'field_count' => 11, ], + 'return-fee' => [ + 'dialog_title' => '导入退件费', + 'template_name' => '退件费导入模板', + 'field_count' => 11, + ], ][$courierSummaryPage] ?? null; @endphp diff --git a/routes/admin.php b/routes/admin.php index 256f66ec..c5aff9ba 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -198,13 +198,13 @@ Route::prefix('workspace/courier-payables/courier-surcharge') }); Route::post('workspace/courier-payables/{summaryPage}/summary-import', [CourierPayableSummaries::class, 'import']) - ->where('summaryPage', 'freight-difference|other-surcharge-summary') + ->where('summaryPage', 'freight-difference|other-surcharge-summary|return-fee') ->name('business.courier-payable-summaries.import'); Route::get('workspace/courier-payables/{summaryPage}/summary-template', [CourierPayableSummaries::class, 'template']) - ->where('summaryPage', 'freight-difference|other-surcharge-summary') + ->where('summaryPage', 'freight-difference|other-surcharge-summary|return-fee') ->name('business.courier-payable-summaries.template'); Route::get('workspace/courier-payables/{summaryPage}/summary-export', [CourierPayableSummaries::class, 'export']) - ->where('summaryPage', 'freight-difference|other-surcharge-summary') + ->where('summaryPage', 'freight-difference|other-surcharge-summary|return-fee') ->name('business.courier-payable-summaries.export'); Route::get('workspace/{section}/{page?}/{tab?}', 'Business\Pages@show') diff --git a/tests/Feature/Business/CourierPayableSummaryPagesTest.php b/tests/Feature/Business/CourierPayableSummaryPagesTest.php index d5934a9c..c4adcd69 100644 --- a/tests/Feature/Business/CourierPayableSummaryPagesTest.php +++ b/tests/Feature/Business/CourierPayableSummaryPagesTest.php @@ -22,7 +22,7 @@ class CourierPayableSummaryPagesTest extends FeatureTestCase Storage::fake('uploads'); } - public function test_final_workspace_places_both_summary_pages_after_courier_payables_report(): void + public function test_final_workspace_places_summary_pages_after_courier_payables_report(): void { $response = $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables-final', @@ -32,6 +32,7 @@ class CourierPayableSummaryPagesTest extends FeatureTestCase route('business.pages.show', ['section' => 'courier-payables', 'page' => 'courier-payables-report']), route('business.pages.show', ['section' => 'courier-payables', 'page' => 'freight-difference']), route('business.pages.show', ['section' => 'courier-payables', 'page' => 'other-surcharge-summary']), + route('business.pages.show', ['section' => 'courier-payables', 'page' => 'return-fee']), ], false); } @@ -166,6 +167,69 @@ class CourierPayableSummaryPagesTest extends FeatureTestCase $spreadsheet->disconnectWorksheets(); } + public function test_return_fee_import_dialog_downloads_the_supplied_xlsx_template(): void + { + $pageResponse = $this->loginAs()->get(route('business.pages.show', [ + 'section' => 'courier-payables', + 'page' => 'return-fee', + ])); + + $pageResponse + ->assertOk() + ->assertSee('data-courier-summary-import-dialog', false) + ->assertSeeText('导入退件费') + ->assertSeeText('退件费导入模板') + ->assertSeeText('.xlsx · 11 个字段') + ->assertSeeText('下载模板') + ->assertSee(route('business.courier-payable-summaries.template', [ + 'summaryPage' => 'return-fee', + ]), false); + + $templateResponse = $this->loginAs()->get(route('business.courier-payable-summaries.template', [ + 'summaryPage' => 'return-fee', + ])); + + $templateResponse + ->assertOk() + ->assertDownload('return_fee_import_template.xlsx') + ->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + $this->assertStringContainsString( + "filename*=utf-8''" . rawurlencode('退件费导入模板.xlsx'), + (string) $templateResponse->headers->get('Content-Disposition'), + ); + + $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/return-fee-import-template.xlsx')); + $worksheet = $spreadsheet->getActiveSheet(); + + $this->assertSame([ + '日期', + '负责人', + '快递名称', + '运单号码', + 'ID', + '条码', + '店铺', + '交易订单', + '结算金额', + '备注', + '成品条码', + ], $worksheet->rangeToArray('A1:K1', null, false, false)[0]); + + foreach (['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K'] as $column) { + $this->assertSame('@', $worksheet->getStyle($column . '2')->getNumberFormat()->getFormatCode()); + } + $this->assertSame('0.00########', $worksheet->getStyle('I2')->getNumberFormat()->getFormatCode()); + + foreach ($worksheet->rangeToArray('A2:K15', null, false, false) as $row) { + $this->assertSame([], array_values(array_filter( + $row, + static fn (mixed $value): bool => $value !== null && $value !== '', + ))); + } + + $spreadsheet->disconnectWorksheets(); + } + public function test_completed_freight_difference_template_preserves_long_identifiers_on_import(): void { $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/freight-difference-import-template.xlsx')); @@ -299,6 +363,78 @@ class CourierPayableSummaryPagesTest extends FeatureTestCase } } + public function test_completed_return_fee_template_preserves_long_identifiers_and_amount_precision(): void + { + $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/return-fee-import-template.xlsx')); + $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->fromArray([ + '2026-09-20', + '苏木', + '测试快递', + '7732543323146123456', + '6041543429417123456', + 'SJ038017N1410V01', + '测试店铺', + '5110751415866048404', + 12.3456, + '退件费', + 'CP000000000000001', + ], null, 'A2'); + $worksheet->getCell('D2')->setValueExplicit('7732543323146123456', DataType::TYPE_STRING); + $worksheet->getCell('E2')->setValueExplicit('6041543429417123456', DataType::TYPE_STRING); + $worksheet->getCell('F2')->setValueExplicit('SJ038017N1410V01', DataType::TYPE_STRING); + $worksheet->getCell('H2')->setValueExplicit('5110751415866048404', DataType::TYPE_STRING); + $worksheet->getCell('K2')->setValueExplicit('CP000000000000001', DataType::TYPE_STRING); + + $path = tempnam(sys_get_temp_dir(), 'return-fee-template-'); + $this->assertNotFalse($path); + $xlsxPath = $path . '.xlsx'; + + try { + IOFactory::createWriter($spreadsheet, 'Xlsx')->save($xlsxPath); + $spreadsheet->disconnectWorksheets(); + + $file = new UploadedFile( + $xlsxPath, + '退件费导入模板.xlsx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + null, + true, + ); + + $this->loginAs()->post(route('business.courier-payable-summaries.import', [ + 'summaryPage' => 'return-fee', + ]), ['import' => $file]) + ->assertRedirect(route('business.pages.show', [ + 'section' => 'courier-payables', + 'page' => 'return-fee', + ])) + ->assertSessionHas('success', '导入完成:1 条数据,11 个字段。'); + + $snapshot = app(CourierPayableSummaryStore::class)->snapshot( + (int) $this->company->id, + 'return-fee', + ); + $this->assertSame('2026-09-20', $snapshot['rows'][0][0]); + $this->assertSame('7732543323146123456', $snapshot['rows'][0][3]); + $this->assertSame('6041543429417123456', $snapshot['rows'][0][4]); + $this->assertSame('SJ038017N1410V01', $snapshot['rows'][0][5]); + $this->assertSame('5110751415866048404', $snapshot['rows'][0][7]); + $this->assertSame('12.3456', $snapshot['rows'][0][8]); + $this->assertSame('CP000000000000001', $snapshot['rows'][0][10]); + } finally { + if (isset($spreadsheet)) { + $spreadsheet->disconnectWorksheets(); + } + if ($path !== false && is_file($path)) { + unlink($path); + } + if (isset($xlsxPath) && is_file($xlsxPath)) { + unlink($xlsxPath); + } + } + } + public function test_import_replaces_the_page_snapshot_and_export_returns_all_rows(): void { $file = UploadedFile::fake()->createWithContent( @@ -340,18 +476,64 @@ class CourierPayableSummaryPagesTest extends FeatureTestCase $this->assertStringContainsString("\t=待确认", $content); } + public function test_return_fee_page_paginates_and_export_includes_all_imported_rows(): void + { + $rows = ["运单号码,交易订单,结算金额\n"]; + for ($index = 1; $index <= 51; $index++) { + $rows[] = sprintf("SF%018d,ORDER%015d,%d.%04d\n", $index, $index, $index, $index); + } + + $file = UploadedFile::fake()->createWithContent('退件费.csv', implode('', $rows)); + $this->loginAs()->post(route('business.courier-payable-summaries.import', [ + 'summaryPage' => 'return-fee', + ]), ['import' => $file]) + ->assertRedirect(route('business.pages.show', [ + 'section' => 'courier-payables', + 'page' => 'return-fee', + ])) + ->assertSessionHas('success', '导入完成:51 条数据,3 个字段。'); + + $firstWaybill = sprintf('SF%018d', 1); + $lastWaybill = sprintf('SF%018d', 51); + $pageRoute = route('business.pages.show', [ + 'section' => 'courier-payables', + 'page' => 'return-fee', + ]); + + $this->loginAs()->get($pageRoute) + ->assertOk() + ->assertSeeText($firstWaybill) + ->assertDontSeeText($lastWaybill) + ->assertSeeText('第 1 / 2 页'); + $this->loginAs()->get($pageRoute . '?page=2') + ->assertOk() + ->assertSeeText($lastWaybill) + ->assertSeeText('第 2 / 2 页'); + + $response = $this->loginAs()->get(route('business.courier-payable-summaries.export', [ + 'summaryPage' => 'return-fee', + ])); + $content = $response->streamedContent(); + + $response->assertOk()->assertHeader('content-type', 'text/csv; charset=UTF-8'); + $this->assertStringContainsString($firstWaybill, $content); + $this->assertStringContainsString($lastWaybill, $content); + $this->assertStringContainsString('51.0051', $content); + } + public function test_summary_imports_accept_more_than_ten_thousand_rows(): void { $store = app(CourierPayableSummaryStore::class); $this->assertSame(500000, $store->maxRows('freight-difference')); $this->assertSame(500000, $store->maxRows('other-surcharge-summary')); + $this->assertSame(500000, $store->maxRows('return-fee')); $rows = ["快递公司,结算月份\n"]; for ($index = 1; $index <= 10001; $index++) { $rows[] = "测试快递,2026-08\n"; } - foreach (['freight-difference', 'other-surcharge-summary'] as $page) { + foreach (['freight-difference', 'other-surcharge-summary', 'return-fee'] as $page) { $file = UploadedFile::fake()->createWithContent($page . '.csv', implode('', $rows)); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ @@ -391,5 +573,17 @@ class CourierPayableSummaryPagesTest extends FeatureTestCase $this->loginAs()->get(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'other-surcharge-summary', ]))->assertForbidden(); + + $this->loginAs()->post(route('business.courier-payable-summaries.import', [ + 'summaryPage' => 'return-fee', + ]))->assertForbidden(); + + $this->loginAs()->get(route('business.courier-payable-summaries.export', [ + 'summaryPage' => 'return-fee', + ]))->assertForbidden(); + + $this->loginAs()->get(route('business.courier-payable-summaries.template', [ + 'summaryPage' => 'return-fee', + ]))->assertForbidden(); } }