CaiWuXiTong/tests/Feature/Business/CourierPayableSummaryPagesTest.php
renshen 813e956df5 应付款终版目录接入独立按表数据
两个「终版」目录(供应商应付(终版)、快递应付(终版))改为独立数据:
保留原有导入、导出、查询、重置、编辑按钮和导入模板,数据初始为空、按表独立存放,
不再借用基础目录的页面与数据。

- 终版目录拥有自己的同名子页面与横向页签
- 新增终版按表数据的独立导入/导出/模板/编辑接口(数据集键 final:<目录>:<页面>)
- 供应商终版 13 个页面、快递终版 4 个页面已接入
- 终版导入模板统一增加“月份”列,记录标识列改为选填
2026-09-29 11:06:41 +08:00

751 lines
31 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Feature\Business;
use App\Business\CourierPayableSummaryStore;
use App\Models\Auth\Permission;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Tests\Feature\FeatureTestCase;
class CourierPayableSummaryPagesTest extends FeatureTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->user->attachPermission(
Permission::where('name', 'read-admin-panel')->firstOrFail()
);
Storage::fake('uploads');
}
public function test_final_directory_places_summary_pages_after_courier_payables_report(): void
{
$response = $this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables-final',
]));
$response->assertOk()->assertSeeInOrder([
route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'courier-payables-report']),
route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'freight-difference']),
route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'other-surcharge-summary']),
route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'return-fee']),
], false);
}
public function test_summary_pages_render_import_and_export_buttons(): void
{
foreach (CourierPayableSummaryStore::PAGES as $page => $title) {
$this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => $page,
]))
->assertOk()
->assertSee("data-courier-payable-summary=\"{$page}\"", false)
->assertSeeText($title)
->assertSeeText('导入')
->assertSeeText('导出')
->assertSee('name="date"', false)
->assertSee('name="person"', false)
->assertSee('name="courier"', false)
->assertSee('name="keyword"', false)
->assertSeeText('查询')
->assertSeeText('重置')
->assertSeeText('暂无数据,请先点击“导入”上传汇总表。');
}
}
public function test_each_summary_page_can_query_by_date_person_courier_and_long_identifier(): void
{
$pages = [
'freight-difference' => ['结算月份', '快递公司', 'SF000000000000000001'],
'other-surcharge-summary' => ['日期', '快递公司', 'ORDER000000000000000002'],
'return-fee' => ['日期', '快递名称', '7732543323146123456'],
];
foreach ($pages as $page => [$dateHeader, $courierHeader, $longIdentifier]) {
$this->storeSnapshot($page, [
$dateHeader,
'负责人',
$courierHeader,
'运单号码',
'备注',
], [
['2026-07', '苏木', '测试中通', $longIdentifier, '需要复核'],
['2026-08', '周生辰', '测试圆通', 'OTHER000000000000000009', '无需复核'],
]);
$url = route('business.pages.show', [
'company_id' => $this->company->id,
'section' => 'courier-payables',
'page' => $page,
]) . '?' . http_build_query([
'date' => '2026-07',
'person' => '苏木',
'courier' => '中通',
'keyword' => $longIdentifier,
]);
$this->loginAs()->get($url)
->assertOk()
->assertSeeText($longIdentifier)
->assertDontSeeText('OTHER000000000000000009')
->assertSeeText('查询到 1 条 / 共 2 条')
->assertSee('value="2026-07"', false)
->assertSee('value="苏木"', false)
->assertSee('value="中通"', false)
->assertSee('value="' . $longIdentifier . '"', false);
}
}
public function test_summary_query_preserves_filters_during_pagination_and_has_a_clear_empty_state(): void
{
$rows = [];
for ($index = 1; $index <= 51; $index++) {
$rows[] = [
'2026-07',
'苏木',
'测试中通',
sprintf('SF%018d', $index),
sprintf('ORDER%015d', $index),
];
}
$this->storeSnapshot('return-fee', ['日期', '负责人', '快递名称', '运单号码', '交易订单'], $rows);
$pageRoute = route('business.pages.show', [
'company_id' => $this->company->id,
'section' => 'courier-payables',
'page' => 'return-fee',
]);
$filters = ['courier' => '中通', 'keyword' => 'ORDER'];
$this->loginAs()->get($pageRoute . '?' . http_build_query($filters))
->assertOk()
->assertSeeText('第 1 / 2 页')
->assertSee('courier=' . rawurlencode('中通') . '&amp;keyword=ORDER&amp;page=2', false);
$lastWaybill = sprintf('SF%018d', 51);
$this->loginAs()->get($pageRoute . '?' . http_build_query([...$filters, 'page' => 2]))
->assertOk()
->assertSeeText($lastWaybill)
->assertSeeText('第 2 / 2 页');
$this->loginAs()->get($pageRoute . '?keyword=' . rawurlencode('完全不存在的数据'))
->assertOk()
->assertSeeText('当前查询条件下没有匹配数据。')
->assertDontSeeText('暂无数据,请先点击“导入”上传汇总表。')
->assertSeeText('查询到 0 条 / 共 51 条');
}
public function test_each_summary_page_exports_only_filtered_rows_and_preserves_year_month_display(): void
{
$pages = [
'freight-difference' => ['结算月份', '快递公司'],
'other-surcharge-summary' => ['日期', '快递公司'],
'return-fee' => ['日期', '快递名称'],
];
foreach ($pages as $page => [$dateHeader, $courierHeader]) {
$this->storeSnapshot($page, [
$dateHeader,
'负责人',
$courierHeader,
'运单号码',
'备注',
], [
['2026-07', '苏木', '测试中通', 'SF000000000000000001', '需要导出'],
['2026-08', '苏木', '测试中通', 'SF000000000000000002', '其他月份'],
['2026-07', '周生辰', '测试圆通', 'SF000000000000000003', '其他负责人'],
]);
$filters = [
'date' => '2026-07',
'person' => '苏木',
'courier' => '中通',
'keyword' => '需要导出',
];
$exportUrl = route('business.courier-payable-summaries.export', [
'company_id' => $this->company->id,
'summaryPage' => $page,
...$filters,
]);
$this->loginAs()->get(route('business.pages.show', [
'company_id' => $this->company->id,
'section' => 'courier-payables',
'page' => $page,
...$filters,
]))
->assertOk()
->assertSee(htmlspecialchars($exportUrl, ENT_QUOTES, 'UTF-8'), false);
$response = $this->loginAs()->get($exportUrl);
$content = $response->streamedContent();
$lines = preg_split('/\r\n|\r|\n/', trim(substr($content, 3)));
$response->assertOk()->assertHeader('content-type', 'text/csv; charset=UTF-8');
$this->assertCount(2, $lines);
$this->assertSame('="2026-07"', str_getcsv($lines[1])[0]);
$this->assertStringContainsString('SF000000000000000001', $content);
$this->assertStringNotContainsString('SF000000000000000002', $content);
$this->assertStringNotContainsString('SF000000000000000003', $content);
}
}
public function test_freight_difference_import_dialog_downloads_the_supplied_xlsx_template(): void
{
$pageResponse = $this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'freight-difference',
]));
$pageResponse
->assertOk()
->assertSee('data-courier-summary-import-dialog', false)
->assertSeeText('下载模板')
->assertSee(route('business.courier-payable-summaries.template', [
'summaryPage' => 'freight-difference',
]), false);
$templateResponse = $this->loginAs()->get(route('business.courier-payable-summaries.template', [
'summaryPage' => 'freight-difference',
]));
$templateResponse
->assertOk()
->assertDownload('freight_difference_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/freight-difference-import-template.xlsx'));
$worksheet = $spreadsheet->getActiveSheet();
$this->assertSame([
'日期',
'负责人',
'快递公司',
'快递单号',
'店铺',
'id',
'条码',
'交易单号',
'差异(结算运费-提供成本运费)',
'是否配件',
'配件成品条码',
], $worksheet->rangeToArray('A1:K1', null, false, false)[0]);
$this->assertSame('FFFFC000', $worksheet->getStyle('E1')->getFill()->getStartColor()->getARGB());
$this->assertSame('FFFFC000', $worksheet->getStyle('F1')->getFill()->getStartColor()->getARGB());
$this->assertSame('FFFFC000', $worksheet->getStyle('H1')->getFill()->getStartColor()->getARGB());
$this->assertSame('@', $worksheet->getStyle('A2')->getNumberFormat()->getFormatCode());
$this->assertSame('@', $worksheet->getStyle('F2')->getNumberFormat()->getFormatCode());
$this->assertSame('@', $worksheet->getStyle('H2')->getNumberFormat()->getFormatCode());
$this->assertSame('', (string) $worksheet->getCell('A2')->getValue());
$spreadsheet->disconnectWorksheets();
}
public function test_other_surcharge_import_dialog_downloads_the_supplied_xlsx_template(): void
{
$pageResponse = $this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'other-surcharge-summary',
]));
$pageResponse
->assertOk()
->assertSee('data-courier-summary-import-dialog', false)
->assertSeeText('导入其他附加费汇总')
->assertSeeText('其他附加费导入模板')
->assertSeeText('.xlsx · 11 个字段')
->assertSeeText('下载模板')
->assertSee(route('business.courier-payable-summaries.template', [
'summaryPage' => 'other-surcharge-summary',
]), false);
$templateResponse = $this->loginAs()->get(route('business.courier-payable-summaries.template', [
'summaryPage' => 'other-surcharge-summary',
]));
$templateResponse
->assertOk()
->assertDownload('other_surcharge_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/other-surcharge-import-template.xlsx'));
$worksheet = $spreadsheet->getActiveSheet();
$this->assertSame([
'日期',
'负责人',
'快递公司',
'快递单号',
'商品ID',
'条码',
'店铺',
'交易单号',
'金额',
'备注',
'成品条码',
], $worksheet->rangeToArray('A1:K1', null, false, false)[0]);
$this->assertSame('@', $worksheet->getStyle('A2')->getNumberFormat()->getFormatCode());
$this->assertSame('@', $worksheet->getStyle('D2')->getNumberFormat()->getFormatCode());
$this->assertSame('@', $worksheet->getStyle('E2')->getNumberFormat()->getFormatCode());
$this->assertSame('@', $worksheet->getStyle('F2')->getNumberFormat()->getFormatCode());
$this->assertSame('@', $worksheet->getStyle('H2')->getNumberFormat()->getFormatCode());
$this->assertSame('0.00########', $worksheet->getStyle('I2')->getNumberFormat()->getFormatCode());
$this->assertSame('@', $worksheet->getStyle('K2')->getNumberFormat()->getFormatCode());
$this->assertSame('', (string) $worksheet->getCell('A2')->getValue());
$this->assertSame('', (string) $worksheet->getCell('K18')->getValue());
$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'));
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray([
'2026-07',
'苏木',
'测试快递',
'YT7632543323146',
'测试店铺',
'855226246304',
'DNZ0117123N1210V01',
'3312228147193110069',
0.1827,
'成品',
'',
], null, 'A2');
$worksheet->getCell('F2')->setValueExplicit('855226246304', DataType::TYPE_STRING);
$worksheet->getCell('H2')->setValueExplicit('3312228147193110069', DataType::TYPE_STRING);
$path = tempnam(sys_get_temp_dir(), 'freight-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' => 'freight-difference',
]), ['import' => $file])
->assertRedirect(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'freight-difference',
]))
->assertSessionHas('success', '导入完成:1 条数据,11 个字段。');
$snapshot = app(CourierPayableSummaryStore::class)->snapshot(
(int) $this->company->id,
'freight-difference',
);
$this->assertSame('2026-07', $snapshot['rows'][0][0]);
$this->assertSame('855226246304', $snapshot['rows'][0][5]);
$this->assertSame('3312228147193110069', $snapshot['rows'][0][7]);
$this->assertSame('0.1827', $snapshot['rows'][0][8]);
} 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_completed_other_surcharge_template_preserves_long_identifiers_on_import(): void
{
$spreadsheet = IOFactory::load(resource_path('templates/courier-payables/other-surcharge-import-template.xlsx'));
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray([
'2026-07',
'苏木',
'测试快递',
'SF5199125637478',
'6041543429417',
'SJ038017N1410V01',
'测试店铺',
'5110751415866048404',
-60,
'安装费',
'CP001',
], null, 'A2');
$worksheet->getCell('E2')->setValueExplicit('6041543429417', DataType::TYPE_STRING);
$worksheet->getCell('H2')->setValueExplicit('5110751415866048404', DataType::TYPE_STRING);
$path = tempnam(sys_get_temp_dir(), 'other-surcharge-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' => 'other-surcharge-summary',
]), ['import' => $file])
->assertRedirect(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'other-surcharge-summary',
]))
->assertSessionHas('success', '导入完成:1 条数据,11 个字段。');
$snapshot = app(CourierPayableSummaryStore::class)->snapshot(
(int) $this->company->id,
'other-surcharge-summary',
);
$this->assertSame('2026-07', $snapshot['rows'][0][0]);
$this->assertSame('6041543429417', $snapshot['rows'][0][4]);
$this->assertSame('5110751415866048404', $snapshot['rows'][0][7]);
$this->assertSame('-60', $snapshot['rows'][0][8]);
$this->assertSame('安装费', $snapshot['rows'][0][9]);
} 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_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(
'运费差.csv',
"快递公司,结算月份,运费差,备注\n测试快递,2026-08,-18.50,复核\n第二快递,2026-08,20,=待确认\n",
);
$this->loginAs()->post(route('business.courier-payable-summaries.import', [
'summaryPage' => 'freight-difference',
]), ['import' => $file])
->assertRedirect(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'freight-difference',
]))
->assertSessionHas('success', '导入完成:2 条数据,4 个字段。');
Storage::disk('uploads')->assertExists(
app(CourierPayableSummaryStore::class)->path((int) $this->company->id, 'freight-difference')
);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'freight-difference',
]))
->assertOk()
->assertSeeText('导入完成:2 条数据,4 个字段。')
->assertSeeText('测试快递')
->assertSeeText('-18.50')
->assertSeeText('第二快递');
$response = $this->loginAs()->get(route('business.courier-payable-summaries.export', [
'summaryPage' => 'freight-difference',
]));
$content = $response->streamedContent();
$response->assertOk()->assertHeader('content-type', 'text/csv; charset=UTF-8');
$this->assertStringContainsString('快递公司,结算月份,运费差,备注', $content);
$this->assertStringContainsString('测试快递,"=""2026-08""",-18.50,复核', $content);
$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', 'return-fee'] as $page) {
$file = UploadedFile::fake()->createWithContent($page . '.csv', implode('', $rows));
$this->loginAs()->post(route('business.courier-payable-summaries.import', [
'summaryPage' => $page,
]), ['import' => $file])
->assertRedirect(route('business.pages.show', [
'section' => 'courier-payables',
'page' => $page,
]))
->assertSessionHas('success', '导入完成:10001 条数据,2 个字段。')
->assertSessionDoesntHaveErrors('import');
$snapshot = $store->snapshot((int) $this->company->id, $page);
$this->assertCount(10001, $snapshot['rows']);
}
}
public function test_summary_actions_require_the_matching_page_permission(): void
{
$this->withExceptionHandling();
$this->user->roles()->sync([
\App\Models\Auth\Role::where('name', 'admin')->firstOrFail()->id,
]);
setting()->set('system.sidebar_permissions', [
'configured' => ['user_' . $this->user->id],
'subjects' => ['user_' . $this->user->id => ['courier-payables/courier-information']],
]);
$this->loginAs()->get(route('business.courier-payable-summaries.export', [
'summaryPage' => 'freight-difference',
]))->assertForbidden();
$this->loginAs()->get(route('business.courier-payable-summaries.template', [
'summaryPage' => 'freight-difference',
]))->assertForbidden();
$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();
}
/** @param list<string> $headers @param list<list<string>> $rows */
private function storeSnapshot(string $page, array $headers, array $rows): void
{
$store = app(CourierPayableSummaryStore::class);
Storage::disk('uploads')->put(
$store->path((int) $this->company->id, $page),
json_encode([
'version' => 1,
'headers' => $headers,
'rows' => $rows,
'source_name' => $page . '.xlsx',
'imported_at' => now()->toIso8601String(),
'imported_by' => $this->user->id,
], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
);
}
}