feat:E-Sign 템플릿 변수 자동채움 시스템 구현
- 시스템 변수 (서명자명, 이메일, 계약제목, 날짜 등) 자동 해석 - 커스텀 변수 정의/관리 (템플릿별 계약금액, 기간 등) - 템플릿 필드 에디터: 변수 관리 + 필드-변수 바인딩 UI - 계약 생성 폼: 템플릿 변수 입력 섹션 추가 - 계약 필드 에디터: 변수 연결 정보 표시 - PdfSignatureService: font_size 반영 렌더링 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -95,6 +95,8 @@ public function store(Request $request): JsonResponse
|
||||
'signers.*.email' => 'required|email|max:200',
|
||||
'signers.*.phone' => 'nullable|string|max:20',
|
||||
'signers.*.role' => 'required|in:creator,counterpart',
|
||||
'metadata' => 'nullable|array',
|
||||
'metadata.*' => 'nullable|string|max:500',
|
||||
]);
|
||||
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
@@ -145,6 +147,7 @@ public function store(Request $request): JsonResponse
|
||||
'original_file_hash' => $fileHash,
|
||||
'original_file_size' => $fileSize,
|
||||
'status' => 'draft',
|
||||
'metadata' => $request->input('metadata'),
|
||||
'expires_at' => $request->input('expires_at')
|
||||
? \Carbon\Carbon::parse($request->input('expires_at'))
|
||||
: now()->addDays($request->input('expires_days', 30)),
|
||||
@@ -383,6 +386,7 @@ public function configureFields(Request $request, int $id): JsonResponse
|
||||
'fields.*.height' => 'required|numeric',
|
||||
'fields.*.field_type' => 'required|in:signature,stamp,text,date,checkbox',
|
||||
'fields.*.field_label' => 'nullable|string|max:100',
|
||||
'fields.*.field_variable' => 'nullable|string|max:50',
|
||||
'fields.*.font_size' => 'nullable|integer|min:6|max:72',
|
||||
'fields.*.is_required' => 'nullable|boolean',
|
||||
]);
|
||||
@@ -405,6 +409,7 @@ public function configureFields(Request $request, int $id): JsonResponse
|
||||
'height' => $field['height'],
|
||||
'field_type' => $field['field_type'],
|
||||
'field_label' => $field['field_label'] ?? null,
|
||||
'field_variable' => $field['field_variable'] ?? null,
|
||||
'font_size' => $field['font_size'] ?? null,
|
||||
'is_required' => $field['is_required'] ?? true,
|
||||
'sort_order' => $i,
|
||||
@@ -623,6 +628,7 @@ public function storeTemplate(Request $request): JsonResponse
|
||||
'items.*.height' => 'required|numeric',
|
||||
'items.*.field_type' => 'required|in:signature,stamp,text,date,checkbox',
|
||||
'items.*.field_label' => 'nullable|string|max:100',
|
||||
'items.*.field_variable' => 'nullable|string|max:50',
|
||||
'items.*.font_size' => 'nullable|integer|min:6|max:72',
|
||||
'items.*.is_required' => 'nullable|boolean',
|
||||
]);
|
||||
@@ -683,6 +689,7 @@ public function storeTemplate(Request $request): JsonResponse
|
||||
'height' => $item['height'],
|
||||
'field_type' => $item['field_type'],
|
||||
'field_label' => $item['field_label'] ?? null,
|
||||
'field_variable' => $item['field_variable'] ?? null,
|
||||
'font_size' => $item['font_size'] ?? null,
|
||||
'is_required' => $item['is_required'] ?? true,
|
||||
'sort_order' => $i,
|
||||
@@ -722,16 +729,27 @@ public function updateTemplate(Request $request, int $id): JsonResponse
|
||||
'name' => 'required|string|max:100',
|
||||
'description' => 'nullable|string',
|
||||
'category' => 'nullable|string|max:50',
|
||||
'variables' => 'nullable|array',
|
||||
'variables.*.key' => 'required|string|max:50',
|
||||
'variables.*.label' => 'required|string|max:100',
|
||||
'variables.*.type' => 'nullable|in:text,number,date',
|
||||
'variables.*.default' => 'nullable|string|max:500',
|
||||
]);
|
||||
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
$template = EsignFieldTemplate::forTenant($tenantId)->findOrFail($id);
|
||||
|
||||
$template->update([
|
||||
$updateData = [
|
||||
'name' => $request->input('name'),
|
||||
'description' => $request->input('description'),
|
||||
'category' => $request->input('category'),
|
||||
]);
|
||||
];
|
||||
|
||||
if ($request->has('variables')) {
|
||||
$updateData['variables'] = $request->input('variables');
|
||||
}
|
||||
|
||||
$template->update($updateData);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
@@ -837,6 +855,7 @@ public function updateTemplateItems(Request $request, int $templateId): JsonResp
|
||||
'items.*.height' => 'required|numeric',
|
||||
'items.*.field_type' => 'required|in:signature,stamp,text,date,checkbox',
|
||||
'items.*.field_label' => 'nullable|string|max:100',
|
||||
'items.*.field_variable' => 'nullable|string|max:50',
|
||||
'items.*.font_size' => 'nullable|integer|min:6|max:72',
|
||||
'items.*.is_required' => 'nullable|boolean',
|
||||
]);
|
||||
@@ -862,6 +881,7 @@ public function updateTemplateItems(Request $request, int $templateId): JsonResp
|
||||
'height' => round($itemData['height'], 2),
|
||||
'field_type' => $itemData['field_type'],
|
||||
'field_label' => $itemData['field_label'] ?? '',
|
||||
'field_variable' => $itemData['field_variable'] ?? null,
|
||||
'font_size' => $itemData['font_size'] ?? null,
|
||||
'is_required' => $itemData['is_required'] ?? true,
|
||||
'sort_order' => $i,
|
||||
@@ -913,6 +933,7 @@ public function duplicateTemplate(int $id): JsonResponse
|
||||
'description' => $template->description,
|
||||
'category' => $template->category,
|
||||
'signer_count' => $template->signer_count,
|
||||
'variables' => $template->variables,
|
||||
'is_active' => true,
|
||||
'created_by' => auth()->id(),
|
||||
], $fileData));
|
||||
@@ -928,6 +949,7 @@ public function duplicateTemplate(int $id): JsonResponse
|
||||
'height' => $item->height,
|
||||
'field_type' => $item->field_type,
|
||||
'field_label' => $item->field_label,
|
||||
'field_variable' => $item->field_variable,
|
||||
'font_size' => $item->font_size,
|
||||
'is_required' => $item->is_required,
|
||||
'sort_order' => $item->sort_order,
|
||||
@@ -988,7 +1010,10 @@ public function applyTemplate(Request $request, int $id): JsonResponse
|
||||
$signerMap[$signer->sign_order] = $signer->id;
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($contract, $template, $tenantId, $signerMap) {
|
||||
// 변수 해석용 맵 구성
|
||||
$variableValues = $this->buildVariableMap($contract, $template);
|
||||
|
||||
DB::transaction(function () use ($contract, $template, $tenantId, $signerMap, $variableValues) {
|
||||
// 기존 필드 삭제
|
||||
EsignSignField::where('contract_id', $contract->id)->delete();
|
||||
|
||||
@@ -997,6 +1022,12 @@ public function applyTemplate(Request $request, int $id): JsonResponse
|
||||
$signerId = $signerMap[$item->signer_order] ?? null;
|
||||
if (!$signerId) continue;
|
||||
|
||||
// 변수가 바인딩된 필드는 자동 채움
|
||||
$fieldValue = null;
|
||||
if ($item->field_variable && isset($variableValues[$item->field_variable])) {
|
||||
$fieldValue = $variableValues[$item->field_variable];
|
||||
}
|
||||
|
||||
EsignSignField::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'contract_id' => $contract->id,
|
||||
@@ -1008,7 +1039,9 @@ public function applyTemplate(Request $request, int $id): JsonResponse
|
||||
'height' => $item->height,
|
||||
'field_type' => $item->field_type,
|
||||
'field_label' => $item->field_label,
|
||||
'field_variable' => $item->field_variable,
|
||||
'font_size' => $item->font_size,
|
||||
'field_value' => $fieldValue,
|
||||
'is_required' => $item->is_required,
|
||||
'sort_order' => $item->sort_order,
|
||||
]);
|
||||
@@ -1073,6 +1106,7 @@ public function copyFieldsFromContract(Request $request, int $id, int $sourceId)
|
||||
'height' => $field->height,
|
||||
'field_type' => $field->field_type,
|
||||
'field_label' => $field->field_label,
|
||||
'field_variable' => $field->field_variable,
|
||||
'font_size' => $field->font_size,
|
||||
'is_required' => $field->is_required,
|
||||
'sort_order' => $field->sort_order,
|
||||
@@ -1088,4 +1122,35 @@ public function copyFieldsFromContract(Request $request, int $id, int $sourceId)
|
||||
'data' => $fields,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 변수 해석 맵 구성 (시스템 변수 + 커스텀 변수)
|
||||
*/
|
||||
private function buildVariableMap(EsignContract $contract, EsignFieldTemplate $template): array
|
||||
{
|
||||
$map = [];
|
||||
|
||||
// 시스템 변수: 서명자 정보
|
||||
$signers = $contract->signers->sortBy('sign_order');
|
||||
$idx = 1;
|
||||
foreach ($signers as $signer) {
|
||||
$map["signer{$idx}_name"] = $signer->name;
|
||||
$map["signer{$idx}_email"] = $signer->email;
|
||||
$map["signer{$idx}_phone"] = $signer->phone ?? '';
|
||||
$idx++;
|
||||
}
|
||||
|
||||
// 시스템 변수: 계약 정보
|
||||
$map['contract_title'] = $contract->title ?? '';
|
||||
$map['current_date'] = now()->format('Y.m.d');
|
||||
$map['expires_at'] = $contract->expires_at ? $contract->expires_at->format('Y.m.d') : '';
|
||||
|
||||
// 커스텀 변수: contract.metadata에서 조회
|
||||
$metadata = $contract->metadata ?? [];
|
||||
foreach ($metadata as $key => $value) {
|
||||
$map[$key] = $value ?? '';
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user