751 lines
31 KiB
PHP
751 lines
31 KiB
PHP
<?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_workspace_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', '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);
|
||
}
|
||
|
||
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('中通') . '&keyword=ORDER&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),
|
||
);
|
||
}
|
||
}
|