192 lines
7.9 KiB
PHP
192 lines
7.9 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Business;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Modules\EcommerceFinance\Repositories\ProductPriceMirrorRepository;
|
|
use Modules\EcommerceFinance\Services\ProductPriceWorkbookImporter;
|
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class ProductPriceActionsTest extends FeatureTestCase
|
|
{
|
|
protected function grantAdminPermission(): void
|
|
{
|
|
$this->user->attachPermission(
|
|
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
|
);
|
|
}
|
|
|
|
public function test_export_downloads_all_rows_for_current_filters(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
$repository = \Mockery::mock(ProductPriceMirrorRepository::class);
|
|
$repository->shouldReceive('ready')->once()->andReturnTrue();
|
|
$repository->shouldReceive('initialized')->once()->andReturnTrue();
|
|
$repository->shouldReceive('exportRows')
|
|
->once()
|
|
->withArgs(function (array $filters, int $companyId): bool {
|
|
return $companyId === (int) $this->company->id
|
|
&& $filters === [
|
|
'barcode' => 'SKU-001',
|
|
'completed_from' => '2026-08-01',
|
|
'completed_to' => '2026-08-31',
|
|
];
|
|
})
|
|
->andReturn([(object) [
|
|
'upstream_key' => hash('sha256', 'export-row'),
|
|
'business_id' => 'B-001',
|
|
'barcode' => 'SKU-001',
|
|
'spu' => 'SPU-001',
|
|
'product_name' => '导出商品',
|
|
'category' => '家具',
|
|
'production_status' => '销售',
|
|
'minimum_price' => 120,
|
|
'price_limit' => 118,
|
|
'purchase_price' => 80,
|
|
'shipping_fee' => 10,
|
|
'jingxi_limit_price' => 100,
|
|
'approval_status' => '已完成',
|
|
'approval_result' => '已同意',
|
|
'workflow_title' => '价格审批',
|
|
'completed_at' => '2026-08-15 10:00:00',
|
|
'is_overridden' => 1,
|
|
'override_updated_at' => '2026-09-16 09:00:00',
|
|
]]);
|
|
$this->app->instance(ProductPriceMirrorRepository::class, $repository);
|
|
|
|
$response = $this->loginAs()->get(route('business.supplier-payables.product-prices.export', [
|
|
'barcode' => 'SKU-001',
|
|
'completed_from' => '2026-08-01',
|
|
'completed_to' => '2026-08-31',
|
|
]));
|
|
|
|
$response->assertOk()->assertDownload();
|
|
$content = $response->streamedContent();
|
|
$this->assertStringContainsString('记录标识', $content);
|
|
$this->assertStringContainsString('SKU-001', $content);
|
|
$this->assertStringContainsString('导出商品', $content);
|
|
$this->assertStringContainsString('人工维护', $content);
|
|
}
|
|
|
|
public function test_update_saves_manual_override_and_returns_to_current_filter(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
$key = hash('sha256', 'update-row');
|
|
$repository = \Mockery::mock(ProductPriceMirrorRepository::class);
|
|
$repository->shouldReceive('applyOverrides')
|
|
->once()
|
|
->withArgs(function (array $records, int $companyId, ?int $userId, string $source) use ($key): bool {
|
|
return $companyId === (int) $this->company->id
|
|
&& $userId === (int) $this->user->id
|
|
&& $source === 'manual'
|
|
&& $records[$key]['barcode'] === 'SKU-EDIT'
|
|
&& (float) $records[$key]['purchase_price'] === 82.5;
|
|
})
|
|
->andReturn(['created' => 1, 'updated' => 0]);
|
|
$this->app->instance(ProductPriceMirrorRepository::class, $repository);
|
|
|
|
$response = $this->loginAs()->patch(
|
|
route('business.supplier-payables.product-prices.update', ['upstreamKey' => $key])
|
|
. '?barcode=SKU&page=2',
|
|
[
|
|
'barcode' => 'SKU-EDIT',
|
|
'spu' => 'SPU-EDIT',
|
|
'product_name' => '编辑商品',
|
|
'purchase_price' => '82.50',
|
|
],
|
|
);
|
|
|
|
$response
|
|
->assertRedirect(route('business.pages.show', [
|
|
'section' => 'supplier-payables',
|
|
'page' => 'product-prices',
|
|
]) . '?barcode=SKU&page=2')
|
|
->assertSessionHas('success', '商品价格已保存。');
|
|
}
|
|
|
|
public function test_update_rejects_prices_with_more_than_four_decimal_places(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
$key = hash('sha256', 'invalid-update-price');
|
|
$repository = \Mockery::mock(ProductPriceMirrorRepository::class);
|
|
$repository->shouldNotReceive('applyOverrides');
|
|
$this->app->instance(ProductPriceMirrorRepository::class, $repository);
|
|
|
|
try {
|
|
$this->loginAs()->patch(
|
|
route('business.supplier-payables.product-prices.update', ['upstreamKey' => $key]),
|
|
[
|
|
'barcode' => 'SKU-EDIT',
|
|
'purchase_price' => '82.12345',
|
|
],
|
|
);
|
|
$this->fail('超过 4 位小数的价格不应通过编辑。');
|
|
} catch (ValidationException $exception) {
|
|
$this->assertSame(
|
|
'采购价最多支持 4 位小数。',
|
|
$exception->errors()['purchase_price'][0],
|
|
);
|
|
}
|
|
}
|
|
|
|
public function test_import_accepts_workbook_and_reports_created_and_updated_counts(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
$importer = \Mockery::mock(ProductPriceWorkbookImporter::class);
|
|
$importer->shouldReceive('import')
|
|
->once()
|
|
->withArgs(function (UploadedFile $file, int $companyId, ?int $userId): bool {
|
|
return $file->getClientOriginalName() === '商品价格.csv'
|
|
&& $companyId === (int) $this->company->id
|
|
&& $userId === (int) $this->user->id;
|
|
})
|
|
->andReturn(['created' => 2, 'updated' => 3, 'rows' => 5]);
|
|
$this->app->instance(ProductPriceWorkbookImporter::class, $importer);
|
|
$file = UploadedFile::fake()->createWithContent(
|
|
'商品价格.csv',
|
|
"记录标识,商品编码,采购价\n" . hash('sha256', 'import-row') . ",SKU-1,80\n",
|
|
);
|
|
|
|
$response = $this->loginAs()->post(
|
|
route('business.supplier-payables.product-prices.import') . '?barcode=SKU-1',
|
|
['import' => $file],
|
|
);
|
|
|
|
$response
|
|
->assertRedirect(route('business.pages.show', [
|
|
'section' => 'supplier-payables',
|
|
'page' => 'product-prices',
|
|
]) . '?barcode=SKU-1&page=1')
|
|
->assertSessionHas('success', '导入完成:新增维护 2 条,更新维护 3 条。');
|
|
}
|
|
|
|
public function test_template_downloads_an_xlsx_with_product_price_import_headers(): void
|
|
{
|
|
$this->grantAdminPermission();
|
|
|
|
$response = $this->loginAs()->get(route(
|
|
'business.supplier-payables.product-prices.template',
|
|
['company_id' => $this->company->id],
|
|
));
|
|
|
|
$response->assertOk()->assertDownload();
|
|
$path = tempnam(sys_get_temp_dir(), 'product-price-template-');
|
|
file_put_contents($path, $response->streamedContent());
|
|
try {
|
|
$spreadsheet = IOFactory::load($path);
|
|
$headers = $spreadsheet->getSheet(0)->rangeToArray('A1:K1')[0];
|
|
$this->assertSame([
|
|
'记录标识', '商品编码', 'SPU', '商品名称', '类目', '生产状态',
|
|
'最低售价', '计算限价', '采购价', '运费', '京喜限价',
|
|
], $headers);
|
|
$this->assertSame('填写说明', $spreadsheet->getSheet(1)->getTitle());
|
|
$spreadsheet->disconnectWorksheets();
|
|
} finally {
|
|
unlink($path);
|
|
}
|
|
}
|
|
}
|