CaiWuXiTong/tests/Feature/Console/InternalFlowReportImportTest.php
WuYongJie 8419d4097c feat: 内部流转报表导入工作簿数据并支持查询导出
- 新增共享镜像表 business_internal_flow_records 与整份替换导入命令 internal-flow:import
- 按 Excel 表头展示抬头、标签、预计9月销量、开票、所需进项成本、收票、剩余缺口与开票比例
- 复用公司基础信息的字段查询、表头排序、筛选字段与导出 Excel 交互
- 新增导入与页面测试,覆盖合计行跳过、重导入替换、筛选排序与未导入空状态
2026-10-06 22:08:08 +08:00

186 lines
7.6 KiB
PHP

<?php
namespace Tests\Feature\Console;
use Illuminate\Support\Facades\DB;
use Modules\EcommerceFinance\Repositories\InternalFlowReportRepository;
use Modules\EcommerceFinance\Services\InternalFlowWorkbookImporter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Tests\TestCase;
class InternalFlowReportImportTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
config([
'database.connections.business_mirror' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
'upstream_mirrors.connection' => 'business_mirror',
]);
DB::purge('business_mirror');
$migration = require base_path(
'database/migrations/2026_10_05_000000_create_business_internal_flow_records_table.php'
);
$migration->up();
}
public function testImportKeepsWorkbookValuesAndSkipsTotalRows(): void
{
$path = $this->writeWorkbook($this->dataRows());
try {
$result = app(InternalFlowWorkbookImporter::class)->import($path);
} finally {
@unlink($path);
}
$this->assertSame(3, $result['imported']);
$this->assertSame(1, $result['skipped']);
$rows = DB::connection('business_mirror')
->table(InternalFlowWorkbookImporter::TABLE)
->orderBy('sort_order')
->get();
$this->assertCount(3, $rows);
$this->assertSame('示例甲供应链有限公司', $rows[0]->company_name);
$this->assertSame('1', (string) $rows[0]->tag);
$this->assertSame('1', $rows[0]->invoicing_ratio);
$this->assertEqualsWithDelta(1200000.0, (float) $rows[0]->projected_sales, 0.000001);
$this->assertSame(1, (int) $rows[0]->sort_order);
$this->assertSame('示例乙电子商务有限公司', $rows[1]->company_name);
$this->assertEqualsWithDelta(512345.6789, (float) $rows[1]->internal_invoiced, 0.000001);
$this->assertEqualsWithDelta(1833433.2211, (float) $rows[1]->required_input_cost, 0.000001);
$this->assertEqualsWithDelta(300000.5, (float) $rows[1]->internal_transfer_1, 0.000001);
$this->assertEqualsWithDelta(1200000.5, (float) $rows[1]->receipt_total, 0.000001);
$this->assertSame('0.42', $rows[1]->invoicing_ratio);
$this->assertSame('示例丙家居有限公司', $rows[2]->company_name);
$this->assertNull($rows[2]->invoiced_to_buyer);
$this->assertEqualsWithDelta(220000.11, (float) $rows[2]->receipt_total, 0.000001);
$this->assertSame('/', $rows[2]->invoicing_ratio);
$this->assertSame(basename($path), $rows[0]->source_file);
$this->assertSame(
0,
DB::connection('business_mirror')
->table(InternalFlowWorkbookImporter::TABLE)
->where('company_name', '合计')
->count(),
);
}
public function testImportReplacesPreviousDatasetAndCommandReportsCounts(): void
{
$first = $this->writeWorkbook($this->dataRows());
app(InternalFlowWorkbookImporter::class)->import($first);
@unlink($first);
$second = $this->writeWorkbook([
['示例乙电子商务有限公司', 2, 2345678.9, 456789.12, 512345.6789, 1833433.2211, 900000, null, null, 300000.5, 0, 0, 1200000.5, 633432.7211, 0.42],
]);
try {
$this->artisan('internal-flow:import', ['path' => $second])
->expectsOutputToContain('明细 1 行')
->assertSuccessful();
} finally {
@unlink($second);
}
$rows = DB::connection('business_mirror')
->table(InternalFlowWorkbookImporter::TABLE)
->get();
$this->assertCount(1, $rows);
$this->assertSame('示例乙电子商务有限公司', $rows[0]->company_name);
}
public function testRepositoryFiltersSortsAndPaginates(): void
{
$path = $this->writeWorkbook($this->dataRows());
app(InternalFlowWorkbookImporter::class)->import($path);
@unlink($path);
$repository = app(InternalFlowReportRepository::class);
$this->assertTrue($repository->ready());
$all = $repository->paginate(1, 50);
$this->assertSame(3, $all['total']);
$this->assertSame('示例甲供应链有限公司', $all['data'][0]['company_name']);
$filtered = $repository->paginate(1, 50, ['company_name' => '示例乙']);
$this->assertSame(1, $filtered['total']);
$this->assertSame('示例乙电子商务有限公司', $filtered['data'][0]['company_name']);
$tagged = $repository->paginate(1, 50, ['tag' => '4']);
$this->assertSame(1, $tagged['total']);
$this->assertSame('示例丙家居有限公司', $tagged['data'][0]['company_name']);
$sorted = $repository->paginate(1, 50, [], 'projected_sales', 'desc');
$this->assertSame('示例乙电子商务有限公司', $sorted['data'][0]['company_name']);
$unknownSort = $repository->paginate(1, 50, [], 'company_name; drop table users', 'desc');
$this->assertSame(3, $unknownSort['total']);
$this->assertSame('示例甲供应链有限公司', $unknownSort['data'][0]['company_name']);
$paged = $repository->paginate(2, 2);
$this->assertSame(3, $paged['total']);
$this->assertCount(1, $paged['data']);
$this->assertSame('示例丙家居有限公司', $paged['data'][0]['company_name']);
}
/**
* @return list<list<mixed>>
*/
private function dataRows(): array
{
return [
['示例甲供应链有限公司', 1, 1200000, 0, 1200000, 0, null, null, null, 0, 0, 0, 0, 0, 1],
['示例乙电子商务有限公司', 2, 2345678.9, 456789.12, 512345.6789, 1833433.2211, 900000, null, null, 300000.5, 0, 0, 1200000.5, 633432.7211, 0.42],
['示例丙家居有限公司', 4, 123456.78, null, null, 987654.32, null, null, null, 0, 0, 220000.11, 220000.11, 767654.21, '/'],
['合计', null, 3679135.68, 456789.12, 1712345.6789, 2821087.5411, 900000, 0, null, 300000.5, 0, 0, 1420000.61, 1401086.9311, '/'],
[null, null, null, null, null, null, null, null, null, null, null, null, null, null, null],
];
}
/**
* @param list<list<mixed>> $dataRows
*/
private function writeWorkbook(array $dataRows): string
{
$temporary = tempnam(sys_get_temp_dir(), 'internal-flow-');
$path = dirname($temporary) . DIRECTORY_SEPARATOR . 'internal-flow-' . basename($temporary) . '.xlsx';
rename($temporary, $path);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle(InternalFlowWorkbookImporter::SHEET_TITLE);
$sheet->fromArray([
['抬头', '标签', '预计9月销量', '开票', null, '所需进项成本', '收票', null, null, null, null, null, null, '剩余缺口', '开票比例'],
[null, null, null, null, null, null, '供应商', '索菲亚', '顾家', '内部流转(来源)', null, null, '合计', null, null],
[null, null, null, '给买家开票', '内部开票', null, null, null, null, '1', '2', '3', null, null, null],
...$dataRows,
], null, 'A1');
foreach (['A1:A3', 'B1:B3', 'C1:C3', 'D1:E2', 'F1:F3', 'G1:M1', 'H2:H3', 'I2:I3', 'J2:L2', 'M2:M3', 'N1:N3', 'O1:O3'] as $range) {
$sheet->mergeCells($range);
}
(new Xlsx($spreadsheet))->save($path);
$spreadsheet->disconnectWorksheets();
return $path;
}
}