426 lines
20 KiB
PHP
426 lines
20 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Business;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Modules\EcommerceFinance\Services\PayablesFinalImporter;
|
|
use Modules\EcommerceFinance\Services\PayablesFinalRegistry;
|
|
use Modules\EcommerceFinance\Services\SupplierPayableOverrideStore;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class SupplierPayableFinalPagesTest extends FeatureTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->user->attachPermission(
|
|
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
|
);
|
|
}
|
|
|
|
public function test_final_table_page_keeps_the_maintenance_toolbar_and_starts_empty(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'board-deductions',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('导入')
|
|
->assertSeeText('导出')
|
|
->assertSeeText('查询')
|
|
->assertSeeText('重置')
|
|
->assertSeeText('编辑')
|
|
->assertSeeText('下载导入模板')
|
|
->assertSeeText('月份')
|
|
->assertSeeText('入库日期')
|
|
->assertSeeText('申领单号')
|
|
->assertSeeText('暂无数据,请点击“导入”上传本表数据。')
|
|
->assertSee('workspace/supplier-payables-final/actions/board-deductions/import', false)
|
|
->assertSee('workspace/supplier-payables-final/actions/board-deductions/template', false)
|
|
// 导入/编辑弹窗的行内脚本必须随页面输出,局部替换导航才不会出现“点了没反应”。
|
|
->assertSee('data-supplier-import-modal', false)
|
|
->assertSee('data-supplier-import-choose', false)
|
|
->assertSee('modal.dataset.ready', false)
|
|
->assertSee('data-supplier-maintenance-modal', false)
|
|
// 页签是局部替换导航,弹窗脚本要带 replay 标记,替换后才会重新执行并绑定。
|
|
->assertSee('data-caiwu-replay', false)
|
|
->assertDontSee('workspace/supplier-payables/actions/board-deductions/import', false);
|
|
}
|
|
|
|
public function test_final_template_downloads_the_same_columns(): void
|
|
{
|
|
$response = $this->loginAs()
|
|
->get(route('business.supplier-payables-final.board-deductions.template'))
|
|
->assertOk()
|
|
->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
|
|
$path = tempnam(sys_get_temp_dir(), 'caiwu-template') . '.xlsx';
|
|
|
|
try {
|
|
file_put_contents($path, $response->streamedContent());
|
|
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($path);
|
|
$header = $spreadsheet->getSheet(0)->rangeToArray('A1:C1')[0];
|
|
$notes = $spreadsheet->getSheet(1)->rangeToArray('A2:B2')[0];
|
|
$spreadsheet->disconnectWorksheets();
|
|
|
|
$this->assertSame(['记录标识', '月份', '入库日期'], $header);
|
|
$this->assertSame('记录标识', $notes[0]);
|
|
$this->assertStringContainsString('选填', (string) $notes[1]);
|
|
} finally {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
|
|
public function test_final_payment_reconciliation_template_keeps_the_extra_columns_in_order(): void
|
|
{
|
|
$response = $this->loginAs()
|
|
->get(route('business.supplier-payables-final.payment-reconciliation.template'))
|
|
->assertOk()
|
|
->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
|
|
$path = tempnam(sys_get_temp_dir(), 'caiwu-template') . '.xlsx';
|
|
|
|
try {
|
|
file_put_contents($path, $response->streamedContent());
|
|
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($path);
|
|
$header = $spreadsheet->getSheet(0)->rangeToArray('A1:U1')[0];
|
|
$spreadsheet->disconnectWorksheets();
|
|
|
|
$this->assertSame([
|
|
'记录标识', '月份', '入库单号', '采购单号', '日期', '供应商', '货号', '商品编码', '规格名称', '数量',
|
|
'单价', '金额', '预估单价', '预估金额', '供应商商品编码', '备注', '仓库名称', '快递名称', '快递单号',
|
|
'未发出', '对账人',
|
|
], $header);
|
|
} finally {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
|
|
public function test_final_payment_reconciliation_import_keeps_the_extra_columns(): void
|
|
{
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'payment-reconciliation.csv',
|
|
"\xEF\xBB\xBF月份,入库单号,采购单号,日期,供应商,货号,商品编码,规格名称,数量,单价,金额,预估单价,预估金额,供应商商品编码,备注,仓库名称,快递名称,快递单号,未发出,对账人\n"
|
|
. "2026-09,RK-1,CG-1,2026-09-08,终版供应商,X-1,P-1,规格甲,2,10,20,9,18,SC-1,备注甲,华东仓,顺丰,SF-1,未发出,对账人甲\n",
|
|
);
|
|
|
|
$this->loginAs()
|
|
->post(route('business.supplier-payables-final.payment-reconciliation.import'), ['import' => $file])
|
|
->assertStatus(302)
|
|
->assertSessionHas('success', '导入完成:新增 1 条,更新 0 条。');
|
|
|
|
$this->assertDatabaseHas('supplier_payable_overrides', [
|
|
'company_id' => $this->company->id,
|
|
'dataset' => 'final:supplier:payment-reconciliation',
|
|
]);
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'payment-reconciliation',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('入库单号')
|
|
->assertSeeText('采购单号')
|
|
->assertSeeText('日期')
|
|
->assertSeeText('对账人')
|
|
->assertSeeText('RK-1')
|
|
->assertSeeText('CG-1')
|
|
->assertSeeText('2026-09-08')
|
|
->assertSeeText('未发出')
|
|
->assertSeeText('对账人甲')
|
|
->assertSeeText('SF-1');
|
|
|
|
$export = $this->loginAs()->get(route('business.supplier-payables-final.payment-reconciliation.export'));
|
|
$export->assertOk();
|
|
$this->assertStringContainsString('采购单号,日期,供应商', $export->streamedContent());
|
|
$this->assertStringContainsString('快递单号,未发出,对账人', $export->streamedContent());
|
|
}
|
|
|
|
public function test_final_return_reconciliation_template_adds_wms_and_creator_columns(): void
|
|
{
|
|
$response = $this->loginAs()
|
|
->get(route('business.supplier-payables-final.return-reconciliation.template'))
|
|
->assertOk()
|
|
->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
|
|
$path = tempnam(sys_get_temp_dir(), 'caiwu-template') . '.xlsx';
|
|
|
|
try {
|
|
file_put_contents($path, $response->streamedContent());
|
|
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($path);
|
|
$header = $spreadsheet->getSheet(0)->rangeToArray('A1:T1')[0];
|
|
$spreadsheet->disconnectWorksheets();
|
|
|
|
$this->assertSame([
|
|
'记录标识', '月份', '发货时间', '明细供应商', '条形码', '商品名称', '发货量', '单价', '金额', '预估单价',
|
|
'预估金额', '供应商条码', '备注', '标题', 'wms配货单号', '仓库名称', '快递单号', '标记多标签', '创建人', '对账人',
|
|
], $header);
|
|
$this->assertNotContains('快递名称', $header);
|
|
} finally {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
|
|
public function test_final_return_reconciliation_import_keeps_wms_and_creator_columns(): void
|
|
{
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'return-reconciliation.csv',
|
|
"\xEF\xBB\xBF月份,发货时间,明细供应商,条形码,商品名称,发货量,单价,金额,预估单价,预估金额,供应商条码,备注,标题,wms配货单号,仓库名称,快递单号,标记多标签,创建人,对账人\n"
|
|
. "2026-09,2026-09-01,退货供应商,BC-1,商品甲,3,10,30,9,27,SB-1,备注甲,标题甲,WMS-1,华南仓,SF-9,多标签,创建人甲,对账人乙\n",
|
|
);
|
|
|
|
$this->loginAs()
|
|
->post(route('business.supplier-payables-final.return-reconciliation.import'), ['import' => $file])
|
|
->assertStatus(302)
|
|
->assertSessionHas('success', '导入完成:新增 1 条,更新 0 条。');
|
|
|
|
$this->assertDatabaseHas('supplier_payable_overrides', [
|
|
'company_id' => $this->company->id,
|
|
'dataset' => 'final:supplier:return-reconciliation',
|
|
]);
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'return-reconciliation',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('wms配货单号')
|
|
->assertSeeText('创建人')
|
|
->assertSeeText('WMS-1')
|
|
->assertSeeText('创建人甲')
|
|
->assertDontSeeText('快递名称');
|
|
|
|
$export = $this->loginAs()->get(route('business.supplier-payables-final.return-reconciliation.export'));
|
|
$export->assertOk();
|
|
$this->assertStringContainsString('备注,标题,wms配货单号', $export->streamedContent());
|
|
$this->assertStringContainsString('标记多标签,创建人,对账人', $export->streamedContent());
|
|
$this->assertStringNotContainsString('快递名称', $export->streamedContent());
|
|
}
|
|
|
|
public function test_final_return_reconciliation_page_paginates_fifteen_rows(): void
|
|
{
|
|
$records = [];
|
|
for ($index = 1; $index <= 20; $index++) {
|
|
$records[sprintf('return:%02d', $index)] = [
|
|
'summary_month' => '2026-09',
|
|
'send_time' => '2026-09-30',
|
|
'supplier' => '退货供应商',
|
|
'barcode' => sprintf('RC-%02d', $index),
|
|
'sku_name' => '退货商品',
|
|
'qty' => '1',
|
|
'unit_price' => '10',
|
|
'amount' => '10',
|
|
];
|
|
}
|
|
app(SupplierPayableOverrideStore::class)->saveBatch(
|
|
'final:supplier:return-reconciliation',
|
|
$records,
|
|
(int) $this->company->id,
|
|
(int) $this->user->id,
|
|
);
|
|
|
|
$baseUrl = route('business.pages.show', [
|
|
'company_id' => (int) $this->company->id,
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'return-reconciliation',
|
|
]);
|
|
$url = fn (int $page): string => $baseUrl . '?page=' . $page;
|
|
|
|
$this->loginAs()->get($url(1))
|
|
->assertOk()
|
|
->assertSeeText('每页 15 条')
|
|
->assertSeeText('第 1 页 / 共 2 页')
|
|
->assertSeeText('RC-01')
|
|
->assertDontSeeText('RC-16');
|
|
|
|
$this->loginAs()->get($url(2))
|
|
->assertOk()
|
|
->assertSeeText('第 2 页 / 共 2 页')
|
|
->assertSeeText('RC-16')
|
|
->assertSeeText('RC-20')
|
|
->assertDontSeeText('RC-01');
|
|
}
|
|
|
|
public function test_final_import_reads_files_larger_than_one_chunk(): void
|
|
{
|
|
$lines = ['月份,入库单号,采购单号,日期,供应商,货号,商品编码,规格名称,数量,单价,金额,预估单价,预估金额,供应商商品编码,备注,仓库名称,快递名称,快递单号,未发出,对账人'];
|
|
for ($i = 1; $i <= 4500; $i++) {
|
|
$lines[] = "2026-09,RK-{$i},CG-{$i},2026-09-08,供应商{$i},X-{$i},P-{$i},规格甲,2,10,20,9,18,SC-1,备注甲,华东仓,顺丰,SF-{$i},未发出,对账人甲";
|
|
}
|
|
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'payment-reconciliation.csv',
|
|
"\xEF\xBB\xBF" . implode("\n", $lines) . "\n",
|
|
);
|
|
|
|
$result = app(PayablesFinalImporter::class)->import(
|
|
app(PayablesFinalRegistry::class)->fields('supplier-payables-final', 'payment-reconciliation'),
|
|
'final:supplier:payment-reconciliation',
|
|
$file,
|
|
(int) $this->company->id,
|
|
(int) $this->user->id,
|
|
);
|
|
|
|
$this->assertSame(['created' => 4500, 'updated' => 0, 'rows' => 4500], $result);
|
|
}
|
|
|
|
public function test_final_import_stops_at_the_row_limit_without_writing(): void
|
|
{
|
|
$lines = ['月份,入库单号,采购单号,日期,供应商,货号,商品编码,规格名称,数量,单价,金额,预估单价,预估金额,供应商商品编码,备注,仓库名称,快递名称,快递单号,未发出,对账人'];
|
|
for ($i = 1; $i <= 10050; $i++) {
|
|
$lines[] = "2026-09,RK-{$i},CG-{$i},2026-09-08,供应商{$i},X-{$i},P-{$i},规格甲,2,10,20,9,18,SC-1,备注甲,华东仓,顺丰,SF-{$i},未发出,对账人甲";
|
|
}
|
|
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'payment-reconciliation.csv',
|
|
"\xEF\xBB\xBF" . implode("\n", $lines) . "\n",
|
|
);
|
|
|
|
try {
|
|
app(PayablesFinalImporter::class)->import(
|
|
app(PayablesFinalRegistry::class)->fields('supplier-payables-final', 'payment-reconciliation'),
|
|
'final:supplier:payment-reconciliation',
|
|
$file,
|
|
(int) $this->company->id,
|
|
(int) $this->user->id,
|
|
);
|
|
$this->fail('超过 1 万行应当拒绝导入。');
|
|
} catch (ValidationException $exception) {
|
|
$this->assertSame(
|
|
['单次最多导入 10,000 条记录,请拆分文件后重试。'],
|
|
$exception->errors()['import'],
|
|
);
|
|
}
|
|
|
|
$this->assertSame(0, (int) DB::connection(config('database.supplier_payable_maintenance_connection'))
|
|
->table('supplier_payable_overrides')
|
|
->where('dataset', 'final:supplier:payment-reconciliation')
|
|
->count());
|
|
}
|
|
|
|
public function test_product_price_final_page_reuses_the_price_template_columns(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'product-prices',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('商品编码')
|
|
->assertSeeText('采购价')
|
|
->assertSeeText('京喜限价')
|
|
->assertSeeText('暂无数据,请点击“导入”上传本表数据。')
|
|
->assertSee('workspace/supplier-payables-final/actions/product-prices/template', false);
|
|
|
|
$this->loginAs()
|
|
->get(route('business.supplier-payables-final.product-prices.template'))
|
|
->assertOk()
|
|
->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
}
|
|
|
|
public function test_import_writes_rows_into_the_final_dataset_only(): void
|
|
{
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'board-deductions.csv',
|
|
"\xEF\xBB\xBF月份,入库日期,出库日期,家具厂,压贴厂,类型,规格,原料名称,实收数量,单价,金额,单位,批次日期,出库单号,出库备注,申领单号,单号类型,送货单号\n"
|
|
. "2026-09,2026-09-01,2026-09-02,终版家具厂,终版压贴厂,类型A,规格A,原料A,3,12.5,37.5,张,2026-09-03,OUT-1,备注A,CLM-1,单号,DL-1\n",
|
|
);
|
|
|
|
$this->loginAs()
|
|
->post(route('business.supplier-payables-final.board-deductions.import'), ['import' => $file])
|
|
->assertStatus(302)
|
|
->assertSessionHas('success', '导入完成:新增 1 条,更新 0 条。');
|
|
|
|
$this->assertDatabaseHas('supplier_payable_overrides', [
|
|
'company_id' => $this->company->id,
|
|
'dataset' => 'final:supplier:board-deductions',
|
|
]);
|
|
$this->assertSame(0, DB::connection(config('database.supplier_payable_maintenance_connection'))
|
|
->table('supplier_payable_overrides')
|
|
->where('company_id', $this->company->id)
|
|
->where('dataset', 'board-deductions')
|
|
->count());
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'board-deductions',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('2026-09')
|
|
->assertSeeText('终版家具厂')
|
|
->assertSeeText('CLM-1')
|
|
->assertDontSeeText('暂无数据,请点击“导入”上传本表数据。');
|
|
}
|
|
|
|
public function test_final_export_and_edit_work_on_the_imported_rows(): void
|
|
{
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'board-deductions.csv',
|
|
"入库日期,出库日期,家具厂,压贴厂,类型,规格,原料名称,实收数量,单价,金额,单位,批次日期,出库单号,出库备注,申领单号,单号类型,送货单号\n"
|
|
. "2026-09-11,2026-09-12,导出家具厂,厂二,类型B,规格B,原料B,1,10,10,张,2026-09-13,OUT-2,备注B,CLM-2,单号,DL-2\n",
|
|
);
|
|
|
|
$this->loginAs()->post(route('business.supplier-payables-final.board-deductions.import'), ['import' => $file])
|
|
->assertStatus(302);
|
|
|
|
$connection = DB::connection(config('database.supplier_payable_maintenance_connection'));
|
|
$recordKey = (string) $connection->table('supplier_payable_overrides')
|
|
->where('company_id', $this->company->id)
|
|
->where('dataset', 'final:supplier:board-deductions')
|
|
->value('record_key');
|
|
|
|
$this->assertStringStartsWith('hash:', $recordKey);
|
|
|
|
$export = $this->loginAs()->get(route('business.supplier-payables-final.board-deductions.export'));
|
|
$export->assertOk();
|
|
$this->assertStringContainsString('导出家具厂', $export->streamedContent());
|
|
|
|
$this->loginAs()
|
|
->patch(route('business.supplier-payables-final.board-deductions.update', ['recordKey' => $recordKey]), [
|
|
'values' => ['furniture_factory' => '编辑后的家具厂'],
|
|
])
|
|
->assertStatus(302)
|
|
->assertSessionHas('success', '保存成功。');
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'board-deductions',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('编辑后的家具厂');
|
|
}
|
|
|
|
public function test_final_page_keyword_filter_narrows_the_rows(): void
|
|
{
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'board-deductions.csv',
|
|
"入库日期,出库日期,家具厂,压贴厂,类型,规格,原料名称,实收数量,单价,金额,单位,批次日期,出库单号,出库备注,申领单号,单号类型,送货单号\n"
|
|
. "2026-09-21,2026-09-22,筛选甲厂,厂二,类型C,规格C,原料C,1,1,1,张,2026-09-23,OUT-3,备注C,CLM-3,单号,DL-3\n"
|
|
. "2026-09-21,2026-09-22,筛选乙厂,厂二,类型D,规格D,原料D,1,1,1,张,2026-09-23,OUT-4,备注D,CLM-4,单号,DL-4\n",
|
|
);
|
|
|
|
$this->loginAs()->post(route('business.supplier-payables-final.board-deductions.import'), ['import' => $file])
|
|
->assertStatus(302);
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'supplier-payables-final',
|
|
'page' => 'board-deductions',
|
|
'keyword' => '筛选甲厂',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('筛选甲厂')
|
|
->assertDontSeeText('筛选乙厂')
|
|
->assertSeeText('共 1 条');
|
|
}
|
|
}
|