122 lines
4.9 KiB
PHP
122 lines
4.9 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature;
|
||
|
||
use App\Exports\PurchaseOrderExport;
|
||
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\UserModel;
|
||
use Maatwebsite\Excel\Facades\Excel;
|
||
|
||
/**
|
||
* 采购单导出:xlsx Content-Type / 蔬果分类过滤 / PDF / format 非法拒绝 / 权限拦截 /
|
||
* 中文文件名 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);
|
||
|
||
$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?type=all&format=xlsx");
|
||
$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);
|
||
}
|
||
|
||
/** 蔬果分类过滤:type=category 仅导出蔬菜/水果顶级分类商品 */
|
||
public function test_export_category_filters_to_vegetables(): void
|
||
{
|
||
$purchase = $this->buildPurchaseWithItems();
|
||
$this->actingAsSysUser();
|
||
|
||
Excel::fake();
|
||
$this->get("/purchase/order/{$purchase->id}/export?type=category&format=xlsx")->assertOk();
|
||
|
||
Excel::assertDownloaded(
|
||
$purchase->purchase_no . '_采购单.xlsx',
|
||
static function (PurchaseOrderExport $export): bool {
|
||
$items = $export->collection();
|
||
// 仅蔬菜商品一行,肉禽被过滤
|
||
return $items->count() === 1;
|
||
}
|
||
);
|
||
}
|
||
|
||
/** PDF 导出:application/pdf */
|
||
public function test_export_pdf(): void
|
||
{
|
||
$purchase = $this->buildPurchaseWithItems();
|
||
$this->actingAsSysUser();
|
||
|
||
$response = $this->get("/purchase/order/{$purchase->id}/export?format=pdf");
|
||
$response->assertOk();
|
||
$this->assertStringContainsString('application/pdf', (string) $response->headers->get('Content-Type'));
|
||
$this->assertStringStartsWith('%PDF', (string) $response->getContent());
|
||
}
|
||
|
||
/** format 参数非法 → 拒绝 */
|
||
public function test_export_invalid_format_rejected(): void
|
||
{
|
||
$purchase = $this->buildPurchaseWithItems();
|
||
$this->actingAsSysUser();
|
||
|
||
$this->get("/purchase/order/{$purchase->id}/export?format=doc")
|
||
->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?format=xlsx");
|
||
$this->assertFalse($response->json('success'), '缺少权限点应被拦截');
|
||
}
|
||
}
|