CaiWuXiTong/tests/Feature/Business/CourierSurchargePageTest.php
zhoushengchen 7894526d07 新增附加费报价批量导入 (#243)
增加模板下载、文件预览校验、重复处理和批量写入,复用公司隔离、报价 Repository 与操作日志。
2026-09-22 17:31:56 +08:00

613 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Feature\Business;
use App\Business\CourierSurchargeQuoteWorkbookImporter;
use App\Business\CourierSurchargeTypeRepository;
use App\Models\Auth\Permission;
use App\Models\Business\CourierSurchargeAuditLog;
use App\Models\Business\CourierSurchargeQuote;
use App\Models\Business\CourierSurchargeType;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Schema;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Tests\Feature\FeatureTestCase;
class CourierSurchargePageTest extends FeatureTestCase
{
/** @var list<string> */
private array $temporaryImportFiles = [];
public function test_courier_surcharge_page_renders_persisted_quote_rules(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->createQuote();
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'courier-surcharge',
]))
->assertOk()
->assertSeeText('附加费报价维护')
->assertSeeText('正式维护数据')
->assertSeeText('报价规则列表')
->assertSeeText('管理附加费类型')
->assertSeeText('类型列表')
->assertSeeText('进仓费')
->assertSeeText('新增报价规则')
->assertSeeText('结算方式')
->assertSeeText('计价标准')
->assertSeeText('生效条件')
->assertSeeText('快递归属')
->assertSeeText('自有快递')
->assertSeeText('代发仓')
->assertSeeText('对账人')
->assertSeeText('张会计')
->assertSeeText('附加费类型(可多选)')
->assertSeeText('列设置')
->assertSeeText('导出当前列')
->assertSeeText('导出全部字段')
->assertSeeText('导入报价')
->assertSeeText('下载 Excel 模板')
->assertSee('data-surcharge-import-modal', false)
->assertSeeText('规则编号与操作列固定显示')
->assertSee('data-column-storage-key="caiwu:table-columns:', false)
->assertSee('data-surcharge-table', false)
->assertSee('data-column-check', false)
->assertSee('data-surcharge-export-current', false)
->assertSee('data-surcharge-export-all', false)
->assertSee('data-column-reset', false)
->assertSee('name="types[]"', false)
->assertSeeInOrder(['data-column-key="courier"', 'data-column-key="scope"', 'data-column-key="reconciler"'], false)
->assertSeeText('曹县德邦')
->assertSeeText('超长费')
->assertSeeText('按尺寸/最长边')
->assertSeeText('8 元/单')
->assertSeeText('已启用')
->assertSeeText('后续结算页面将按启用中的规则计算');
}
public function test_courier_surcharge_type_can_be_created_and_duplicate_is_rejected(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$this->post(route('business.courier-surcharge.types.store'), ['name' => '偏远地区费'])
->assertRedirect();
$this->assertDatabaseHas('business_courier_surcharge_types', [
'company_id' => company_id(),
'name' => '偏远地区费',
'status' => CourierSurchargeType::STATUS_ACTIVE,
]);
$this->withExceptionHandling()
->from(route('business.pages.show', ['section' => 'courier-payables', 'page' => 'courier-surcharge']))
->post(route('business.courier-surcharge.types.store'), ['name' => '偏远地区费'])
->assertRedirect()
->assertSessionHasErrors('name');
}
public function test_inactive_type_is_rejected_for_new_quotes_but_keeps_historical_value(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$type = app(CourierSurchargeTypeRepository::class)->all()->firstWhere('name', '超长费');
$this->assertNotNull($type);
$quote = $this->createQuote();
$this->post(route('business.courier-surcharge.types.status', ['type' => $type->id]), [
'status' => CourierSurchargeType::STATUS_INACTIVE,
])->assertRedirect();
$this->withExceptionHandling()->post(route('business.courier-surcharge.store'), [
'courier' => '停用类型测试快递',
'scope' => '自有快递',
'reconciler' => '张会计',
'types' => ['超长费'],
'mode' => '固定单价',
'standard' => '1 元/单',
'condition' => '测试',
'effective_at' => '2026-09-02',
])->assertSessionHasErrors('types.0');
$this->get(route('business.pages.show', ['section' => 'courier-payables', 'page' => 'courier-surcharge']))
->assertOk()
->assertSeeText($quote->courier)
->assertSeeText('超长费')
->assertSeeText('管理附加费类型');
$page = $this->get(route('business.pages.show', ['section' => 'courier-payables', 'page' => 'courier-surcharge']))
->assertOk();
$this->assertStringNotContainsString('value="超长费"><span>超长费</span>', $page->getContent());
}
public function test_type_status_endpoint_rejects_another_company_type(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$otherType = CourierSurchargeType::withoutGlobalScopes()->create([
'company_id' => company_id() + 999,
'name' => '其他公司类型',
'status' => CourierSurchargeType::STATUS_ACTIVE,
'sort_order' => 99,
]);
$this->loginAs();
$this->withExceptionHandling()->post(route('business.courier-surcharge.types.status', ['type' => $otherType->id]), [
'status' => CourierSurchargeType::STATUS_INACTIVE,
])->assertNotFound();
}
public function test_courier_surcharge_page_filters_persisted_rules(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->createQuote();
CourierSurchargeQuote::query()->create([
'company_id' => company_id(),
'courier' => '常州松宇货运',
'scope' => '代发仓',
'reconciler' => '李会计',
'types' => ['送货费'],
'mode' => '按体积',
'standard' => '体积 ≤ 10 m³:50 元/单',
'condition' => '计费体积不超过 10 m³',
'effective_at' => '2026-09-02',
'status' => CourierSurchargeQuote::STATUS_ACTIVE,
'remark' => null,
]);
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'courier-payables',
'page' => 'courier-surcharge',
'courier' => '常州松宇货运',
'scope' => '代发仓',
'reconciler' => '李会计',
'status' => '已启用',
]))
->assertOk()
->assertSeeText('常州松宇货运')
->assertSeeText('送货费')
->assertSeeText('代发仓')
->assertSeeText('李会计')
->assertSeeText('当前规则')
->assertDontSee('<td>曹县德邦</td>', false)
->assertDontSee('<td>霸州百世快运-雅柏</td>', false);
}
public function test_courier_surcharge_store_update_and_status_are_persistent(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$payload = [
'courier' => '测试快递',
'scope' => '代发仓',
'reconciler' => '李会计',
'types' => ['拦截费', '改地址'],
'mode' => '固定单价',
'standard' => '12 元/单',
'condition' => '拦截成功',
'effective_at' => '2026-09-02',
'remark' => '接口保存测试',
];
$this->post(route('business.courier-surcharge.store'), $payload)
->assertRedirect(route('business.pages.show', [
'company_id' => company_id(), 'section' => 'courier-payables', 'page' => 'courier-surcharge',
]));
$quote = CourierSurchargeQuote::query()->where('courier', '测试快递')->firstOrFail();
$this->assertSame(['拦截费', '改地址'], $quote->types);
$this->assertSame(CourierSurchargeQuote::STATUS_ACTIVE, $quote->status);
$this->assertSame((int) user_id(), (int) $quote->created_by);
$this->assertSame((int) user_id(), (int) $quote->updated_by);
$this->patch(route('business.courier-surcharge.update', ['quote' => $quote->id]), [
...$payload,
'courier' => '测试快递(已修改)',
'types' => ['保费'],
])->assertRedirect();
$quote->refresh();
$this->assertSame('测试快递(已修改)', $quote->courier);
$this->assertSame(['保费'], $quote->types);
$this->post(route('business.courier-surcharge.status', ['quote' => $quote->id]), [
'status' => CourierSurchargeQuote::STATUS_INACTIVE,
])->assertRedirect();
$this->assertSame(CourierSurchargeQuote::STATUS_INACTIVE, $quote->fresh()->status);
}
public function test_quote_operations_are_recorded_with_actor_and_changed_fields(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$payload = [
'courier' => '日志测试快递',
'scope' => '自有快递',
'reconciler' => '周会计',
'types' => ['进仓费'],
'mode' => '固定单价',
'standard' => '10 元/单',
'condition' => '完成进仓',
'effective_at' => '2026-09-21',
'remark' => '初始标准',
];
$this->post(route('business.courier-surcharge.store'), $payload)->assertRedirect();
$quote = CourierSurchargeQuote::query()->where('courier', '日志测试快递')->firstOrFail();
$created = CourierSurchargeAuditLog::query()
->where('target_type', CourierSurchargeAuditLog::TARGET_QUOTE)
->where('target_id', $quote->id)
->where('action', CourierSurchargeAuditLog::ACTION_CREATED)
->firstOrFail();
$this->assertSame((int) user_id(), (int) $created->actor_id);
$this->assertSame('日志测试快递 · 进仓费', $created->target_label);
$this->assertSame('10 元/单', data_get($created->changes, 'after.standard'));
$this->patch(route('business.courier-surcharge.update', ['quote' => $quote->id]), [
...$payload,
'standard' => '12 元/单',
'remark' => '调整后标准',
])->assertRedirect();
$updated = CourierSurchargeAuditLog::query()
->where('target_type', CourierSurchargeAuditLog::TARGET_QUOTE)
->where('target_id', $quote->id)
->where('action', CourierSurchargeAuditLog::ACTION_UPDATED)
->firstOrFail();
$this->assertSame('10 元/单', data_get($updated->changes, 'before.standard'));
$this->assertSame('12 元/单', data_get($updated->changes, 'after.standard'));
$this->assertSame('初始标准', data_get($updated->changes, 'before.remark'));
$this->assertSame('调整后标准', data_get($updated->changes, 'after.remark'));
$this->post(route('business.courier-surcharge.status', ['quote' => $quote->id]), [
'status' => CourierSurchargeQuote::STATUS_INACTIVE,
])->assertRedirect();
$this->post(route('business.courier-surcharge.status', ['quote' => $quote->id]), [
'status' => CourierSurchargeQuote::STATUS_ACTIVE,
])->assertRedirect();
$this->assertDatabaseHas('business_courier_surcharge_audit_logs', [
'company_id' => company_id(),
'target_type' => CourierSurchargeAuditLog::TARGET_QUOTE,
'target_id' => $quote->id,
'action' => CourierSurchargeAuditLog::ACTION_DEACTIVATED,
]);
$this->assertDatabaseHas('business_courier_surcharge_audit_logs', [
'company_id' => company_id(),
'target_type' => CourierSurchargeAuditLog::TARGET_QUOTE,
'target_id' => $quote->id,
'action' => CourierSurchargeAuditLog::ACTION_ACTIVATED,
]);
$this->get(route('business.pages.show', ['section' => 'courier-payables', 'page' => 'courier-surcharge']))
->assertOk()
->assertSeeText('操作日志')
->assertSeeText('新增')
->assertSeeText('更改')
->assertSeeText('停用')
->assertSeeText('启用')
->assertSeeText($this->user->name)
->assertSeeText('计价标准:10 元/单 → 12 元/单');
}
public function test_type_operations_are_recorded_and_other_company_logs_are_hidden(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$this->post(route('business.courier-surcharge.types.store'), ['name' => '日志专用类型'])
->assertRedirect();
$type = CourierSurchargeType::query()->where('name', '日志专用类型')->firstOrFail();
$this->post(route('business.courier-surcharge.types.status', ['type' => $type->id]), [
'status' => CourierSurchargeType::STATUS_INACTIVE,
])->assertRedirect();
$this->post(route('business.courier-surcharge.types.status', ['type' => $type->id]), [
'status' => CourierSurchargeType::STATUS_ACTIVE,
])->assertRedirect();
$this->assertSame([
CourierSurchargeAuditLog::ACTION_CREATED,
CourierSurchargeAuditLog::ACTION_DEACTIVATED,
CourierSurchargeAuditLog::ACTION_ACTIVATED,
], CourierSurchargeAuditLog::query()
->where('target_type', CourierSurchargeAuditLog::TARGET_TYPE)
->where('target_id', $type->id)
->orderBy('id')
->pluck('action')
->all());
CourierSurchargeAuditLog::withoutGlobalScopes()->create([
'company_id' => company_id() + 999,
'actor_id' => user_id(),
'action' => CourierSurchargeAuditLog::ACTION_CREATED,
'target_type' => CourierSurchargeAuditLog::TARGET_TYPE,
'target_id' => 999,
'target_label' => '其他公司的私有类型',
'changes' => ['after' => ['name' => '其他公司的私有类型']],
]);
$this->get(route('business.pages.show', ['section' => 'courier-payables', 'page' => 'courier-surcharge']))
->assertOk()
->assertSeeText('附加费类型 · 日志专用类型')
->assertDontSeeText('其他公司的私有类型');
}
public function test_existing_quotes_remain_visible_but_writes_stop_when_audit_table_is_missing(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$quote = $this->createQuote();
Schema::drop('business_courier_surcharge_audit_logs');
$this->loginAs()
->get(route('business.pages.show', ['section' => 'courier-payables', 'page' => 'courier-surcharge']))
->assertOk()
->assertSeeText($quote->courier)
->assertSeeText('操作日志表尚未完成迁移')
->assertSee('data-surcharge-open disabled', false);
$this->withExceptionHandling()->post(route('business.courier-surcharge.store'), [
'courier' => '不应保存的快递',
'scope' => '自有快递',
'reconciler' => '周会计',
'types' => ['进仓费'],
'mode' => '固定单价',
'standard' => '10 元/单',
'condition' => '测试',
'effective_at' => '2026-09-21',
])->assertServiceUnavailable();
$this->assertDatabaseMissing('business_courier_surcharge_quotes', ['courier' => '不应保存的快递']);
}
public function test_courier_surcharge_import_template_contains_exact_headers_and_instructions(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$response = $this->loginAs()->get(route('business.courier-surcharge.import.template'));
$response->assertOk()->assertDownload();
$path = $this->temporaryPath('surcharge-template-');
file_put_contents($path, $response->streamedContent());
$spreadsheet = IOFactory::load($path);
try {
$this->assertSame(
CourierSurchargeQuoteWorkbookImporter::HEADERS,
$spreadsheet->getSheet(0)->rangeToArray('A1:J1')[0],
);
$this->assertSame('填写说明', $spreadsheet->getSheet(1)->getTitle());
$this->assertStringContainsString('不会静默覆盖', (string) $spreadsheet->getSheet(1)->getCell('B8')->getValue());
} finally {
$spreadsheet->disconnectWorksheets();
}
}
public function test_valid_workbook_can_be_previewed_and_committed_with_audit_logs(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$file = $this->workbook([[
'导入测试快递', '代发仓', '李会计', '进仓费、超长费', '固定单价',
'12.50 元/单', '入仓即收取', '2026-09-22', '已停用', '批量导入测试',
]]);
$previewResponse = $this->post(route('business.courier-surcharge.import.preview'), ['import_file' => $file]);
$previewResponse->assertRedirect()->assertSessionHas('courierSurchargeImportPreview', function (array $preview): bool {
return $preview['summary'] === ['total' => 1, 'valid' => 1, 'new' => 1, 'duplicate' => 0, 'invalid' => 0]
&& $preview['rows'][0]['line'] === 2
&& $preview['rows'][0]['data']['types'] === ['进仓费', '超长费'];
});
$preview = $previewResponse->getSession()->get('courierSurchargeImportPreview');
$this->post(route('business.courier-surcharge.import.commit'), [
'preview_token' => $preview['token'],
'duplicate_policy' => CourierSurchargeQuoteWorkbookImporter::DUPLICATE_SKIP,
])->assertRedirect()->assertSessionHas('success', '附加费报价导入完成:新增 1 条,更新 0 条,跳过 0 条。');
$quote = CourierSurchargeQuote::query()->where('courier', '导入测试快递')->firstOrFail();
$this->assertSame(['进仓费', '超长费'], $quote->types);
$this->assertSame(CourierSurchargeQuote::STATUS_INACTIVE, $quote->status);
$this->assertSame('批量导入测试', $quote->remark);
$this->assertDatabaseHas('business_courier_surcharge_audit_logs', [
'target_id' => $quote->id,
'action' => CourierSurchargeAuditLog::ACTION_CREATED,
'actor_id' => $this->user->id,
]);
$this->assertDatabaseHas('business_courier_surcharge_audit_logs', [
'target_id' => $quote->id,
'action' => CourierSurchargeAuditLog::ACTION_DEACTIVATED,
'actor_id' => $this->user->id,
]);
}
public function test_import_preview_reports_row_errors_and_blocks_commit(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$type = app(CourierSurchargeTypeRepository::class)->all()->firstWhere('name', '超长费');
$this->post(route('business.courier-surcharge.types.status', ['type' => $type->id]), [
'status' => CourierSurchargeType::STATUS_INACTIVE,
])->assertRedirect();
$previewResponse = $this->post(route('business.courier-surcharge.import.preview'), [
'import_file' => $this->workbook([[
'', '未知归属', '李会计', '超长费、虚构费', '未知方式', '',
'测试条件', '2026-02-31', '未知状态', '',
]]),
]);
$previewResponse->assertRedirect()->assertSessionHas('courierSurchargeImportPreview', function (array $preview): bool {
$errors = implode('|', $preview['rows'][0]['errors']);
return $preview['summary']['invalid'] === 1
&& str_contains($errors, '快递名称不能为空')
&& str_contains($errors, '附加费类型不存在:虚构费')
&& str_contains($errors, '附加费类型已停用:超长费')
&& str_contains($errors, '生效日期必须是有效的')
&& str_contains($errors, '状态只能填写');
});
$preview = $previewResponse->getSession()->get('courierSurchargeImportPreview');
$this->withExceptionHandling()->post(route('business.courier-surcharge.import.commit'), [
'preview_token' => $preview['token'],
'duplicate_policy' => CourierSurchargeQuoteWorkbookImporter::DUPLICATE_SKIP,
])->assertSessionHasErrors('import');
$this->assertDatabaseMissing('business_courier_surcharge_quotes', ['reconciler' => '李会计', 'condition' => '测试条件']);
}
public function test_import_requires_exact_headers_and_enforces_row_limit(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$wrongHeaders = CourierSurchargeQuoteWorkbookImporter::HEADERS;
$wrongHeaders[0] = '快递公司';
$this->withExceptionHandling()->post(route('business.courier-surcharge.import.preview'), [
'import_file' => $this->workbook([], $wrongHeaders),
])->assertSessionHasErrors('import');
$rows = array_fill(0, CourierSurchargeQuoteWorkbookImporter::MAX_ROWS + 1, [
'测试快递', '自有快递', '周会计', '进仓费', '固定单价',
'1 元/单', '测试条件', '2026-09-22', '已启用', '',
]);
$this->withExceptionHandling()->post(route('business.courier-surcharge.import.preview'), [
'import_file' => $this->workbook($rows),
])->assertSessionHasErrors('import');
}
public function test_file_duplicates_are_reported_and_existing_rules_require_an_explicit_policy(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$duplicateRow = [
'文件内重复快递', '自有快递', '周会计', '进仓费', '固定单价',
'5 元/单', '重复条件', '2026-09-22', '已启用', '',
];
$response = $this->post(route('business.courier-surcharge.import.preview'), [
'import_file' => $this->workbook([$duplicateRow, $duplicateRow]),
]);
$response->assertSessionHas('courierSurchargeImportPreview', function (array $preview): bool {
return $preview['summary']['invalid'] === 2
&& str_contains($preview['rows'][0]['errors'][0], '文件内规则重复')
&& str_contains($preview['rows'][1]['errors'][0], '文件内规则重复');
});
$existing = $this->createQuote();
$existingRow = [
$existing->courier, $existing->scope, $existing->reconciler, $existing->typeLabel(), $existing->mode,
'新计价标准', $existing->condition, $existing->effective_at->format('Y-m-d'), '已停用', '更新后的备注',
];
$skipPreview = $this->post(route('business.courier-surcharge.import.preview'), [
'import_file' => $this->workbook([$existingRow]),
])->getSession()->get('courierSurchargeImportPreview');
$this->post(route('business.courier-surcharge.import.commit'), [
'preview_token' => $skipPreview['token'],
'duplicate_policy' => CourierSurchargeQuoteWorkbookImporter::DUPLICATE_SKIP,
])->assertSessionHas('success', '附加费报价导入完成:新增 0 条,更新 0 条,跳过 1 条。');
$this->assertSame('最长边 > 160 cm:8 元/单', $existing->fresh()->standard);
$updatePreview = $this->post(route('business.courier-surcharge.import.preview'), [
'import_file' => $this->workbook([$existingRow]),
])->getSession()->get('courierSurchargeImportPreview');
$this->post(route('business.courier-surcharge.import.commit'), [
'preview_token' => $updatePreview['token'],
'duplicate_policy' => CourierSurchargeQuoteWorkbookImporter::DUPLICATE_UPDATE,
])->assertSessionHas('success', '附加费报价导入完成:新增 0 条,更新 1 条,跳过 0 条。');
$existing->refresh();
$this->assertSame('新计价标准', $existing->standard);
$this->assertSame('更新后的备注', $existing->remark);
$this->assertSame(CourierSurchargeQuote::STATUS_INACTIVE, $existing->status);
$this->assertDatabaseHas('business_courier_surcharge_audit_logs', [
'target_id' => $existing->id,
'action' => CourierSurchargeAuditLog::ACTION_UPDATED,
'actor_id' => $this->user->id,
]);
}
public function test_import_commit_rejects_expired_or_other_context_preview(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$this->loginAs();
$token = str_repeat('a', 48);
$this->withSession([
'courier_surcharge_import_previews' => [
$token => [
'company_id' => (int) company_id() + 999,
'user_id' => (int) $this->user->id,
'expires_at' => now()->subMinute()->timestamp,
'rows' => [],
],
],
])->post(route('business.courier-surcharge.import.commit'), [
'preview_token' => $token,
'duplicate_policy' => CourierSurchargeQuoteWorkbookImporter::DUPLICATE_SKIP,
])->assertRedirect()->assertSessionHasErrors('import');
}
/** @param list<list<mixed>> $rows @param list<string>|null $headers */
private function workbook(array $rows, ?array $headers = null): UploadedFile
{
$path = $this->temporaryPath('surcharge-import-');
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray($headers ?? CourierSurchargeQuoteWorkbookImporter::HEADERS, null, 'A1');
if ($rows !== []) {
$sheet->fromArray($rows, null, 'A2');
}
(new Xlsx($spreadsheet))->save($path);
$spreadsheet->disconnectWorksheets();
return new UploadedFile(
$path,
'附加费报价.xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
null,
true,
);
}
private function temporaryPath(string $prefix): string
{
$path = tempnam(sys_get_temp_dir(), $prefix);
$this->temporaryImportFiles[] = $path;
return $path;
}
protected function tearDown(): void
{
foreach ($this->temporaryImportFiles as $path) {
@unlink($path);
}
parent::tearDown();
}
private function createQuote(): CourierSurchargeQuote
{
return CourierSurchargeQuote::query()->create([
'company_id' => company_id(),
'courier' => '曹县德邦',
'scope' => '自有快递',
'reconciler' => '张会计',
'types' => ['超长费'],
'mode' => '按尺寸/最长边',
'standard' => '最长边 > 160 cm:8 元/单',
'condition' => '最长边超过 160 cm',
'effective_at' => '2026-09-01',
'status' => CourierSurchargeQuote::STATUS_ACTIVE,
'remark' => '大件渠道标准',
]);
}
}