diff --git a/app/Http/Controllers/Business/CourierAgreementSignatures.php b/app/Http/Controllers/Business/CourierAgreementSignatures.php index 9abc2d11..f916de00 100644 --- a/app/Http/Controllers/Business/CourierAgreementSignatures.php +++ b/app/Http/Controllers/Business/CourierAgreementSignatures.php @@ -523,6 +523,7 @@ class CourierAgreementSignatures extends Controller 'cooperation_party_a_phone' => ['required_if:document_type,cooperation-contract', 'string', 'max:64'], 'cooperation_party_b_name' => ['required_if:document_type,cooperation-contract', 'string', 'max:255'], 'cooperation_party_b_phone' => ['required_if:document_type,cooperation-contract', 'string', 'max:64'], + 'cooperation_party_b_signing_place' => ['nullable', 'string', 'max:100'], 'cooperation_contract_number' => ['nullable', 'string', 'max:100'], 'cooperation_party_b_legal_representative' => ['nullable', 'string', 'max:100'], 'cooperation_start_date' => ['required_if:document_type,cooperation-contract', 'date'], @@ -548,6 +549,7 @@ class CourierAgreementSignatures extends Controller unset($payload['esign_account'], $payload['idempotency_key'], $payload['operator_name'], $payload['credential_images']); $payload['authorization_items'] = array_values(array_map('strval', $data['authorization_items'] ?? [])); if (($data['document_type'] ?? '') === 'cooperation-contract' && ($data['partner_type'] ?? 'courier') === 'courier') { + $payload['cooperation_party_b_signing_place'] = trim((string) ($data['cooperation_party_b_signing_place'] ?? '')) ?: '常州市'; $payload['cooperation_contract_number'] = trim((string) ($data['cooperation_contract_number'] ?? '')); $payload['cooperation_party_b_legal_representative'] = ($data['counterparty_signer_type'] ?? 'organization') === 'person' ? null : trim((string) ($data['cooperation_party_b_legal_representative'] ?? '')); diff --git a/resources/views/business/pages/courier-payables/agreement-signing.blade.php b/resources/views/business/pages/courier-payables/agreement-signing.blade.php index f6993334..57609fae 100644 --- a/resources/views/business/pages/courier-payables/agreement-signing.blade.php +++ b/resources/views/business/pages/courier-payables/agreement-signing.blade.php @@ -1866,6 +1866,11 @@ +
默认常州市,可按实际签约地点修改;留空时仍使用常州市。
+代表人:
日 期:待填写
-签约地:常州市
+签约地:常州市
代表人:
日 期:{!! $mixedText($signatureDate) !!}
-签约地:常州市
+签约地:{!! $mixedText(trim((string) ($payload['cooperation_party_b_signing_place'] ?? '')) ?: '常州市') !!}
diff --git a/tests/Feature/Business/AgreementSigningPageTest.php b/tests/Feature/Business/AgreementSigningPageTest.php index 4fe9bc94..5ef2b834 100644 --- a/tests/Feature/Business/AgreementSigningPageTest.php +++ b/tests/Feature/Business/AgreementSigningPageTest.php @@ -507,6 +507,18 @@ class AgreementSigningPageTest extends FeatureTestCase ->assertSeeText('委托有效期:从'); } + public function test_cooperation_contract_has_an_editable_party_b_signing_place_and_preview(): void + { + $this->loginAs()->get(route('business.pages.show', [ + 'section' => 'courier-payables', 'page' => 'agreement-signing', 'agreement_view' => 'create', + ])) + ->assertOk() + ->assertSeeText('乙方签约地') + ->assertSee('name="cooperation_party_b_signing_place"', false) + ->assertSee('data-preview-bind="cooperationPartyBSigningPlace"', false) + ->assertSeeText('默认常州市,可按实际签约地点修改;留空时仍使用常州市。'); + } + public function testAgreementSigningPageRejectsUsersWithoutItsSidebarPermission(): void { $subject = 'user_' . $this->user->id; diff --git a/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php b/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php index 406c5505..5504ea18 100644 --- a/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php +++ b/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php @@ -545,6 +545,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase $payload = $this->cooperationContractPayload('cooperation-001'); $payload['cooperation_contract_number'] = ' KD-2026-009 '; $payload['cooperation_party_b_legal_representative'] = ' 王法人 '; + $payload['cooperation_party_b_signing_place'] = ' 南京市鼓楼区 '; $this->loginAs() ->post(route('business.courier-agreement-signatures.store'), $payload) @@ -559,6 +560,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase $this->assertSame('2026-12-31', $signature->payload['cooperation_end_date']); $this->assertSame('KD-2026-009', $signature->payload['cooperation_contract_number']); $this->assertSame('王法人', $signature->payload['cooperation_party_b_legal_representative']); + $this->assertSame('南京市鼓楼区', $signature->payload['cooperation_party_b_signing_place']); Http::assertSent(function (ClientRequest $request): bool { if ($request->url() !== 'https://smlopenapi.esign.test/v3/sign-flow/create-by-file') { @@ -585,8 +587,9 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase $payload = $this->cooperationContractPayload('contract-metadata-too-long'); $payload['cooperation_contract_number'] = str_repeat('X', 101); $payload['cooperation_party_b_legal_representative'] = str_repeat('法', 101); + $payload['cooperation_party_b_signing_place'] = str_repeat('地', 101); $this->loginAs()->post(route('business.courier-agreement-signatures.store'), $payload) - ->assertSessionHasErrors(['cooperation_contract_number', 'cooperation_party_b_legal_representative']); + ->assertSessionHasErrors(['cooperation_contract_number', 'cooperation_party_b_legal_representative', 'cooperation_party_b_signing_place']); $this->assertSame(0, CourierAgreementSignature::query()->count()); Http::assertNothingSent(); } @@ -595,6 +598,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase { $payload = $this->cooperationContractPayload('cooperation-font'); $payload['signature_date'] = '2026-10-10'; + $payload['cooperation_party_b_signing_place'] = '南京市 '; $signature = new CourierAgreementSignature([ 'business_no' => 'AGR-FONT-REGRESSION', 'template_code' => 'TPL-COOPERATION-CONTRACT-V1', @@ -635,11 +639,39 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase $this->assertStringContainsString('95%', $text); $this->assertStringContainsString('5000元/次', $text); $xpath = new DOMXPath($document); + $parties = $xpath->query('//table[@class="contract-signatures__table"]//td'); + $this->assertStringContainsString('签约地:常州市', $parties->item(0)->textContent); + $this->assertStringContainsString('签约地:南京市 ', $parties->item(1)->textContent); + $this->assertSame(0, $xpath->query('//body//script')->length); foreach ($xpath->query('//body//text()[not(ancestor::*[contains(concat(" ", normalize-space(@class), " "), " latin ")])]') as $node) { $this->assertDoesNotMatchRegularExpression('/[\x21-\x7E\x{2018}-\x{201F}]/u', $node->nodeValue); } } + /** @dataProvider missingOrBlankSigningPlaces */ + public function test_cooperation_contract_defaults_missing_or_blank_party_b_signing_place(?string $place): void + { + $this->fakeEsign(); + $payload = $this->cooperationContractPayload('signing-place-default'); + if ($place !== null) { + $payload['cooperation_party_b_signing_place'] = $place; + } + $this->loginAs()->post(route('business.courier-agreement-signatures.store'), $payload)->assertRedirect(); + $signature = CourierAgreementSignature::query()->firstOrFail(); + $this->assertSame('常州市', $signature->payload['cooperation_party_b_signing_place']); + + // Old saved payloads also retain the previous location when rendered. + $html = view('business.partials.cooperation-contract', [ + 'mode' => 'pdf', 'payload' => [], 'signatureDate' => '待填写', 'mixedText' => fn ($value) => e($value), + ])->render(); + $this->assertSame(2, substr_count($html, '签约地:常州市')); + } + + public static function missingOrBlankSigningPlaces(): array + { + return ['missing' => [null], 'empty' => [''], 'whitespace' => [' ']]; + } + public function test_cooperation_contract_uses_the_persons_real_name_instead_of_the_courier_name(): void { $this->fakeEsign(); diff --git a/tests/Unit/CourierAgreementSignatureLayoutTest.php b/tests/Unit/CourierAgreementSignatureLayoutTest.php index 2face374..0db6e773 100644 --- a/tests/Unit/CourierAgreementSignatureLayoutTest.php +++ b/tests/Unit/CourierAgreementSignatureLayoutTest.php @@ -69,6 +69,7 @@ class CourierAgreementSignatureLayoutTest extends TestCase 'contract_partner_name' => '示例合作公司', 'shared_courier_names' => ['示例快递'], 'cooperation_party_a_name' => '示例甲方公司', 'cooperation_party_a_phone' => '00000000000', 'cooperation_party_b_name' => '示例乙方公司', 'cooperation_party_b_phone' => '00000000000', + 'cooperation_party_b_signing_place' => '江苏省南京市鼓楼区', 'cooperation_start_date' => '2026-10-11', 'cooperation_end_date' => '2027-10-10', 'principal_name' => '示例委托方', 'principal_id_number' => 'EXAMPLE-ID', 'trustee_name' => '示例受托方', 'trustee_id_number' => 'EXAMPLE-ID',