修复合作合同双方 E 签宝个人实名信息 #285
@ -63,7 +63,10 @@ class CourierAgreementSignatures extends Controller
|
||||
$request->merge(['counterparty_signer_type' => 'organization']);
|
||||
}
|
||||
|
||||
$data = $request->validate($this->rules());
|
||||
$data = $request->validate($this->rules(), [
|
||||
'operator_name.required_if' => '请填写快递方经办人的 E 签宝实名认证姓名。',
|
||||
'secondary_operator_name.required_if' => '请填写我方 E 签宝账号绑定的实名认证姓名。',
|
||||
]);
|
||||
$type = $data['document_type'];
|
||||
if ($type === 'authorization' && count($data['authorization_items'] ?? []) === 0) {
|
||||
throw ValidationException::withMessages(['authorization_items' => '授权委托书至少选择一项授权事项。']);
|
||||
@ -100,6 +103,17 @@ class CourierAgreementSignatures extends Controller
|
||||
$data['operator_name'] = trim((string) $data['counterparty_organization_name']);
|
||||
}
|
||||
}
|
||||
if ($type === 'cooperation-contract') {
|
||||
$companyNames = [trim($data['cooperation_party_a_name']), trim($data['cooperation_party_b_name'])];
|
||||
foreach (['operator_name' => '快递方', 'secondary_operator_name' => '我方'] as $field => $party) {
|
||||
$data[$field] = trim($data[$field]);
|
||||
if (in_array($data[$field], $companyNames, true)) {
|
||||
throw ValidationException::withMessages([
|
||||
$field => "{$party}实名认证姓名应填写账号绑定的个人姓名,不能填写甲乙方公司名称。",
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->validateEsignAccount($data['esign_account'], 'esign_account');
|
||||
if (! empty($data['secondary_esign_account'])) {
|
||||
$this->validateEsignAccount($data['secondary_esign_account'], 'secondary_esign_account');
|
||||
@ -303,7 +317,8 @@ class CourierAgreementSignatures extends Controller
|
||||
'signing_deadline' => $date,
|
||||
'esign_account' => ['required', 'string', 'max:255'],
|
||||
'secondary_esign_account' => ['required_if:document_type,entity-change,cooperation-contract,authorization', 'nullable', 'string', 'max:255'],
|
||||
'operator_name' => ['nullable', 'string', 'min:2', 'max:100'],
|
||||
'operator_name' => ['required_if:document_type,cooperation-contract', 'nullable', 'string', 'min:2', 'max:100'],
|
||||
'secondary_operator_name' => ['required_if:document_type,cooperation-contract', 'nullable', 'string', 'min:2', 'max:100'],
|
||||
'counterparty_organization_name' => ['required_if:document_type,account-proof,account-change', 'nullable', 'string', 'min:2', 'max:255'],
|
||||
'counterparty_signer_type' => ['required_if:document_type,account-proof,account-change', 'nullable', 'in:organization,person'],
|
||||
'archive_mode' => ['required', 'in:auto,manual'],
|
||||
|
||||
@ -307,13 +307,13 @@ final class CourierAgreementSignatureService
|
||||
[
|
||||
'signer_type' => 1,
|
||||
'organization_name' => $payload['cooperation_party_a_name'] ?? $signature->company_full_name,
|
||||
'operator_name' => $payload['cooperation_party_a_name'] ?? $signature->company_full_name,
|
||||
'operator_name' => $payload['secondary_operator_name'],
|
||||
'operator_account' => $payload['secondary_esign_account'] ?? $signature->operator_account,
|
||||
],
|
||||
[
|
||||
'signer_type' => 1,
|
||||
'organization_name' => $payload['cooperation_party_b_name'] ?? $signature->courier_name,
|
||||
'operator_name' => $signature->operator_name ?: ($payload['cooperation_party_b_name'] ?? $signature->courier_name),
|
||||
'operator_name' => $signature->operator_name,
|
||||
'operator_account' => $signature->operator_account,
|
||||
],
|
||||
];
|
||||
|
||||
@ -1528,6 +1528,11 @@
|
||||
<input class="agreement-control" id="agreement-secondary-esign-account" name="secondary_esign_account" type="text" placeholder="填写另一签署方的手机号或邮箱" autocomplete="email" disabled>
|
||||
<p class="agreement-help">主体变更、合作合同和授权委托书需要双方分别实名签署;合作合同统一使用我方账号 {{ $agreementCooperationSecondaryEsignAccount }}。</p>
|
||||
</div>
|
||||
<div class="agreement-field" data-cooperation-operator-field hidden>
|
||||
<label for="agreement-secondary-operator-name">我方 E 签宝实名认证姓名 <span class="agreement-required" aria-hidden="true">*</span></label>
|
||||
<input class="agreement-control" id="agreement-secondary-operator-name" name="secondary_operator_name" type="text" placeholder="填写我方账号绑定的个人真实姓名" autocomplete="name" disabled>
|
||||
<p class="agreement-help">双方实名认证姓名均填写各自账号绑定的个人姓名;甲乙方公司名称在下方合同资料中填写。</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -2004,6 +2009,8 @@
|
||||
var accountSignerHelp = root.querySelector('[data-account-signer-help]');
|
||||
var operatorNameInput = root.querySelector('[data-account-operator-name]');
|
||||
var operatorRequiredMark = root.querySelector('[data-account-operator-required]');
|
||||
var cooperationOperatorField = root.querySelector('[data-cooperation-operator-field]');
|
||||
var secondaryOperatorInput = root.querySelector('input[name="secondary_operator_name"]');
|
||||
var sharedCourierList = root.querySelector('[data-shared-courier-list]');
|
||||
var sharedCourierAdd = root.querySelector('[data-shared-courier-add]');
|
||||
var quoteVersionField = root.querySelector('[data-quote-confirmation-field]');
|
||||
@ -2704,6 +2711,7 @@
|
||||
var courier = fieldValue('[name="courier_name"]', '快递公司');
|
||||
var companyName = fieldValue('[name="company_full_name"]', '待填写公司全称');
|
||||
var accountAgreement = templateType === 'account-proof' || templateType === 'account-change';
|
||||
var cooperationContract = templateType === 'cooperation-contract';
|
||||
var accountSignerTypeInput = accountSignerTypeInputs.find(function (input) {
|
||||
return input.checked;
|
||||
});
|
||||
@ -2724,14 +2732,20 @@
|
||||
: '填写对方在 E 签宝认证的企业全称';
|
||||
}
|
||||
if (operatorNameInput) {
|
||||
operatorNameInput.disabled = !accountAgreement || accountSignerIsPerson;
|
||||
operatorNameInput.required = accountAgreement && !accountSignerIsPerson;
|
||||
if (accountSignerIsPerson) {
|
||||
var needsOperatorName = cooperationContract || (accountAgreement && !accountSignerIsPerson);
|
||||
operatorNameInput.disabled = !needsOperatorName;
|
||||
operatorNameInput.required = needsOperatorName;
|
||||
if (accountAgreement && accountSignerIsPerson) {
|
||||
operatorNameInput.value = accountOrganizationInput ? accountOrganizationInput.value : '';
|
||||
}
|
||||
}
|
||||
if (operatorRequiredMark) {
|
||||
operatorRequiredMark.hidden = !accountAgreement || accountSignerIsPerson;
|
||||
operatorRequiredMark.hidden = !operatorNameInput.required;
|
||||
}
|
||||
if (cooperationOperatorField) {
|
||||
cooperationOperatorField.hidden = !cooperationContract;
|
||||
secondaryOperatorInput.disabled = !cooperationContract;
|
||||
secondaryOperatorInput.required = cooperationContract;
|
||||
}
|
||||
if (accountSignerLabel) {
|
||||
accountSignerLabel.textContent = accountSignerIsPerson ? '对方签署个人姓名' : '对方签署企业全称';
|
||||
|
||||
@ -93,6 +93,8 @@ class AgreementSigningPageTest extends FeatureTestCase
|
||||
->assertSee("setPreviewValue('proofSigner', '公司盖章')", false)
|
||||
->assertSee("setPreviewValue('changeSigner', '公司盖章')", false)
|
||||
->assertSeeText('合作合同统一使用我方账号 18052733960')
|
||||
->assertSeeText('我方 E 签宝实名认证姓名')
|
||||
->assertSee('name="secondary_operator_name"', false)
|
||||
->assertSeeText('填写变量')
|
||||
->assertSeeText('签署完成后自动归档')
|
||||
->assertSeeText('人工确认后归档')
|
||||
|
||||
@ -181,25 +181,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
|
||||
public function test_cooperation_contract_maps_party_variables_and_creates_two_signers(): void
|
||||
{
|
||||
$this->fakeEsign();
|
||||
$payload = [
|
||||
'idempotency_key' => 'cooperation-001',
|
||||
'courier_name' => '新北中通',
|
||||
'document_type' => 'cooperation-contract',
|
||||
'signing_subject' => $this->company->name,
|
||||
'company_full_name' => $this->company->name,
|
||||
'signature_date' => '2026-09-21',
|
||||
'signing_deadline' => now()->addDays(7)->toDateString(),
|
||||
'esign_account' => '13800138000',
|
||||
'secondary_esign_account' => '13900139000',
|
||||
'operator_name' => '张经理',
|
||||
'archive_mode' => 'auto',
|
||||
'cooperation_party_a_name' => $this->company->name,
|
||||
'cooperation_party_a_phone' => '0519-88886666',
|
||||
'cooperation_party_b_name' => '新北中通快递有限公司',
|
||||
'cooperation_party_b_phone' => '18112896651',
|
||||
'cooperation_start_date' => '2026-01-01',
|
||||
'cooperation_end_date' => '2026-12-31',
|
||||
];
|
||||
$payload = $this->cooperationContractPayload('cooperation-001');
|
||||
|
||||
$this->loginAs()
|
||||
->post(route('business.courier-agreement-signatures.store'), $payload)
|
||||
@ -209,6 +191,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
|
||||
$this->assertSame('cooperation-contract', $signature->document_type);
|
||||
$this->assertSame('TPL-COOPERATION-CONTRACT-V1', $signature->template_code);
|
||||
$this->assertSame('18052733960', $signature->payload['secondary_esign_account']);
|
||||
$this->assertSame('李经理', $signature->payload['secondary_operator_name']);
|
||||
$this->assertSame('0519-88886666', $signature->payload['cooperation_party_a_phone']);
|
||||
$this->assertSame('2026-12-31', $signature->payload['cooperation_end_date']);
|
||||
|
||||
@ -223,10 +206,41 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
|
||||
return count($signers) === 2
|
||||
&& ($signers[0]['orgSignerInfo']['orgName'] ?? null) === $this->company->name
|
||||
&& ($signers[0]['orgSignerInfo']['transactorInfo']['psnAccount'] ?? null) === '18052733960'
|
||||
&& ($signers[1]['orgSignerInfo']['orgName'] ?? null) === '新北中通快递有限公司';
|
||||
&& ($signers[0]['orgSignerInfo']['transactorInfo']['psnInfo']['psnName'] ?? null) === '李经理'
|
||||
&& ($signers[1]['orgSignerInfo']['orgName'] ?? null) === '新北中通快递有限公司'
|
||||
&& ($signers[1]['orgSignerInfo']['transactorInfo']['psnAccount'] ?? null) === '13800138000'
|
||||
&& ($signers[1]['orgSignerInfo']['transactorInfo']['psnInfo']['psnName'] ?? null) === '张经理';
|
||||
});
|
||||
}
|
||||
|
||||
public function test_cooperation_contract_requires_both_real_names_before_creating_a_task(): void
|
||||
{
|
||||
$this->fakeEsign();
|
||||
foreach (['operator_name', 'secondary_operator_name'] as $field) {
|
||||
$payload = $this->cooperationContractPayload('missing-' . $field);
|
||||
$payload[$field] = ' ';
|
||||
$this->withExceptionHandling()->loginAs()
|
||||
->post(route('business.courier-agreement-signatures.store'), $payload)
|
||||
->assertSessionHasErrors($field);
|
||||
}
|
||||
$this->assertSame(0, CourierAgreementSignature::query()->count());
|
||||
Http::assertNothingSent();
|
||||
}
|
||||
|
||||
public function test_cooperation_contract_rejects_company_names_as_personal_names(): void
|
||||
{
|
||||
$this->fakeEsign();
|
||||
foreach (['operator_name' => 'cooperation_party_b_name', 'secondary_operator_name' => 'cooperation_party_a_name'] as $field => $companyField) {
|
||||
$payload = $this->cooperationContractPayload('company-' . $field);
|
||||
$payload[$field] = $payload[$companyField];
|
||||
$this->withExceptionHandling()->loginAs()
|
||||
->post(route('business.courier-agreement-signatures.store'), $payload)
|
||||
->assertSessionHasErrors($field);
|
||||
}
|
||||
$this->assertSame(0, CourierAgreementSignature::query()->count());
|
||||
Http::assertNothingSent();
|
||||
}
|
||||
|
||||
public function test_shared_contract_accepts_multiple_couriers_and_preserves_note_in_pdf(): void
|
||||
{
|
||||
$this->fakeEsign();
|
||||
@ -716,6 +730,30 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
|
||||
];
|
||||
}
|
||||
|
||||
private function cooperationContractPayload(string $idempotencyKey): array
|
||||
{
|
||||
return [
|
||||
'idempotency_key' => $idempotencyKey,
|
||||
'courier_name' => '新北中通',
|
||||
'document_type' => 'cooperation-contract',
|
||||
'signing_subject' => $this->company->name,
|
||||
'company_full_name' => $this->company->name,
|
||||
'signature_date' => '2026-09-21',
|
||||
'signing_deadline' => now()->addDays(7)->toDateString(),
|
||||
'esign_account' => '13800138000',
|
||||
'secondary_esign_account' => '13900139000',
|
||||
'operator_name' => '张经理',
|
||||
'secondary_operator_name' => '李经理',
|
||||
'archive_mode' => 'auto',
|
||||
'cooperation_party_a_name' => $this->company->name,
|
||||
'cooperation_party_a_phone' => '0519-88886666',
|
||||
'cooperation_party_b_name' => '新北中通快递有限公司',
|
||||
'cooperation_party_b_phone' => '18112896651',
|
||||
'cooperation_start_date' => '2026-01-01',
|
||||
'cooperation_end_date' => '2026-12-31',
|
||||
];
|
||||
}
|
||||
|
||||
private function accountChangePayload(string $idempotencyKey): array
|
||||
{
|
||||
return [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user