92 lines
3.7 KiB
PHP
92 lines
3.7 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature;
|
||
|
||
use App\Models\CustomerLevelModel;
|
||
use App\Models\ProductCategoryModel;
|
||
use App\Models\ProductModel;
|
||
use App\Models\ProductPriceModel;
|
||
use App\Models\PurchaseOrderModel;
|
||
use App\Models\StoreModel;
|
||
use App\Models\StoreOrderModel;
|
||
use App\Models\UserModel;
|
||
|
||
/**
|
||
* 采购单导出(仅 Excel 表格,全品类):xlsx Content-Type /
|
||
* 权限拦截 / 中文文件名 RFC 5987 编码
|
||
*/
|
||
class ExportTest extends ProcurementTestCase
|
||
{
|
||
/**
|
||
* 构造含蔬果 + 肉禽两类商品的采购单(下单 → 汇总生成)
|
||
*/
|
||
private function buildPurchaseWithItems(): PurchaseOrderModel
|
||
{
|
||
$vegRoot = ProductCategoryModel::create(['name' => '蔬菜', 'parent_id' => 0, 'sort' => 0, 'status' => 1]);
|
||
$meatRoot = ProductCategoryModel::create(['name' => '肉禽蛋', 'parent_id' => 0, 'sort' => 1, 'status' => 1]);
|
||
|
||
$level = CustomerLevelModel::factory()->create();
|
||
$store = StoreModel::factory()->create(['level_id' => $level->id]);
|
||
$veg = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'category_id' => $vegRoot->id]);
|
||
$meat = ProductModel::factory()->create(['status' => ProductModel::STATUS_ON, 'category_id' => $meatRoot->id]);
|
||
ProductPriceModel::factory()->create(['product_id' => $veg->id, 'level_id' => $level->id, 'price' => '5.00']);
|
||
ProductPriceModel::factory()->create(['product_id' => $meat->id, 'level_id' => $level->id, 'price' => '20.00']);
|
||
|
||
$this->actingAsMiniUser(UserModel::factory()->forStore($store->id)->create());
|
||
$this->postJson('/mini/order', ['items' => [
|
||
['product_id' => $veg->id, 'quantity' => 2],
|
||
['product_id' => $meat->id, 'quantity' => 1],
|
||
]])->assertJsonPath('success', true);
|
||
|
||
// 接单后生成采购单
|
||
StoreOrderModel::query()->update(['status' => StoreOrderModel::STATUS_SUMMARIZED]);
|
||
|
||
$this->actingAsSysUser();
|
||
$this->postJson('/purchase/order/generate', ['purchase_date' => now()->toDateString()])
|
||
->assertJsonPath('success', true);
|
||
|
||
return PurchaseOrderModel::first();
|
||
}
|
||
|
||
/** xlsx 导出:正确 Content-Type + 中文文件名 RFC 5987 编码 */
|
||
public function test_export_xlsx_with_encoded_chinese_filename(): void
|
||
{
|
||
$purchase = $this->buildPurchaseWithItems();
|
||
$this->actingAsSysUser();
|
||
|
||
$response = $this->get("/purchase/order/{$purchase->id}/export");
|
||
$response->assertOk();
|
||
|
||
$this->assertStringContainsString(
|
||
'spreadsheetml',
|
||
(string) $response->headers->get('Content-Type'),
|
||
'xlsx 应返回电子表格 Content-Type'
|
||
);
|
||
|
||
$disposition = (string) $response->headers->get('Content-Disposition');
|
||
// Symfony 输出小写 utf-8''(RFC 5987),大小写不敏感断言
|
||
$this->assertMatchesRegularExpression("/filename\*=utf-8''/i", $disposition, '中文文件名应走 RFC 5987 编码');
|
||
$this->assertStringContainsString(rawurlencode('采购单'), $disposition);
|
||
}
|
||
|
||
/** 采购单不存在 → 拒绝 */
|
||
public function test_export_missing_purchase_rejected(): void
|
||
{
|
||
$this->actingAsSysUser();
|
||
|
||
$this->get('/purchase/order/99999/export')
|
||
->assertJsonPath('success', false);
|
||
}
|
||
|
||
/** 无 purchase.order.export 权限点 → 拦截 */
|
||
public function test_export_requires_export_permission(): void
|
||
{
|
||
$purchase = $this->buildPurchaseWithItems();
|
||
// 仅持有查询权限的用户
|
||
$this->actingAsSysUser(['purchase.order.query']);
|
||
|
||
$response = $this->get("/purchase/order/{$purchase->id}/export");
|
||
$this->assertFalse($response->json('success'), '缺少权限点应被拦截');
|
||
}
|
||
}
|