协议签署:供应商合作合同显示模板待提供 #301

Merged
limeng merged 3 commits from fix/limeng-supplier-contract-template-placeholder into main 2026-10-11 13:47:20 +08:00
3 changed files with 42 additions and 25 deletions

View File

@ -40,8 +40,13 @@ class CourierAgreementSignatures extends Controller
$request->merge(['partner_type' => $request->input('partner_type', 'courier')]);
$request->validate(['partner_type' => ['required', 'in:courier,supplier']]);
if ($request->input('partner_type') === 'supplier') {
if ($request->input('document_type') === 'cooperation-contract') {
throw ValidationException::withMessages([
'document_type' => '供应商合作合同模板尚未提供,暂不能发起签署。',
]);
}
$request->validate(['supplier_id' => ['required', 'integer', 'min:1'],
'document_type' => ['required', 'in:account-proof,account-change,entity-change,cooperation-contract,authorization']]);
'document_type' => ['required', 'in:account-proof,account-change,entity-change,authorization']]);
$supplier = app(\Modules\EcommerceFinance\Repositories\SupplierManagementMirrorRepository::class)
->signingSupplier((int) $request->input('supplier_id'));
if (! $supplier) throw ValidationException::withMessages(['supplier_id' => '供应商资料暂不可用,请重新选择供应商。']);

View File

