From 72dff27db9bcb12410230a7627421e94115a7cdf Mon Sep 17 00:00:00 2001 From: zhoushengchen <3057083327@qq.com> Date: Tue, 22 Sep 2026 18:22:23 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=AB=E9=80=92?= =?UTF-8?q?=E9=99=84=E5=8A=A0=E8=B4=B9=E4=BE=9B=E5=BA=94=E5=95=86=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourierSurchargeAuditLogRepository.php | 1 + .../CourierSurchargeQuoteRepository.php | 18 ++++++- .../CourierSurchargeQuoteWorkbookImporter.php | 14 ++++-- .../Business/CourierSurchargeQuotes.php | 2 + app/Http/Controllers/Business/Pages.php | 1 + app/Models/Business/CourierSurchargeQuote.php | 1 + ...lier_to_courier_surcharge_quotes_table.php | 32 ++++++++++++ .../courier-surcharge.blade.php | 11 ++-- .../Business/CourierSurchargePageTest.php | 50 ++++++++++++++++--- 9 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 database/migrations/2026_09_22_010000_add_supplier_to_courier_surcharge_quotes_table.php diff --git a/app/Business/CourierSurchargeAuditLogRepository.php b/app/Business/CourierSurchargeAuditLogRepository.php index f6140b4b..ec24441e 100644 --- a/app/Business/CourierSurchargeAuditLogRepository.php +++ b/app/Business/CourierSurchargeAuditLogRepository.php @@ -20,6 +20,7 @@ class CourierSurchargeAuditLogRepository 'courier' => '快递名称', 'scope' => '快递归属', 'reconciler' => '对账人', + 'supplier' => '供应商', 'types' => '附加费类型', 'mode' => '结算方式', 'standard' => '计价标准', diff --git a/app/Business/CourierSurchargeQuoteRepository.php b/app/Business/CourierSurchargeQuoteRepository.php index 8f43052b..43064f2e 100644 --- a/app/Business/CourierSurchargeQuoteRepository.php +++ b/app/Business/CourierSurchargeQuoteRepository.php @@ -40,7 +40,11 @@ class CourierSurchargeQuoteRepository public function ready(): bool { try { - return Schema::hasTable((new CourierSurchargeQuote())->getTable()) && $this->types->ready(); + $table = (new CourierSurchargeQuote())->getTable(); + + return Schema::hasTable($table) + && Schema::hasColumn($table, 'supplier') + && $this->types->ready(); } catch (QueryException) { return false; } @@ -76,7 +80,7 @@ class CourierSurchargeQuoteRepository } $filtered = $all->filter(function (CourierSurchargeQuote $quote) use ($filters): bool { - foreach (['courier', 'scope', 'reconciler', 'mode'] as $key) { + foreach (['courier', 'scope', 'reconciler', 'supplier', 'mode'] as $key) { if (($filters[$key] ?? '') !== '' && (string) $quote->{$key} !== $filters[$key]) { return false; } @@ -96,6 +100,7 @@ class CourierSurchargeQuoteRepository $quote->courier, $quote->scope, $quote->reconciler, + (string) $quote->supplier, $quote->typeLabel(), $quote->mode, $quote->standard, @@ -139,6 +144,7 @@ class CourierSurchargeQuoteRepository 'modes' => collect(self::MODES), 'scopes' => self::SCOPES, 'reconcilers' => $all->pluck('reconciler')->filter()->unique()->sort()->values(), + 'suppliers' => $all->pluck('supplier')->filter()->unique()->sort()->values(), 'statuses' => ['已启用', '已停用'], ], ]; @@ -168,6 +174,9 @@ class CourierSurchargeQuoteRepository ->where('condition', trim((string) $data['condition'])) ->whereDate('effective_at', (string) $data['effective_at']); + $supplier = trim((string) ($data['supplier'] ?? '')); + $supplier === '' ? $query->whereNull('supplier') : $query->where('supplier', $supplier); + if ($lockForUpdate) { $query->lockForUpdate(); } @@ -186,6 +195,7 @@ class CourierSurchargeQuoteRepository trim((string) ($data['courier'] ?? '')), trim((string) ($data['scope'] ?? '')), trim((string) ($data['reconciler'] ?? '')), + trim((string) ($data['supplier'] ?? '')), $this->normalizedTypes((array) ($data['types'] ?? [])), trim((string) ($data['mode'] ?? '')), trim((string) ($data['effective_at'] ?? '')), @@ -279,6 +289,7 @@ class CourierSurchargeQuoteRepository 'courier' => $quote->courier, 'scope' => $quote->scope, 'reconciler' => $quote->reconciler, + 'supplier' => $quote->supplier, 'types' => array_values((array) $quote->types), 'mode' => $quote->mode, 'standard' => $quote->standard, @@ -302,6 +313,7 @@ class CourierSurchargeQuoteRepository 'courier' => trim((string) $data['courier']), 'scope' => trim((string) $data['scope']), 'reconciler' => trim((string) $data['reconciler']), + 'supplier' => trim((string) ($data['supplier'] ?? '')) ?: null, 'types' => array_values(array_unique(array_map('strval', $data['types']))), 'mode' => trim((string) $data['mode']), 'standard' => trim((string) $data['standard']), @@ -333,6 +345,7 @@ class CourierSurchargeQuoteRepository 'courier' => $quote->courier, 'scope' => $quote->scope, 'reconciler' => $quote->reconciler, + 'supplier' => $quote->supplier, 'types' => array_values((array) $quote->types), 'type' => $quote->typeLabel(), 'mode' => $quote->mode, @@ -356,6 +369,7 @@ class CourierSurchargeQuoteRepository 'modes' => collect(self::MODES), 'scopes' => self::SCOPES, 'reconcilers' => collect(), + 'suppliers' => collect(), 'statuses' => ['已启用', '已停用'], ]; } diff --git a/app/Business/CourierSurchargeQuoteWorkbookImporter.php b/app/Business/CourierSurchargeQuoteWorkbookImporter.php index 22ecff3a..a630cc1f 100644 --- a/app/Business/CourierSurchargeQuoteWorkbookImporter.php +++ b/app/Business/CourierSurchargeQuoteWorkbookImporter.php @@ -29,6 +29,7 @@ class CourierSurchargeQuoteWorkbookImporter '快递名称', '快递归属', '对账人', + '供应商', '附加费类型', '结算方式', '计价标准', @@ -64,7 +65,7 @@ class CourierSurchargeQuoteWorkbookImporter $sheet->freezePane('A2'); $sheet->setAutoFilter($headerRange); - $widths = [18, 14, 14, 24, 18, 30, 32, 14, 12, 30]; + $widths = [18, 14, 14, 20, 24, 18, 30, 32, 14, 12, 30]; foreach ($widths as $index => $width) { $column = Coordinate::stringFromColumnIndex($index + 1); $sheet->getColumnDimension($column)->setWidth($width); @@ -78,6 +79,7 @@ class CourierSurchargeQuoteWorkbookImporter $instructions->fromArray([ ['项目', '填写要求'], ['必填字段', '快递名称、快递归属、对账人、附加费类型、结算方式、计价标准、生效条件、生效日期、状态。'], + ['供应商', '选填;没有供应商时保持空白。'], ['附加费类型', '多种类型使用“、”分隔;只能填写当前已启用类型。当前可选:' . implode('、', $types)], ['快递归属', '自有快递 或 代发仓。'], ['结算方式', implode('、', CourierSurchargeQuoteRepository::MODES)], @@ -92,8 +94,8 @@ class CourierSurchargeQuoteWorkbookImporter ]); $instructions->getColumnDimension('A')->setWidth(18); $instructions->getColumnDimension('B')->setWidth(90); - $instructions->getStyle('A1:B9')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER)->setWrapText(true); - foreach (range(1, 9) as $row) { + $instructions->getStyle('A1:B10')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER)->setWrapText(true); + foreach (range(1, 10) as $row) { $instructions->getRowDimension($row)->setRowHeight($row === 1 ? 24 : 34); } @@ -144,7 +146,7 @@ class CourierSurchargeQuoteWorkbookImporter } if ($headers !== self::HEADERS) { throw ValidationException::withMessages([ - 'import' => '表头不正确。请下载最新模板,并保持 10 个字段名称及顺序不变。', + 'import' => '表头不正确。请下载最新模板,并保持 11 个字段名称及顺序不变。', ]); } @@ -273,7 +275,7 @@ class CourierSurchargeQuoteWorkbookImporter } $errors = []; - foreach (array_slice(self::HEADERS, 0, 9) as $header) { + foreach (['快递名称', '快递归属', '对账人', '附加费类型', '结算方式', '计价标准', '生效条件', '生效日期', '状态'] as $header) { if ($display[$header] === '') { $errors[] = $header . '不能为空。'; } @@ -281,6 +283,7 @@ class CourierSurchargeQuoteWorkbookImporter $this->checkLength($display, '快递名称', 191, $errors); $this->checkLength($display, '对账人', 100, $errors); + $this->checkLength($display, '供应商', 191, $errors); $this->checkLength($display, '计价标准', 2000, $errors); $this->checkLength($display, '生效条件', 2000, $errors); $this->checkLength($display, '备注', 2000, $errors); @@ -333,6 +336,7 @@ class CourierSurchargeQuoteWorkbookImporter 'courier' => $display['快递名称'], 'scope' => $display['快递归属'], 'reconciler' => $display['对账人'], + 'supplier' => $display['供应商'] !== '' ? $display['供应商'] : null, 'types' => $types, 'mode' => $display['结算方式'], 'standard' => $display['计价标准'], diff --git a/app/Http/Controllers/Business/CourierSurchargeQuotes.php b/app/Http/Controllers/Business/CourierSurchargeQuotes.php index 5825c7f6..3364bf36 100644 --- a/app/Http/Controllers/Business/CourierSurchargeQuotes.php +++ b/app/Http/Controllers/Business/CourierSurchargeQuotes.php @@ -134,6 +134,7 @@ class CourierSurchargeQuotes extends Controller 'courier' => ['required', 'string', 'max:191'], 'scope' => ['required', 'in:' . implode(',', CourierSurchargeQuoteRepository::SCOPES)], 'reconciler' => ['required', 'string', 'max:100'], + 'supplier' => ['nullable', 'string', 'max:191'], 'types' => ['required', 'array', 'min:1', 'max:50'], 'types.*' => ['string', 'in:' . implode(',', $this->quotes->activeTypeNames())], 'mode' => ['required', 'in:' . implode(',', CourierSurchargeQuoteRepository::MODES)], @@ -145,6 +146,7 @@ class CourierSurchargeQuotes extends Controller $data['courier'] = trim($data['courier']); $data['reconciler'] = trim($data['reconciler']); + $data['supplier'] = trim((string) ($data['supplier'] ?? '')) ?: null; $data['types'] = array_values(array_unique($data['types'])); return $data; diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index 1bc354b1..f62591be 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -1128,6 +1128,7 @@ class Pages extends Controller 'mode' => $clean(request('mode', ''), 40), 'scope' => $clean(request('scope', ''), 20), 'reconciler' => $clean(request('reconciler', ''), 40), + 'supplier' => $clean(request('supplier', ''), 191), 'status' => $clean(request('status', ''), 20), ]; $data = $this->courierSurchargeQuotes->pageData($filters); diff --git a/app/Models/Business/CourierSurchargeQuote.php b/app/Models/Business/CourierSurchargeQuote.php index 8cf53c08..5f3ff814 100644 --- a/app/Models/Business/CourierSurchargeQuote.php +++ b/app/Models/Business/CourierSurchargeQuote.php @@ -17,6 +17,7 @@ class CourierSurchargeQuote extends Model 'courier', 'scope', 'reconciler', + 'supplier', 'types', 'mode', 'standard', diff --git a/database/migrations/2026_09_22_010000_add_supplier_to_courier_surcharge_quotes_table.php b/database/migrations/2026_09_22_010000_add_supplier_to_courier_surcharge_quotes_table.php new file mode 100644 index 00000000..0ba54e23 --- /dev/null +++ b/database/migrations/2026_09_22_010000_add_supplier_to_courier_surcharge_quotes_table.php @@ -0,0 +1,32 @@ +string('supplier', 191)->nullable()->after('reconciler'); + }); + } + + public function down(): void + { + if (! Schema::hasTable('business_courier_surcharge_quotes') + || ! Schema::hasColumn('business_courier_surcharge_quotes', 'supplier')) { + return; + } + + Schema::table('business_courier_surcharge_quotes', function (Blueprint $table): void { + $table->dropColumn('supplier'); + }); + } +}; diff --git a/resources/views/business/pages/courier-payables/courier-surcharge.blade.php b/resources/views/business/pages/courier-payables/courier-surcharge.blade.php index 5b72ec04..eafe0a63 100644 --- a/resources/views/business/pages/courier-payables/courier-surcharge.blade.php +++ b/resources/views/business/pages/courier-payables/courier-surcharge.blade.php @@ -17,6 +17,7 @@ ['key' => 'courier', 'label' => '快递名称', 'exportable' => true], ['key' => 'scope', 'label' => '快递归属', 'exportable' => true], ['key' => 'reconciler', 'label' => '对账人', 'exportable' => true], + ['key' => 'supplier', 'label' => '供应商', 'exportable' => true], ['key' => 'type', 'label' => '附加费类型', 'exportable' => true], ['key' => 'mode', 'label' => '结算方式', 'exportable' => true], ['key' => 'standard', 'label' => '计价标准', 'exportable' => true], @@ -114,10 +115,11 @@
筛选报价规则
-
+
+
@@ -144,10 +146,10 @@ @forelse ($rows as $row) - {{ $row['id'] }}{{ $row['courier'] }}{{ $row['scope'] }}{{ $row['reconciler'] }}{{ $row['type'] }}{{ $row['mode'] }}{{ $row['standard'] }}{{ $row['condition'] }}{{ $row['effective_at'] }}{{ $row['status'] }}{{ $row['remark'] ?: '—' }}
@csrf
+ {{ $row['id'] }}{{ $row['courier'] }}{{ $row['scope'] }}{{ $row['reconciler'] }}{{ $row['supplier'] ?: '—' }}{{ $row['type'] }}{{ $row['mode'] }}{{ $row['standard'] }}{{ $row['condition'] }}{{ $row['effective_at'] }}{{ $row['status'] }}{{ $row['remark'] ?: '—' }}
@csrf
@empty -
没有符合条件的报价规则,请点击“新增报价规则”。
+
没有符合条件的报价规则,请点击“新增报价规则”。
@endforelse
维护建议:固定单价、按重量、按体积、按尺寸/最长边等方式直接维护标准;聚工单客服核实、取发出运费和按账单等方式只维护触发条件,具体金额由后续结算链路读取业务凭证。批量导入必须先预览校验,已有规则需明确选择跳过或更新。
@@ -242,6 +244,7 @@
+
附加费类型(可多选)
@foreach (($options['types'] ?? []) as $option)@endforeach
@@ -482,7 +485,7 @@ }); root.querySelectorAll('[data-surcharge-edit]').forEach((button) => button.addEventListener('click', () => { const row = button.closest('[data-surcharge-row]'); const data = JSON.parse(row.dataset.rule); form.action = row.dataset.updateUrl; methodField.value = 'PATCH'; title.textContent = '编辑报价规则'; - ['courier', 'scope', 'mode', 'standard', 'condition', 'effective_at', 'reconciler', 'remark'].forEach((key) => { const field = form.elements.namedItem(key); if (field) field.value = data[key] || ''; }); + ['courier', 'scope', 'mode', 'standard', 'condition', 'effective_at', 'reconciler', 'supplier', 'remark'].forEach((key) => { const field = form.elements.namedItem(key); if (field) field.value = data[key] || ''; }); const selectedTypes = Array.isArray(data.types) ? data.types : (data.type || '').split('、').map((item) => item.trim()).filter(Boolean); form.querySelectorAll('input[name="types[]"]').forEach((checkbox) => { checkbox.checked = selectedTypes.includes(checkbox.value); }); modal.classList.add('is-open'); diff --git a/tests/Feature/Business/CourierSurchargePageTest.php b/tests/Feature/Business/CourierSurchargePageTest.php index ee6dd409..adf4f586 100644 --- a/tests/Feature/Business/CourierSurchargePageTest.php +++ b/tests/Feature/Business/CourierSurchargePageTest.php @@ -46,6 +46,8 @@ class CourierSurchargePageTest extends FeatureTestCase ->assertSeeText('代发仓') ->assertSeeText('对账人') ->assertSeeText('张会计') + ->assertSeeText('供应商') + ->assertSeeText('曹县物流供应商') ->assertSeeText('附加费类型(可多选)') ->assertSeeText('列设置') ->assertSeeText('导出当前列') @@ -61,7 +63,7 @@ class CourierSurchargePageTest extends FeatureTestCase ->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) + ->assertSeeInOrder(['data-column-key="courier"', 'data-column-key="scope"', 'data-column-key="reconciler"', 'data-column-key="supplier"'], false) ->assertSeeText('曹县德邦') ->assertSeeText('超长费') ->assertSeeText('按尺寸/最长边') @@ -151,6 +153,7 @@ class CourierSurchargePageTest extends FeatureTestCase 'courier' => '常州松宇货运', 'scope' => '代发仓', 'reconciler' => '李会计', + 'supplier' => '常州物流供应商', 'types' => ['送货费'], 'mode' => '按体积', 'standard' => '体积 ≤ 10 m³:50 元/单', @@ -167,6 +170,7 @@ class CourierSurchargePageTest extends FeatureTestCase 'courier' => '常州松宇货运', 'scope' => '代发仓', 'reconciler' => '李会计', + 'supplier' => '常州物流供应商', 'status' => '已启用', ])) ->assertOk() @@ -174,6 +178,7 @@ class CourierSurchargePageTest extends FeatureTestCase ->assertSeeText('送货费') ->assertSeeText('代发仓') ->assertSeeText('李会计') + ->assertSeeText('常州物流供应商') ->assertSeeText('当前规则') ->assertDontSee('曹县德邦', false) ->assertDontSee('霸州百世快运-雅柏', false); @@ -188,6 +193,7 @@ class CourierSurchargePageTest extends FeatureTestCase 'courier' => '测试快递', 'scope' => '代发仓', 'reconciler' => '李会计', + 'supplier' => '', 'types' => ['拦截费', '改地址'], 'mode' => '固定单价', 'standard' => '12 元/单', @@ -203,6 +209,7 @@ class CourierSurchargePageTest extends FeatureTestCase $quote = CourierSurchargeQuote::query()->where('courier', '测试快递')->firstOrFail(); $this->assertSame(['拦截费', '改地址'], $quote->types); + $this->assertNull($quote->supplier); $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); @@ -210,11 +217,13 @@ class CourierSurchargePageTest extends FeatureTestCase $this->patch(route('business.courier-surcharge.update', ['quote' => $quote->id]), [ ...$payload, 'courier' => '测试快递(已修改)', + 'supplier' => '测试供应商', 'types' => ['保费'], ])->assertRedirect(); $quote->refresh(); $this->assertSame('测试快递(已修改)', $quote->courier); + $this->assertSame('测试供应商', $quote->supplier); $this->assertSame(['保费'], $quote->types); $this->post(route('business.courier-surcharge.status', ['quote' => $quote->id]), [ @@ -233,6 +242,7 @@ class CourierSurchargePageTest extends FeatureTestCase 'courier' => '日志测试快递', 'scope' => '自有快递', 'reconciler' => '周会计', + 'supplier' => '原供应商', 'types' => ['进仓费'], 'mode' => '固定单价', 'standard' => '10 元/单', @@ -255,6 +265,7 @@ class CourierSurchargePageTest extends FeatureTestCase $this->patch(route('business.courier-surcharge.update', ['quote' => $quote->id]), [ ...$payload, + 'supplier' => '新供应商', 'standard' => '12 元/单', 'remark' => '调整后标准', ])->assertRedirect(); @@ -268,6 +279,8 @@ class CourierSurchargePageTest extends FeatureTestCase $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->assertSame('原供应商', data_get($updated->changes, 'before.supplier')); + $this->assertSame('新供应商', data_get($updated->changes, 'after.supplier')); $this->post(route('business.courier-surcharge.status', ['quote' => $quote->id]), [ 'status' => CourierSurchargeQuote::STATUS_INACTIVE, @@ -297,6 +310,7 @@ class CourierSurchargePageTest extends FeatureTestCase ->assertSeeText('停用') ->assertSeeText('启用') ->assertSeeText($this->user->name) + ->assertSeeText('供应商:原供应商 → 新供应商') ->assertSeeText('计价标准:10 元/单 → 12 元/单'); } @@ -383,10 +397,10 @@ class CourierSurchargePageTest extends FeatureTestCase try { $this->assertSame( CourierSurchargeQuoteWorkbookImporter::HEADERS, - $spreadsheet->getSheet(0)->rangeToArray('A1:J1')[0], + $spreadsheet->getSheet(0)->rangeToArray('A1:K1')[0], ); $this->assertSame('填写说明', $spreadsheet->getSheet(1)->getTitle()); - $this->assertStringContainsString('不会静默覆盖', (string) $spreadsheet->getSheet(1)->getCell('B8')->getValue()); + $this->assertStringContainsString('不会静默覆盖', (string) $spreadsheet->getSheet(1)->getCell('B9')->getValue()); } finally { $spreadsheet->disconnectWorksheets(); } @@ -397,7 +411,7 @@ class CourierSurchargePageTest extends FeatureTestCase $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); $this->loginAs(); $file = $this->workbook([[ - '导入测试快递', '代发仓', '李会计', '进仓费、超长费', '固定单价', + '导入测试快递', '代发仓', '李会计', '导入供应商', '进仓费、超长费', '固定单价', '12.50 元/单', '入仓即收取', '2026-09-22', '已停用', '批量导入测试', ]]); @@ -416,6 +430,7 @@ class CourierSurchargePageTest extends FeatureTestCase $quote = CourierSurchargeQuote::query()->where('courier', '导入测试快递')->firstOrFail(); $this->assertSame(['进仓费', '超长费'], $quote->types); + $this->assertSame('导入供应商', $quote->supplier); $this->assertSame(CourierSurchargeQuote::STATUS_INACTIVE, $quote->status); $this->assertSame('批量导入测试', $quote->remark); $this->assertDatabaseHas('business_courier_surcharge_audit_logs', [ @@ -441,7 +456,7 @@ class CourierSurchargePageTest extends FeatureTestCase $previewResponse = $this->post(route('business.courier-surcharge.import.preview'), [ 'import_file' => $this->workbook([[ - '', '未知归属', '李会计', '超长费、虚构费', '未知方式', '', + '', '未知归属', '李会计', '', '超长费、虚构费', '未知方式', '', '测试条件', '2026-02-31', '未知状态', '', ]]), ]); @@ -476,7 +491,7 @@ class CourierSurchargePageTest extends FeatureTestCase ])->assertSessionHasErrors('import'); $rows = array_fill(0, CourierSurchargeQuoteWorkbookImporter::MAX_ROWS + 1, [ - '测试快递', '自有快递', '周会计', '进仓费', '固定单价', + '测试快递', '自有快递', '周会计', '', '进仓费', '固定单价', '1 元/单', '测试条件', '2026-09-22', '已启用', '', ]); $this->withExceptionHandling()->post(route('business.courier-surcharge.import.preview'), [ @@ -489,7 +504,7 @@ class CourierSurchargePageTest extends FeatureTestCase $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'), [ @@ -503,7 +518,7 @@ class CourierSurchargePageTest extends FeatureTestCase $existing = $this->createQuote(); $existingRow = [ - $existing->courier, $existing->scope, $existing->reconciler, $existing->typeLabel(), $existing->mode, + $existing->courier, $existing->scope, $existing->reconciler, $existing->supplier, $existing->typeLabel(), $existing->mode, '新计价标准', $existing->condition, $existing->effective_at->format('Y-m-d'), '已停用', '更新后的备注', ]; $skipPreview = $this->post(route('business.courier-surcharge.import.preview'), [ @@ -534,6 +549,24 @@ class CourierSurchargePageTest extends FeatureTestCase ]); } + public function test_import_duplicate_identity_includes_optional_supplier(): void + { + $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); + $this->loginAs(); + $base = [ + '供应商区分快递', '自有快递', '周会计', '供应商甲', '进仓费', '固定单价', + '5 元/单', '相同条件', '2026-09-22', '已启用', '', + ]; + $otherSupplier = $base; + $otherSupplier[3] = '供应商乙'; + + $this->post(route('business.courier-surcharge.import.preview'), [ + 'import_file' => $this->workbook([$base, $otherSupplier]), + ])->assertSessionHas('courierSurchargeImportPreview', function (array $preview): bool { + return $preview['summary'] === ['total' => 2, 'valid' => 2, 'new' => 2, 'duplicate' => 0, 'invalid' => 0]; + }); + } + public function test_import_commit_rejects_expired_or_other_context_preview(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); @@ -600,6 +633,7 @@ class CourierSurchargePageTest extends FeatureTestCase 'courier' => '曹县德邦', 'scope' => '自有快递', 'reconciler' => '张会计', + 'supplier' => '曹县物流供应商', 'types' => ['超长费'], 'mode' => '按尺寸/最长边', 'standard' => '最长边 > 160 cm:8 元/单', -- 2.47.2 From d11693dc97e43dee951312af7107ba0ba53c9a26 Mon Sep 17 00:00:00 2001 From: zhoushengchen <3057083327@qq.com> Date: Tue, 22 Sep 2026 18:32:43 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=8C=89=E6=8A=A5=E4=BB=B7=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E9=99=84=E5=8A=A0=E8=B4=B9=E6=93=8D=E4=BD=9C=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CourierSurchargeAuditLogRepository.php | 2 + .../courier-surcharge.blade.php | 58 ++++++++++++++++--- .../Business/CourierSurchargePageTest.php | 14 ++++- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/app/Business/CourierSurchargeAuditLogRepository.php b/app/Business/CourierSurchargeAuditLogRepository.php index ec24441e..eb3acdd5 100644 --- a/app/Business/CourierSurchargeAuditLogRepository.php +++ b/app/Business/CourierSurchargeAuditLogRepository.php @@ -99,6 +99,8 @@ class CourierSurchargeAuditLogRepository 'actor_name' => $log->actor?->name ?: '系统', 'action' => $log->action, 'action_label' => self::ACTION_LABELS[$log->action] ?? $log->action, + 'target_type' => $log->target_type, + 'target_id' => $log->target_id, 'target_type_label' => self::TARGET_LABELS[$log->target_type] ?? $log->target_type, 'target_label' => $log->target_label, 'changes_label' => $this->changesLabel($log), diff --git a/resources/views/business/pages/courier-payables/courier-surcharge.blade.php b/resources/views/business/pages/courier-payables/courier-surcharge.blade.php index eafe0a63..7996f7e8 100644 --- a/resources/views/business/pages/courier-payables/courier-surcharge.blade.php +++ b/resources/views/business/pages/courier-payables/courier-surcharge.blade.php @@ -63,6 +63,10 @@ .sm-audit-table td:nth-child(5) { min-width:24rem; white-space:normal; line-height:1.5; } .sm-audit-action { display:inline-flex; align-items:center; padding:.15rem .45rem; border-radius:999px; background:var(--sm-soft); color:var(--sm-accent); font-size:.7rem; font-weight:700; } .sm-audit-empty { padding:1.8rem 1rem; text-align:center; color:var(--sm-muted); } + .sm-audit-empty[hidden],.sm-audit-table-wrap[hidden],.sm-audit-table tr[hidden] { display:none; } + .sm-table tr[data-surcharge-row] { cursor:pointer; } + .sm-table tr[data-surcharge-row]:focus-visible { outline:2px solid var(--sm-accent); outline-offset:-2px; } + .sm-table tr.is-log-selected td,.sm-table tr.is-log-selected:hover td,.sm-table tr.is-log-selected [data-column-key="id"],.sm-table tr.is-log-selected [data-column-key="actions"] { background:var(--sm-soft); } .sm-column-picker { position:relative; } .sm-column-picker__panel { position:absolute; right:0; z-index:20; display:grid; width:min(22rem,calc(100vw - 2rem)); gap:.65rem; margin-top:.35rem; padding:.8rem; border:1px solid var(--sm-border); border-radius:.55rem; background:#fff; box-shadow:0 4px 8px rgba(23,23,23,.1); } .sm-column-picker__panel[hidden] { display:none; } .sm-column-picker__intro { display:grid; gap:.2rem; color:var(--sm-muted); font-size:.7rem; line-height:1.45; } .sm-column-picker__intro strong { color:var(--sm-ink); font-size:.78rem; } .sm-visually-hidden { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; } .sm-column-picker__list { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.35rem; max-height:15rem; overflow:auto; } .sm-column-picker__option { display:flex; align-items:center; gap:.35rem; min-width:0; padding:.35rem .4rem; border:1px solid transparent; border-radius:.35rem; color:var(--sm-ink); font-size:.7rem; cursor:grab; } .sm-column-picker__option:hover,.sm-column-picker__option.is-drop-target { border-color:var(--sm-border); background:#fffdf8; } .sm-column-picker__option.is-fixed { cursor:default; } .sm-column-picker__option.is-dragging { opacity:.55; } .sm-column-picker__option input { flex:none; accent-color:var(--sm-accent); } .sm-column-picker__option .material-icons-outlined { color:var(--sm-muted); font-size:.95rem; } .sm-column-picker__foot { display:flex; justify-content:flex-end; padding-top:.35rem; border-top:1px solid var(--sm-border); } .sm-badge { display:inline-flex; align-items:center; padding:.15rem .5rem; border-radius:999px; font-size:.7rem; font-weight:700; } .sm-badge.is-active { background:#edf5e8; color:#53793e; } .sm-badge.is-inactive { background:#fff4df; color:#935f07; } .sm-empty { padding:3rem 1rem; text-align:center; color:var(--sm-muted); } .sm-rule-note { padding:1rem 1.15rem; color:var(--sm-muted); font-size:.75rem; line-height:1.65; background:#fffdf8; } @@ -145,7 +149,7 @@ @endforeach @forelse ($rows as $row) - + {{ $row['id'] }}{{ $row['courier'] }}{{ $row['scope'] }}{{ $row['reconciler'] }}{{ $row['supplier'] ?: '—' }}{{ $row['type'] }}{{ $row['mode'] }}{{ $row['standard'] }}{{ $row['condition'] }}{{ $row['effective_at'] }}{{ $row['status'] }}{{ $row['remark'] ?: '—' }}
@csrf
@empty @@ -155,19 +159,19 @@
维护建议:固定单价、按重量、按体积、按尺寸/最长边等方式直接维护标准;聚工单客服核实、取发出运费和按账单等方式只维护触发条件,具体金额由后续结算链路读取业务凭证。批量导入必须先预览校验,已有规则需明确选择跳过或更新。
-
-
操作日志
记录当前公司最近 50 条新增、更改、启用和停用操作
+
+
操作日志
请选择报价
@if (!$auditReady)
操作日志表尚未就绪,完成迁移后将从首次新操作开始记录。
- @elseif ($auditLogs->isEmpty()) -
暂无操作日志。新增、编辑或切换报价规则/附加费类型状态后,记录会显示在这里。
@else -
+
请选择上方一条报价
+ +