新增退件费导入页面与50万行上限 (#216)
Co-authored-by: 苏木 <594326199@qq.com> Co-committed-by: 苏木 <594326199@qq.com>
This commit is contained in:
parent
1e3f014743
commit
12164f22a7
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -698,7 +698,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,
|
||||
|
||||
@ -541,6 +541,7 @@ $navigation = [
|
||||
['slug' => 'courier-payables-report', 'title' => '快递应付表'],
|
||||
['slug' => 'freight-difference', 'title' => '运费差'],
|
||||
['slug' => 'other-surcharge-summary', 'title' => '其他附加费汇总'],
|
||||
['slug' => 'return-fee', 'title' => '退件费'],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
||||
Binary file not shown.
@ -10,6 +10,11 @@
|
||||
'template_name' => '其他附加费导入模板',
|
||||
'field_count' => 11,
|
||||
],
|
||||
'return-fee' => [
|
||||
'dialog_title' => '导入退件费',
|
||||
'template_name' => '退件费导入模板',
|
||||
'field_count' => 11,
|
||||
],
|
||||
][$courierSummaryPage] ?? null;
|
||||
@endphp
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user