@ -1687,7 +1687,7 @@
</section>
<section class="agreement-form-section" aria-labelledby="agreement-variables-heading">
<h3 class="agreement-section-heading" id="agreement-variables-heading"><span data-variable-template-name>收款账户证明</span> — 填写变量</h3>
<h3 class="agreement-section-heading" id="agreement-variables-heading"><span data-variable-template-name>收款账户证明</span> <span data-variable-template-status>— 填写变量</span></h3>
<div class="agreement-form-grid" data-template-fields="account-proof">
<div class="agreement-field">
<label for="agreement-proof-effective-date">生效日期 <span class="agreement-required" aria-hidden="true">*</span></label>
@ -1820,6 +1820,10 @@
</div>
</div>
<div data-template-fields="supplier-cooperation-pending" hidden>
<p class="agreement-help">供应商合作合同模板尚未提供。提供正式模板后,可在这里填写合同内容。</p>
</div>
<div data-template-fields="authorization" hidden>
<div class="agreement-form-grid">
<div class="agreement-field agreement-field--full">
@ -1996,6 +2000,12 @@
<p class="agreement-preview-page-number">原合同全文预览</p>
</article>
<article class="agreement-preview-paper" data-template-preview="supplier-cooperation-pending" hidden>
<h3>合作合同</h3>
<p>供应商合作合同模板待提供。</p>
<p>提供正式合同模板后,可在这里查看合同预览。</p>
</article>
<article class="agreement-preview-paper" data-template-preview="authorization" hidden>
<h3>授权委托书</h3>
<div class="agreement-preview-account">
@ -2798,9 +2808,6 @@
} else {
documentSelect.value = 'account-proof';
}
sendButton.disabled = (supplier && !supplierOptions.length) || !esignAvailable;
sendButton.title = supplier && !supplierOptions.length ? '供应商资料暂不可用' : (esignAvailable ? '推送至E签宝' : 'E签宝尚未配置');
sendButton.querySelector('span:last-child').textContent = '推送至E签宝';
syncCourierSelection();
}
@ -3277,11 +3284,16 @@
function refreshPreview() {
var documentOption = selectedDocument();
var templateType = documentOption.value;
var supplierCooperationPending = currentPartnerType === 'supplier' && templateType === 'cooperation-contract';
var supplierUnavailable = currentPartnerType === 'supplier' && !supplierOptions.length;
sendButton.disabled = supplierCooperationPending || supplierUnavailable || !esignAvailable;
sendButton.title = supplierCooperationPending ? '供应商合作合同模板尚未提供' : supplierUnavailable ? '供应商资料暂不可用' : (esignAvailable ? '推送至E签宝' : 'E签宝尚未配置');
sendButton.querySelector('span:last-child').textContent = supplierCooperationPending ? '合作合同模板待提供' : '推送至E签宝';
var courier = fieldValue('[name="courier_name"]', currentPartnerType === 'supplier' ? '供应商' : '快递公司');
var companyName = fieldValue('[name="company_full_name"]', '待填写公司全称');
var accountAgreement = templateType === 'account-proof' || templateType === 'account-change';
var entityChange = templateType === 'entity-change';
var cooperationContract = templateType === 'cooperation-contract';
var cooperationContract = templateType === 'cooperation-contract' && !supplierCooperationPending;
var authorization = templateType === 'authorization';
var sharedContract = templateType === 'shared-contract';
var formerCompanyDeregistered = entityChange && entityChangeDeregisteredInput && entityChangeDeregisteredInput.checked;
@ -3348,7 +3360,7 @@
? '填写与 E 签宝账号实名一致的个人姓名;该姓名会作为个人签署人发送,不需要填写企业全称。'
: '必须与对方 E 签宝企业认证名称一致;这是签署主体,不要求与银行卡持卡人姓名一致。';
}
activateTemplate(templateType);
activateTemplate(supplierCooperationPending ? 'supplier-cooperation-pending' : templateType);
setText('[data-cooperation-party-b-label]', otherSignerIsPerson ? '乙方个人姓名(E 签宝实名)' : '乙方名称');
root.querySelector('[name="cooperation_party_b_name"]').placeholder = otherSignerIsPerson
? '填写合作人的 E 签宝实名认证姓名'
@ -3358,7 +3370,7 @@
refreshSignerAccountLabels(templateType);
var secondary = root.querySelector('[data-secondary-signer-fields]');
if (secondary) {
var needsSecondary = entityChange || templateType === 'cooperation-contract' || templateType === 'authorization';
var needsSecondary = entityChange || cooperationContract || authorization;
secondary.hidden = !needsSecondary;
secondary.querySelectorAll('input').forEach(function (control) {
control.disabled = !needsSecondary;
@ -3388,10 +3400,12 @@
setText('[data-secondary-operator-label]', entityChange ? '变更后主体 E 签宝实名认证姓名' : cooperationContract ? '我方 E 签宝实名认证姓名' : '受托方 E 签宝实名认证姓名');
}
setText('[data-template-name]', documentOption.dataset.name);
setText('[data-template-code]', documentOption.dataset.code);
setText('[data-template-description]', formerCompanyDeregistered ? '原法人以个人身份签字,变更后公司仍须法人签字并盖章;双方分别接收签署请求。' : otherSignerIsPerson ? '使用合作人的个人实名认证姓名和账号签署。' : documentOption.dataset.description);
setText('[data-template-code]', supplierCooperationPending ? '模板待提供' : documentOption.dataset.code);
setText('[data-template-description]', supplierCooperationPending ? '供应商合作合同模板尚未提供。提供正式模板后,可填写并预览。' : formerCompanyDeregistered ? '原法人以个人身份签字,变更后公司仍须法人签字并盖章;双方分别接收签署请求。' : otherSignerIsPerson ? '使用合作人的个人实名认证姓名和账号签署。' : documentOption.dataset.description);
setText('[data-variable-template-name]', documentOption.dataset.name);
setText('[data-preview-template-code]', documentOption.dataset.code);
setText('[data-variable-template-status]', supplierCooperationPending ? '— 模板待提供' : '— 填写变量');
setText('[data-preview-template-code]', supplierCooperationPending ? '模板待提供' : documentOption.dataset.code);
setText('[data-preview-status-hint]', supplierCooperationPending ? '提供模板后可预览' : '蓝色=已填 红色=待填');
setPreviewValue('company', companyName);
setPreviewValue('courier', courier);
setPreviewValue('signatureDate', formatDateField('[name="signature_date"]', '待填写'));
@ -3710,6 +3724,11 @@
form.addEventListener('submit', function (event) {
refreshPreview();
if (currentPartnerType === 'supplier' && selectedDocument().value === 'cooperation-contract') {
event.preventDefault();
root.querySelector('[data-request-error]').textContent = '供应商合作合同模板尚未提供,暂不能发起签署。';
return;
}
if (!form.checkValidity()) {
event.preventDefault();
var error = root.querySelector('[data-request-error]');

View File

@ -128,24 +128,17 @@ class CourierAgreementSignatureWorkflowTest extends FeatureTestCase
Http::assertNothingSent();
}
public function test_supplier_cooperation_contract_keeps_both_signing_parties(): void
public function test_supplier_cooperation_contract_without_a_template_never_sends(): void
{
$this->fakeEsign();
$repository = Mockery::mock(SupplierManagementMirrorRepository::class);
$repository->shouldReceive('signingSupplier')->with(7)->once()->andReturn((object) [
'Id' => 7, 'scmname' => '测试供应商', 'accountbankno' => '13900139000',
]);
$this->app->instance(SupplierManagementMirrorRepository::class, $repository);
Http::preventStrayRequests();
$this->withExceptionHandling();
$payload = $this->cooperationContractPayload('supplier-cooperation');
$payload['partner_type'] = 'supplier';
$payload['supplier_id'] = 7;
unset($payload['esign_account']);
$this->loginAs()->post(route('business.courier-agreement-signatures.store'), $payload)->assertRedirect();
$signature = CourierAgreementSignature::query()->firstOrFail();
$this->assertSame('测试供应商', $signature->courier_name);
$this->assertSame('13900139000', $signature->operator_account);
$this->assertSame('18052733960', $signature->payload['secondary_esign_account']);
$this->assertSame(CourierAgreementSignature::STATUS_SIGNING, $signature->status);
$this->loginAs()->post(route('business.courier-agreement-signatures.store'), $payload)
->assertSessionHasErrors(['document_type' => '供应商合作合同模板尚未提供,暂不能发起签署。']);
$this->assertSame(0, CourierAgreementSignature::query()->count());
Http::assertNothingSent();
}
public function test_unknown_partner_type_is_rejected_before_sending(): void