From e44606f1d6986633cfd86a2a906ff51751432015 Mon Sep 17 00:00:00 2001 From: limeng <1131177064@qq.com> Date: Mon, 21 Sep 2026 09:56:01 +0800 Subject: [PATCH] fix: make signature dates explicit in signing PDFs --- .../Business/CourierAgreementSignatures.php | 5 +- .../Business/CourierQuoteSignatures.php | 13 +++- app/Support/CourierAgreementPdf.php | 11 ++- app/Support/CourierQuotePdf.php | 5 +- app/Support/CourierQuoteSignatureService.php | 6 +- .../agreement-signing.blade.php | 24 +++--- .../quote-change-show.blade.php | 3 +- .../partials/cooperation-contract.blade.php | 6 +- .../business/pdf/courier-agreement.blade.php | 12 +-- .../business/pdf/courier-quote.blade.php | 2 +- .../CourierAgreementSignatureWorkflowTest.php | 78 +++++++++++++++++++ .../CourierQuoteSignatureWorkflowTest.php | 51 +++++++++++- 12 files changed, 181 insertions(+), 35 deletions(-) diff --git a/app/Http/Controllers/Business/CourierAgreementSignatures.php b/app/Http/Controllers/Business/CourierAgreementSignatures.php index b6e27238..13e908ce 100644 --- a/app/Http/Controllers/Business/CourierAgreementSignatures.php +++ b/app/Http/Controllers/Business/CourierAgreementSignatures.php @@ -278,6 +278,7 @@ class CourierAgreementSignatures extends Controller 'quote_version_id' => ['required_if:document_type,quote-confirmation', 'nullable', 'integer', 'min:1'], 'signing_subject' => ['required', 'string', 'min:2', 'max:255'], 'company_full_name' => ['required', 'string', 'min:2', 'max:255'], + 'signature_date' => ['required', 'date'], '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'], @@ -299,12 +300,10 @@ class CourierAgreementSignatures extends Controller 'entity_change_date' => ['required_if:document_type,entity-change', 'date'], 'changed_company_name' => ['required_if:document_type,entity-change', 'string', 'max:255'], 'changed_company_start_date' => ['required_if:document_type,entity-change', 'date'], - 'entity_proof_date' => ['required_if:document_type,entity-change', 'date'], 'contract_partner_name' => ['required_if:document_type,shared-contract', 'string', 'max:255'], 'shared_courier_names' => ['nullable', 'array', 'min:1'], 'shared_courier_names.*' => ['required', 'string', 'max:255'], 'shared_courier_name' => ['nullable', 'string', 'max:255'], - 'shared_proof_date' => ['nullable', 'date'], 'shared_note' => ['nullable', 'string', 'max:2000'], 'cooperation_party_a_name' => ['required_if:document_type,cooperation-contract', 'string', 'max:255'], 'cooperation_party_a_phone' => ['required_if:document_type,cooperation-contract', 'string', 'max:64'], @@ -342,7 +341,7 @@ class CourierAgreementSignatures extends Controller ->findOrFail((int) $data['quote_version_id']); $this->ensureQuoteVersionMatchesCourier($version, $data['courier_name']); - $signature = $this->quoteSignatureService->prepareVersion($version, user_id()); + $signature = $this->quoteSignatureService->prepareVersion($version, user_id(), $data['signature_date']); $account = trim((string) $data['esign_account']); $signature = $this->quoteSignatureService->send($signature, [ 'organization_name' => $version->carrier_name, diff --git a/app/Http/Controllers/Business/CourierQuoteSignatures.php b/app/Http/Controllers/Business/CourierQuoteSignatures.php index 98fb9a7d..bc9c31dc 100644 --- a/app/Http/Controllers/Business/CourierQuoteSignatures.php +++ b/app/Http/Controllers/Business/CourierQuoteSignatures.php @@ -19,11 +19,14 @@ class CourierQuoteSignatures extends Controller { } - public function prepare(CourierQuoteVersion $quoteVersion): RedirectResponse + public function prepare(Request $request, CourierQuoteVersion $quoteVersion): RedirectResponse { $this->ensureCanManage(); abort_unless($quoteVersion->status === 'approved', 422, '只有已审核报价版本可以生成签署文件。'); - $signature = $this->service->prepareVersion($quoteVersion, user_id()); + $data = $request->validate([ + 'signature_date' => ['required', 'date'], + ]); + $signature = $this->service->prepareVersion($quoteVersion, user_id(), $data['signature_date']); return redirect()->route('business.courier-quote-changes.show', $quoteVersion->source_change_id) ->with('success', "{$quoteVersion->version_code} 的签署 PDF 已生成,文件校验值已固定。") @@ -40,9 +43,15 @@ class CourierQuoteSignatures extends Controller 'operator_name' => ['required', 'string', 'min:2', 'max:100'], 'operator_account' => ['required', 'string', 'max:255'], 'notice_type' => ['required', 'in:1,2'], + 'signature_date' => ['required', 'date'], 'expires_at' => ['required', 'date', 'after:now', 'before_or_equal:' . now()->addDays(90)->toDateTimeString()], ]); $this->validateAccountForNotice($data['operator_account'], $data['notice_type']); + $quoteSignature = $this->service->prepareVersion( + $quoteSignature->version, + user_id(), + $data['signature_date'], + ); $signature = $this->service->send($quoteSignature, $data, user_id()); $environment = $signature->environment === 'sandbox' ? '沙箱' : '正式环境'; diff --git a/app/Support/CourierAgreementPdf.php b/app/Support/CourierAgreementPdf.php index d64ae502..8629db0d 100644 --- a/app/Support/CourierAgreementPdf.php +++ b/app/Support/CourierAgreementPdf.php @@ -3,17 +3,26 @@ namespace App\Support; use App\Models\Business\CourierAgreementSignature; +use Illuminate\Support\Carbon; use RuntimeException; final class CourierAgreementPdf { public function render(CourierAgreementSignature $signature): string { + $payload = $signature->payload ?? []; + $signatureDate = (string) ($payload['signature_date'] ?? $payload['entity_proof_date'] ?? $payload['shared_proof_date'] ?? ''); + try { + $signatureDate = $signatureDate !== '' ? Carbon::parse($signatureDate)->format('Y年n月j日') : '待填写'; + } catch (\Throwable) { + $signatureDate = '待填写'; + } $html = view('business.pdf.courier-agreement', [ 'signature' => $signature, 'type' => $signature->document_type, 'title' => $this->title($signature), - 'payload' => $signature->payload ?? [], + 'payload' => $payload, + 'signatureDate' => $signatureDate, 'fontPath' => str_replace('\\', '/', resource_path('fonts/DroidSansFallback.ttf')), ])->render(); diff --git a/app/Support/CourierQuotePdf.php b/app/Support/CourierQuotePdf.php index b85b85dc..04c267de 100644 --- a/app/Support/CourierQuotePdf.php +++ b/app/Support/CourierQuotePdf.php @@ -3,6 +3,7 @@ namespace App\Support; use App\Models\Business\CourierQuoteVersion; +use Illuminate\Support\Carbon; use RuntimeException; final class CourierQuotePdf @@ -11,9 +12,10 @@ final class CourierQuotePdf { } - public function render(CourierQuoteVersion $version): string + public function render(CourierQuoteVersion $version, ?string $signatureDate = null): string { $data = $this->confirmation->data($version); + $signatureDate = Carbon::parse($signatureDate ?: now())->format('Y年n月j日'); $html = view('business.pdf.courier-quote', [ 'version' => $version, 'change' => $version->change, @@ -23,6 +25,7 @@ final class CourierQuotePdf 'expiryDate' => $data['expiry_date'], 'faceFee' => $data['face_fee'], 'bubbleCoefficient' => $data['bubble_coefficient'], + 'signatureDate' => $signatureDate, 'generatedAt' => now(), 'fontPath' => str_replace('\\', '/', resource_path('fonts/DroidSansFallback.ttf')), ])->render(); diff --git a/app/Support/CourierQuoteSignatureService.php b/app/Support/CourierQuoteSignatureService.php index 72f60bae..c9bf7b2e 100644 --- a/app/Support/CourierQuoteSignatureService.php +++ b/app/Support/CourierQuoteSignatureService.php @@ -18,11 +18,11 @@ final class CourierQuoteSignatureService { } - public function prepareVersion(CourierQuoteVersion $version, int $userId): CourierQuoteSignature + public function prepareVersion(CourierQuoteVersion $version, int $userId, ?string $signatureDate = null): CourierQuoteSignature { $existing = $version->signature; - if ($existing && $this->sourcePdfIsIntact($existing)) { + if ($signatureDate === null && $existing && $this->sourcePdfIsIntact($existing)) { return $existing; } @@ -32,7 +32,7 @@ final class CourierQuoteSignatureService ]); } - $bytes = $this->pdf->render($version); + $bytes = $this->pdf->render($version, $signatureDate); $path = $this->path($version, 'source.pdf'); $sha256 = hash('sha256', $bytes); 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 562ab5c1..8db7a0f6 100644 --- a/resources/views/business/pages/courier-payables/agreement-signing.blade.php +++ b/resources/views/business/pages/courier-payables/agreement-signing.blade.php @@ -1394,6 +1394,11 @@ +
+ + +

该日期会直接写入待签文件,并随原文一起推送到 E 签宝。

+
@@ -1499,10 +1504,6 @@
-
- - -
-

日期:签署后生成

+

日期:{{ now()->format('Y年n月j日') }}

第 1 页 / 共 1 页

@@ -1745,7 +1746,7 @@

双方确认以下报价版本作为快递服务结算依据。报价版本一经签署即形成独立留档,后续变价须生成新版本并重新签署。

请选择报价版本后查看报价明细
-

乙方(快递方)签章:

日期:签署后生成

+

乙方(快递方)签章:

日期:{{ now()->format('Y年n月j日') }}

报价确认单 · 快递方单方签署

@@ -2421,7 +2422,7 @@ setText('[data-preview-template-code]', documentOption.dataset.code); setPreviewValue('company', companyName); setPreviewValue('courier', courier); - setPreviewValue('signatureDate', '签署后生成'); + setPreviewValue('signatureDate', formatDateField('[name="signature_date"]', '待填写')); if (templateType === 'account-proof') { setPreviewValue('proofEffectiveDate', formatDateField('[name="account_proof_effective_date"]', '待填写')); @@ -2450,7 +2451,6 @@ setPreviewValue('entityChangeDate', formatDateField('[name="entity_change_date"]', '待填写')); setPreviewValue('changedCompanyName', fieldValue('[name="changed_company_name"]', '待填写')); setPreviewValue('changedCompanyStartDate', formatDateField('[name="changed_company_start_date"]', '待填写')); - setPreviewValue('entityProofDate', formatDateField('[name="entity_proof_date"]', '待填写')); } if (templateType === 'shared-contract') { diff --git a/resources/views/business/pages/courier-payables/quote-change-show.blade.php b/resources/views/business/pages/courier-payables/quote-change-show.blade.php index 121a1f04..ee8a4082 100644 --- a/resources/views/business/pages/courier-payables/quote-change-show.blade.php +++ b/resources/views/business/pages/courier-payables/quote-change-show.blade.php @@ -72,7 +72,7 @@
@if(!$quoteSignature)
该历史报价版本尚未生成签署原文。
- @if($canManageSignature)
@csrf
@endif + @if($canManageSignature)
@csrf
日期将直接写入待签原文。
@endif @else
签署状态
{{ $quoteSignature->statusLabel() }}
@@ -97,6 +97,7 @@
+
diff --git a/resources/views/business/partials/cooperation-contract.blade.php b/resources/views/business/partials/cooperation-contract.blade.php index 6ea5e17a..78f69095 100644 --- a/resources/views/business/partials/cooperation-contract.blade.php +++ b/resources/views/business/partials/cooperation-contract.blade.php @@ -128,10 +128,10 @@

乙 方:待填写

代表人:

代表人:

-

日 期:签署后生成

-

日 期:签署后生成

+

日 期:待填写

+

日 期:待填写

签约地:常州市

@else -

甲 方:{!! $mixedText($payload['cooperation_party_a_name'] ?? '') !!}

乙 方:{!! $mixedText($payload['cooperation_party_b_name'] ?? '') !!}

代表人:

代表人:

日 期:签署后生成

日 期:签署后生成

签约地:常州市

+

甲 方:{!! $mixedText($payload['cooperation_party_a_name'] ?? '') !!}

乙 方:{!! $mixedText($payload['cooperation_party_b_name'] ?? '') !!}

代表人:

代表人:

日 期:{!! $mixedText($signatureDate) !!}

日 期:{!! $mixedText($signatureDate) !!}

签约地:常州市

@endif diff --git a/resources/views/business/pdf/courier-agreement.blade.php b/resources/views/business/pdf/courier-agreement.blade.php index dd0f2bfb..c635d90a 100644 --- a/resources/views/business/pdf/courier-agreement.blade.php +++ b/resources/views/business/pdf/courier-agreement.blade.php @@ -10,7 +10,7 @@ h1 { text-align: center; font-size: 20px; margin: 0 0 24px; } h2 { font-size: 14px; margin: 18px 0 8px; } h4 { font-size: 14px; margin: 18px 0 8px; } - p { margin: 0 0 12px; } + p { margin: 0 0 12px; overflow-wrap: anywhere; word-break: break-all; } .meta { color: #595959; font-size: 11px; text-align: right; margin-bottom: 20px; } .account { margin: 14px 0 20px 28px; } .account div { margin: 6px 0; } @@ -57,7 +57,7 @@

请贵公司按照上述汇款资料付款,如因我司提供收款银行帐户资料错误,导致相关款项支付不到位,则由此产生的一切经济纠纷和责任由我司承担。我司承诺若上述收款银行帐户资料发生变化及时书面通知贵公司。

以上收款账户的变更,以我司的盖章为有效。

-

公司盖章:

日期:签署后生成

+

公司盖章:

日期:{!! $mixedText($signatureDate) !!}

@elseif ($type === 'account-change')

TO {!! $mixedText($payload['company_full_name']) !!}:

基于我公司正规化管理的需要,同时保证广大客户的汇款安全,我司收款账户将于 {!! $mixedText($payload['account_change_date']) !!} 变更账户,之前相应的账户(收款账户为:{!! $mixedText($payload['old_account_name']) !!},账号:{!! $mixedText($payload['old_account_number']) !!})于 {!! $mixedText($payload['old_account_stop_date']) !!} 停止使用,即日起我司收款的银行帐户进行变更,自 {!! $mixedText($payload['new_account_effective_date']) !!} 起我司与贵司往来款项请汇款至以下新账户,新汇款资料如下:

@@ -67,20 +67,20 @@
银行账户:{!! $mixedText($payload['new_bank_account']) !!}

如因我司提供收款银行帐户资料错误导致相关款项支付不到位,由此产生的一切经济纠纷和责任由我司承担。以上收款账户的变更,以我司的盖章为有效。

-

公司盖章:

日期:签署后生成

+

公司盖章:

日期:{!! $mixedText($signatureDate) !!}

@elseif ($type === 'entity-change')

致 {!! $mixedText($payload['company_full_name']) !!}:

我司由于企业名称变更,原公司名称 {!! $mixedText($payload['former_company_name']) !!} 于 {!! $mixedText($payload['entity_change_date']) !!} 变更为 {!! $mixedText($payload['changed_company_name']) !!},自 {!! $mixedText($payload['changed_company_start_date']) !!} 开始,我司将启用变更后的公司与贵公司开展工作,原公司名称停止使用。

变更后,原公司的各项事宜由变更后的公司继续执行。原公司的结算未付款项,由变更之后的公司提供的收款账户收款。原公司的业务和款项若出现法律上的纠纷,由变更后的公司承担责任。

特此证明!

-

变更前公司法人签字盖公章:{!! $mixedText($payload['former_company_name']) !!}

变更后公司法人签字盖公章:{!! $mixedText($payload['changed_company_name']) !!}

日期:{!! $mixedText($payload['entity_proof_date']) !!}

+

变更前公司法人签字盖公章:{!! $mixedText($payload['former_company_name']) !!}

变更后公司法人签字盖公章:{!! $mixedText($payload['changed_company_name']) !!}

日期:{!! $mixedText($signatureDate) !!}

@elseif ($type === 'shared-contract')

与 {!! $mixedText($payload['contract_partner_name'] ?? '') !!} 合作的 {!! $mixedText($sharedCourierText) !!} 共同用一份合同,打款资料为同一份资料,同一个签字人以及同一个法人!

特此证明!

@if (filled($payload['shared_note'] ?? null))

备注:{!! $mixedText($payload['shared_note']) !!}

@endif -

法人签字并公司盖章:

日期:{!! $mixedText($payload['shared_proof_date'] ?? '签署后生成') !!}

+

法人签字并公司盖章:

日期:{!! $mixedText($signatureDate) !!}

@elseif ($type === 'cooperation-contract') @include('business.partials.cooperation-contract', ['mode' => 'pdf', 'payload' => $payload]) @else @@ -98,7 +98,7 @@
  • 代表委托方代收 {!! $mixedText($payload['payment_counterparty']) !!} 的往来货款。
  • 委托有效期:从 {!! $mixedText($payload['authorization_start_date']) !!} 至 {!! $mixedText($payload['authorization_end_date']) !!} 止。

    -

    委托方:{!! $mixedText($payload['principal_name']) !!}

    受托方:{!! $mixedText($payload['trustee_name']) !!}

    日期:签署后生成

    +

    委托方:{!! $mixedText($payload['principal_name']) !!}

    受托方:{!! $mixedText($payload['trustee_name']) !!}

    日期:{!! $mixedText($signatureDate) !!}

    @endif diff --git a/resources/views/business/pdf/courier-quote.blade.php b/resources/views/business/pdf/courier-quote.blade.php index 6e6cb29a..f2bd000e 100644 --- a/resources/views/business/pdf/courier-quote.blade.php +++ b/resources/views/business/pdf/courier-quote.blade.php @@ -134,7 +134,7 @@

    签署确认

    报价版本:{!! $mixedText($version->version_code) !!} 快递公司:{!! $mixedText($version->carrier_name) !!} 执行日期:{!! $mixedText($version->effective_date?->toDateString()) !!}
    -
    乙方(快递方)签章:{!! $mixedText($version->carrier_name) !!}

    签署日期:
    +
    乙方(快递方)签章:{!! $mixedText($version->carrier_name) !!}

    签署日期:{!! $mixedText($signatureDate) !!}
    生成时间:{!! $mixedText($generatedAt->format('Y-m-d H:i:s')) !!} · 版本 {!! $mixedText($version->version_code) !!}
    diff --git a/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php b/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php index 652075cc..34764fe8 100644 --- a/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php +++ b/tests/Feature/Business/CourierAgreementSignatureWorkflowTest.php @@ -49,6 +49,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase $this->assertSame(CourierAgreementSignature::STATUS_SIGNING, $signature->status); $this->assertSame('flow-agreement-001', $signature->esign_sign_flow_id); $this->assertSame('新北中通快递有限公司', $signature->organization_name); + $this->assertSame('2026-09-21', $signature->payload['signature_date']); Storage::disk('uploads')->assertExists($signature->source_pdf_path); $this->assertSame('新北中通快递有限公司', $signature->payload['account_proof_account_name']); @@ -101,6 +102,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase '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', @@ -151,6 +153,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase 'document_type' => 'shared-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', 'operator_name' => '张经理', @@ -232,6 +235,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase 'quote_version_id' => $version->id, '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', 'operator_name' => '李经理', @@ -261,6 +265,79 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase }); } + public function test_signature_date_is_required_for_every_signing_request(): void + { + $payload = $this->accountProofPayload('missing-signature-date'); + unset($payload['signature_date']); + + $this->withExceptionHandling()->loginAs() + ->post(route('business.courier-agreement-signatures.store'), $payload) + ->assertSessionHasErrors('signature_date'); + + $this->assertDatabaseCount('business_courier_agreement_signatures', 0); + } + + public function test_all_agreement_templates_render_the_selected_signature_date(): void + { + $payload = [ + 'company_full_name' => $this->company->name, + 'signature_date' => '2026-09-21', + 'account_proof_effective_date' => '2026-09-01', + 'account_proof_account_name' => '测试收款人', + 'account_proof_bank_name' => '测试银行', + 'account_proof_bank_account' => '6222000012345678', + 'account_change_date' => '2026-09-01', + 'old_account_name' => '旧账户', + 'old_account_number' => '6222000011111111', + 'old_account_stop_date' => '2026-09-01', + 'new_account_effective_date' => '2026-09-02', + 'new_account_name' => '新账户', + 'new_bank_name' => '新银行', + 'new_bank_account' => '6222000022222222', + 'former_company_name' => '变更前公司', + 'entity_change_date' => '2026-09-01', + 'changed_company_name' => '变更后公司', + 'changed_company_start_date' => '2026-09-02', + 'contract_partner_name' => '快递公司甲', + 'shared_courier_names' => ['快递公司乙'], + 'cooperation_party_a_name' => '甲方公司', + 'cooperation_party_a_phone' => '0519-88886666', + 'cooperation_party_b_name' => '乙方公司', + 'cooperation_party_b_phone' => '0519-88889999', + 'cooperation_start_date' => '2026-09-01', + 'cooperation_end_date' => '2027-08-31', + 'principal_name' => '委托方', + 'principal_id_number' => '91320400TEST', + 'trustee_name' => '受托方', + 'trustee_id_number' => '320400TEST', + 'authorization_items' => ['1'], + 'bill_counterparty' => '对账方', + 'loan_counterparty' => '借支方', + 'payment_counterparty' => '付款方', + 'authorization_start_date' => '2026-09-01', + 'authorization_end_date' => '2027-08-31', + ]; + + foreach (['account-proof', 'account-change', 'entity-change', 'shared-contract', 'cooperation-contract', 'authorization'] as $type) { + $signature = new CourierAgreementSignature([ + 'business_no' => 'AGR-DATE-' . $type, + 'template_code' => CourierAgreementSignature::DOCUMENTS[$type]['code'], + 'document_type' => $type, + ]); + $html = view('business.pdf.courier-agreement', [ + 'signature' => $signature, + 'type' => $type, + 'title' => CourierAgreementSignature::DOCUMENTS[$type]['name'], + 'payload' => $payload, + 'signatureDate' => '2026年9月21日', + 'fontPath' => str_replace('\\', '/', resource_path('fonts/DroidSansFallback.ttf')), + ])->render(); + + $this->assertStringContainsString('2026年9月21日', strip_tags($html), "{$type} 未输出签署日期变量"); + $this->assertStringNotContainsString('签署后生成', $html, "{$type} 仍包含旧占位文案"); + } + } + public function test_verified_agreement_callback_is_idempotent_and_queues_archive(): void { Queue::fake(); @@ -541,6 +618,7 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase 'document_type' => 'account-proof', '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', 'operator_name' => '张经理', diff --git a/tests/Feature/Business/CourierQuoteSignatureWorkflowTest.php b/tests/Feature/Business/CourierQuoteSignatureWorkflowTest.php index e367acf8..1312d469 100644 --- a/tests/Feature/Business/CourierQuoteSignatureWorkflowTest.php +++ b/tests/Feature/Business/CourierQuoteSignatureWorkflowTest.php @@ -46,6 +46,26 @@ class CourierQuoteSignatureWorkflowTest extends FeatureTestCase $again = app(CourierQuoteSignatureService::class)->prepareVersion($version->fresh(), $this->user->id); $this->assertSame($signature->id, $again->id); $this->assertSame($signature->source_pdf_sha256, $again->source_pdf_sha256); + + $dated = app(CourierQuoteSignatureService::class)->prepareVersion($version->fresh(), $this->user->id, '2026-09-21'); + $this->assertSame($signature->id, $dated->id); + $this->assertNotSame($signature->source_pdf_sha256, $dated->source_pdf_sha256); + + $html = view('business.pdf.courier-quote', [ + 'version' => $version, + 'change' => $version->change, + 'values' => [['省份', '面单'], ['广东省', 6]], + 'formulas' => [[], []], + 'partyA' => $this->company->name, + 'expiryDate' => '长期', + 'faceFee' => '6', + 'bubbleCoefficient' => '1.2', + 'signatureDate' => '2026年9月21日', + 'generatedAt' => now(), + 'fontPath' => str_replace('\\', '/', resource_path('fonts/DroidSansFallback.ttf')), + ])->render(); + $this->assertStringContainsString('签署日期:', $html); + $this->assertStringContainsString('2026年9月21日', strip_tags($html)); } public function test_admin_can_push_the_exact_pdf_to_esign_sandbox(): void @@ -75,6 +95,7 @@ class CourierQuoteSignatureWorkflowTest extends FeatureTestCase 'operator_name' => '张经理', 'operator_account' => '13800138000', 'notice_type' => '1', + 'signature_date' => '2026-09-21', 'expires_at' => now()->addDays(7)->format('Y-m-d H:i:s'), ])->assertRedirect(route('business.courier-quote-changes.show', $signature->version->source_change_id)); @@ -115,16 +136,41 @@ class CourierQuoteSignatureWorkflowTest extends FeatureTestCase Storage::disk('uploads')->put($signature->source_pdf_path, '%PDF-tampered'); Http::fake(); + try { + app(CourierQuoteSignatureService::class)->send($signature, [ + 'organization_name' => '新北中通快递有限公司', + 'operator_name' => '张经理', + 'operator_account' => '13800138000', + 'notice_type' => '1', + 'expires_at' => now()->addDays(7)->format('Y-m-d H:i:s'), + ], $this->user->id); + $this->fail('A tampered source PDF must not be sent.'); + } catch (\Illuminate\Validation\ValidationException $exception) { + $this->assertSame( + '待签报价 PDF 缺失或校验不一致,请重新生成后再推送。', + $exception->errors()['signature'][0], + ); + } + + Http::assertNothingSent(); + $this->assertNull($signature->fresh()->esign_sign_flow_id); + } + + public function test_quote_send_requires_a_signature_date(): void + { + $signature = $this->createPreparedSignature(); + Http::fake(); + $this->withExceptionHandling()->loginAs()->post(route('business.courier-quote-signatures.send', $signature), [ 'organization_name' => '新北中通快递有限公司', 'operator_name' => '张经理', 'operator_account' => '13800138000', 'notice_type' => '1', 'expires_at' => now()->addDays(7)->format('Y-m-d H:i:s'), - ])->assertSessionHasErrors('signature'); + ])->assertSessionHasErrors('signature_date'); Http::assertNothingSent(); - $this->assertNull($signature->fresh()->esign_sign_flow_id); + $this->assertSame(CourierQuoteSignature::STATUS_PREPARED, $signature->fresh()->status); } public function test_source_pdf_cannot_be_regenerated_after_signing_has_started(): void @@ -175,6 +221,7 @@ class CourierQuoteSignatureWorkflowTest extends FeatureTestCase 'operator_name' => '张经理', 'operator_account' => '13800138000', 'notice_type' => '1', + 'signature_date' => '2026-09-21', 'expires_at' => now()->addDays(7)->format('Y-m-d H:i:s'), ]) ->assertForbidden(); -- 2.47.2