parent
11c506af89
commit
a5c536f9dd
@ -24,6 +24,10 @@ class ServiceCompanyReconciliationRepository
|
||||
'settlement_income' => '业绩明细收入(不减退款)',
|
||||
];
|
||||
|
||||
public const FIXED_FEE_RATES_PERCENT = [
|
||||
'customer_service' => 1.5,
|
||||
];
|
||||
|
||||
private readonly ConnectionInterface $connection;
|
||||
|
||||
public function __construct(DatabaseManager $database)
|
||||
@ -523,6 +527,17 @@ class ServiceCompanyReconciliationRepository
|
||||
if ($feeRatePercent < 0 || $feeRatePercent > 100) {
|
||||
throw ValidationException::withMessages(['fee_rate_percent' => '结算比例必须在 0% 到 100% 之间。']);
|
||||
}
|
||||
$fixedFeeRatePercent = self::fixedFeeRatePercent((string) $company['service_type_code']);
|
||||
if ($fixedFeeRatePercent !== null
|
||||
&& abs($feeRatePercent - $fixedFeeRatePercent) > 0.000001) {
|
||||
throw ValidationException::withMessages([
|
||||
'fee_rate_percent' => sprintf(
|
||||
'%s的结算比例固定为 %s%%。',
|
||||
self::SERVICE_TYPES[(string) $company['service_type_code']],
|
||||
rtrim(rtrim(number_format($fixedFeeRatePercent, 4, '.', ''), '0'), '.'),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'service_company_key' => (string) $company['company_key'],
|
||||
@ -1039,6 +1054,11 @@ class ServiceCompanyReconciliationRepository
|
||||
return $text === '' ? null : mb_substr($text, 0, $length);
|
||||
}
|
||||
|
||||
public static function fixedFeeRatePercent(string $serviceTypeCode): ?float
|
||||
{
|
||||
return self::FIXED_FEE_RATES_PERCENT[$serviceTypeCode] ?? null;
|
||||
}
|
||||
|
||||
private function serviceTypeCodeFromLabel(string $label): string
|
||||
{
|
||||
if (mb_stripos($label, '客服') !== false) {
|
||||
|
||||
@ -41,6 +41,7 @@ class ServiceCompanyReconciliation extends Controller
|
||||
'storeCompanies' => [],
|
||||
'serviceTypes' => ServiceCompanyReconciliationRepository::SERVICE_TYPES,
|
||||
'baseMetrics' => ServiceCompanyReconciliationRepository::BASE_METRICS,
|
||||
'fixedFeeRatePercent' => null,
|
||||
'companyDirectory' => $this->repository->companyDirectory(),
|
||||
'companyPlanSyncError' => $companyPlanSyncError,
|
||||
'canEdit' => FinanceLedgerAccess::canEditMapping(user()),
|
||||
@ -94,6 +95,9 @@ class ServiceCompanyReconciliation extends Controller
|
||||
'storeCompanies' => $this->repository->storeCompanies($tenantId),
|
||||
'serviceTypes' => ServiceCompanyReconciliationRepository::SERVICE_TYPES,
|
||||
'baseMetrics' => ServiceCompanyReconciliationRepository::BASE_METRICS,
|
||||
'fixedFeeRatePercent' => ServiceCompanyReconciliationRepository::fixedFeeRatePercent(
|
||||
(string) $company['service_type_code'],
|
||||
),
|
||||
'companyDirectory' => $this->repository->companyDirectory(),
|
||||
'companyPlanSyncError' => $companyPlanSyncError,
|
||||
'canEdit' => FinanceLedgerAccess::canEditMapping(user()),
|
||||
|
||||
@ -995,7 +995,13 @@
|
||||
<header class="svc-panel__head">
|
||||
<div>
|
||||
<h3 id="svc-relations-title">店铺公司结算关系</h3>
|
||||
<p>一行只对应一家店铺公司;修改比例会生成新版本。</p>
|
||||
<p>
|
||||
@if ($fixedFeeRatePercent !== null)
|
||||
客服服务公司结算比例固定为 {{ $fixedFeeRatePercent }}%;其他信息修改会生成新版本。
|
||||
@else
|
||||
一行只对应一家店铺公司;修改比例会生成新版本。
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@if ($canEdit)
|
||||
<button class="svc-page__button" type="button" data-relation-create>
|
||||
@ -1314,7 +1320,12 @@
|
||||
|
||||
@if ($isDetail)
|
||||
<dialog class="svc-dialog" id="svc-relation-dialog" aria-labelledby="svc-relation-dialog-title">
|
||||
<form method="POST" action="{{ route('business.service-company-reconciliation.relations.store', ['serviceCompany' => $serviceCompany['id']]) }}" data-relation-form>
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ route('business.service-company-reconciliation.relations.store', ['serviceCompany' => $serviceCompany['id']]) }}"
|
||||
data-relation-form
|
||||
@if ($fixedFeeRatePercent !== null) data-fixed-fee-rate="{{ $fixedFeeRatePercent }}" @endif
|
||||
>
|
||||
@csrf
|
||||
<input type="hidden" name="_method" value="POST" data-relation-method>
|
||||
<header class="svc-dialog__head">
|
||||
@ -1346,7 +1357,21 @@
|
||||
</div>
|
||||
<div class="svc-field">
|
||||
<label for="svc-fee-rate">结算比例(%)</label>
|
||||
<input id="svc-fee-rate" type="number" name="fee_rate_percent" min="0" max="100" step="0.0001" value="1.5" required data-relation-rate>
|
||||
<input
|
||||
id="svc-fee-rate"
|
||||
type="number"
|
||||
name="fee_rate_percent"
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.0001"
|
||||
value="{{ $fixedFeeRatePercent ?? 1.5 }}"
|
||||
required
|
||||
data-relation-rate
|
||||
@if ($fixedFeeRatePercent !== null) readonly @endif
|
||||
>
|
||||
@if ($fixedFeeRatePercent !== null)
|
||||
<span class="svc-page__hint-text">客服服务公司固定 1.5%,由系统统一控制。</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="svc-field">
|
||||
<label for="svc-effective-from">生效日期</label>
|
||||
@ -1496,6 +1521,7 @@
|
||||
|
||||
const relationForm = relationDialog?.querySelector('[data-relation-form]');
|
||||
const relationStore = relationDialog?.querySelector('[data-relation-store]');
|
||||
const fixedFeeRate = relationForm?.dataset.fixedFeeRate || '';
|
||||
|
||||
function resetRelationForm() {
|
||||
if (!relationForm) return;
|
||||
@ -1503,7 +1529,8 @@
|
||||
relationForm.action = page.dataset.storeRoute;
|
||||
relationForm.querySelector('[data-relation-method]').value = 'POST';
|
||||
relationDialog.querySelector('[data-relation-dialog-title]').textContent = '新增店铺公司关系';
|
||||
relationForm.querySelector('[data-relation-rate]').value = '1.5';
|
||||
relationForm.querySelector('[data-relation-rate]').value = fixedFeeRate || '1.5';
|
||||
relationForm.querySelector('[data-relation-rate]').readOnly = fixedFeeRate !== '';
|
||||
relationForm.querySelector('[data-relation-from]').value = @json($isDetail ? now()->startOfMonth()->toDateString() : '');
|
||||
}
|
||||
|
||||
@ -1521,7 +1548,7 @@
|
||||
relationDialog.querySelector('[data-relation-dialog-title]').textContent = '编辑店铺公司关系';
|
||||
relationForm.querySelector('[data-relation-store]').value = button.dataset.storeKey;
|
||||
relationForm.querySelector('[data-relation-base]').value = button.dataset.baseMetric;
|
||||
relationForm.querySelector('[data-relation-rate]').value = button.dataset.feeRate;
|
||||
relationForm.querySelector('[data-relation-rate]').value = fixedFeeRate || button.dataset.feeRate;
|
||||
relationForm.querySelector('[data-relation-from]').value = button.dataset.effectiveFrom;
|
||||
relationForm.querySelector('[data-relation-to]').value = button.dataset.effectiveTo || '';
|
||||
relationForm.querySelector('[data-relation-contract-no]').value = button.dataset.contractNo || '';
|
||||
|
||||
@ -161,10 +161,11 @@ SQL);
|
||||
$relationId = (int) DB::connection('business_mirror')
|
||||
->table('svc_service_relations')
|
||||
->value('id');
|
||||
$this->patch(route('business.service-company-reconciliation.relations.update', [
|
||||
$updateRoute = route('business.service-company-reconciliation.relations.update', [
|
||||
'serviceCompany' => $serviceCompanyId,
|
||||
'relation' => $relationId,
|
||||
]), [
|
||||
]);
|
||||
$relationPayload = [
|
||||
'store_company_key' => $storeCompanyKey,
|
||||
'base_metric' => 'settlement_income',
|
||||
'fee_rate_percent' => 2,
|
||||
@ -173,7 +174,21 @@ SQL);
|
||||
'contract_no' => 'HT-TEST-001',
|
||||
'contract_status' => 'stamped',
|
||||
'status' => 'active',
|
||||
])->assertSessionHasNoErrors();
|
||||
];
|
||||
$this->withExceptionHandling()
|
||||
->patch($updateRoute, $relationPayload)
|
||||
->assertSessionHasErrors('fee_rate_percent');
|
||||
$this->assertSame(
|
||||
1,
|
||||
(int) DB::connection('business_mirror')
|
||||
->table('svc_service_relations')
|
||||
->where('id', $relationId)
|
||||
->value('rule_version'),
|
||||
);
|
||||
|
||||
$relationPayload['fee_rate_percent'] = 1.5;
|
||||
$this->patch($updateRoute, $relationPayload)
|
||||
->assertSessionHasNoErrors();
|
||||
|
||||
$this->assertSame(
|
||||
2,
|
||||
@ -198,10 +213,54 @@ SQL);
|
||||
->assertSeeText('常州测试客服服务有限公司')
|
||||
->assertSeeText('上海测试店铺公司有限公司')
|
||||
->assertSeeText('HT-TEST-001')
|
||||
->assertSeeText('2.0000%')
|
||||
->assertSeeText('1.5000%')
|
||||
->assertSeeText('客服服务公司结算比例固定为 1.5%')
|
||||
->assertSee('data-fixed-fee-rate="1.5"', false)
|
||||
->assertSeeText('同一店铺公司 + 同一服务类型');
|
||||
}
|
||||
|
||||
public function test_non_customer_service_company_keeps_configurable_settlement_rate(): void
|
||||
{
|
||||
$this->seedStoreMirror();
|
||||
$this->loginAs();
|
||||
$this->post(route('business.service-company-reconciliation.companies.store'), [
|
||||
'company_name' => '常州测试运营服务有限公司',
|
||||
'unified_social_credit_code' => '91320400TESTOPS02',
|
||||
'service_type_code' => 'operations',
|
||||
'company_status' => '正常',
|
||||
'status' => 'active',
|
||||
])->assertSessionHasNoErrors();
|
||||
|
||||
$serviceCompanyId = (int) DB::connection('business_mirror')
|
||||
->table('svc_service_companies')
|
||||
->where('unified_social_credit_code', '91320400TESTOPS02')
|
||||
->value('id');
|
||||
$storeCompanyKey = ServiceCompanyReconciliationRepository::companyKey(
|
||||
'上海测试店铺公司有限公司',
|
||||
'91310000TESTSTORE01',
|
||||
);
|
||||
$this->post(route('business.service-company-reconciliation.relations.store', [
|
||||
'serviceCompany' => $serviceCompanyId,
|
||||
]), [
|
||||
'store_company_key' => $storeCompanyKey,
|
||||
'base_metric' => 'settlement_income',
|
||||
'fee_rate_percent' => 2,
|
||||
'effective_from' => '2025-01-01',
|
||||
'effective_to' => '',
|
||||
'contract_no' => 'HT-TEST-OPERATIONS',
|
||||
'contract_status' => 'stamped',
|
||||
'status' => 'active',
|
||||
])->assertSessionHasNoErrors();
|
||||
|
||||
$this->assertSame(
|
||||
0.02,
|
||||
(float) DB::connection('business_mirror')
|
||||
->table('svc_service_relations')
|
||||
->where('service_company_id', $serviceCompanyId)
|
||||
->value('fee_rate'),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_same_store_company_and_service_type_cannot_have_overlapping_service_companies(): void
|
||||
{
|
||||
$this->seedStoreMirror();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user