- 견적서 전용 폼/조회 파셜 추가 - create/edit/show 페이지에 견적서 통합 - Alpine.js 동적 품목 테이블 (자동 세액 계산) - 공급자 정보 테넌트에서 자동 로드 - 미리보기/인쇄 기능
911 lines
47 KiB
PHP
911 lines
47 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', '결재 상세')
|
|
|
|
@section('content')
|
|
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 mb-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-gray-800">결재 상세</h1>
|
|
<p class="text-sm text-gray-500 mt-1">{{ $approval->document_number }}</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
@if($approval->isEditable() && $approval->drafter_id === auth()->id())
|
|
<a href="{{ route('approvals.edit', $approval->id) }}"
|
|
class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition text-sm">
|
|
수정
|
|
</a>
|
|
@endif
|
|
<a href="{{ route('approvals.drafts') }}" class="text-gray-600 hover:text-gray-800 text-sm px-3 py-2 border rounded-lg">
|
|
목록
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 문서 정보 --}}
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
<div class="flex justify-between items-start gap-4 mb-4">
|
|
<div class="flex flex-wrap gap-y-3" style="gap-column: 0;">
|
|
<div class="pr-6 border-r border-gray-200 mr-6">
|
|
<span class="text-xs text-gray-500">상태</span>
|
|
<div class="mt-1">
|
|
@include('approvals.partials._status-badge', ['status' => $approval->status])
|
|
@if($approval->is_urgent)
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-700 ml-1">긴급</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
<div class="pr-6 border-r border-gray-200 mr-6">
|
|
<span class="text-xs text-gray-500">양식</span>
|
|
<div class="mt-1 text-sm font-medium">{{ $approval->form?->name ?? '-' }}</div>
|
|
</div>
|
|
<div class="pr-6 border-r border-gray-200 mr-6">
|
|
<span class="text-xs text-gray-500">기안자</span>
|
|
<div class="mt-1 text-sm font-medium">{{ $approval->drafter?->name ?? '-' }}</div>
|
|
</div>
|
|
<div class="pr-6{{ $approval->completed_at || $approval->parent_doc_id ? ' border-r border-gray-200 mr-6' : '' }}">
|
|
<span class="text-xs text-gray-500">기안일</span>
|
|
<div class="mt-1 text-sm">{{ $approval->drafted_at?->format('Y-m-d H:i') ?? '-' }}</div>
|
|
</div>
|
|
@if($approval->completed_at)
|
|
<div class="pr-6{{ $approval->parent_doc_id ? ' border-r border-gray-200 mr-6' : '' }}">
|
|
<span class="text-xs text-gray-500">완료일</span>
|
|
<div class="mt-1 text-sm">{{ $approval->completed_at->format('Y-m-d H:i') }}</div>
|
|
</div>
|
|
@endif
|
|
@if($approval->parent_doc_id)
|
|
<div class="pr-6">
|
|
<span class="text-xs text-gray-500">원본 문서</span>
|
|
<div class="mt-1 text-sm">
|
|
<a href="{{ route('approvals.show', $approval->parent_doc_id) }}" class="text-blue-600 hover:underline">
|
|
{{ $approval->parentDocument?->document_number ?? '원본 보기' }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- 결재서명란 --}}
|
|
<div class="shrink-0">
|
|
@include('approvals.partials._approval-stamp-table', ['approval' => $approval])
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 참조자 --}}
|
|
@php
|
|
$refSteps = $approval->steps->where('step_type', 'reference')->values();
|
|
@endphp
|
|
@if($refSteps->count() > 0)
|
|
<div class="mb-4 flex items-center gap-2 flex-wrap">
|
|
<span class="text-xs font-semibold text-gray-500 flex items-center gap-1">
|
|
<svg class="w-3.5 h-3.5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
|
</svg>
|
|
참조
|
|
</span>
|
|
@foreach($refSteps as $ref)
|
|
<span class="inline-flex items-center px-2 py-0.5 bg-green-50 border border-green-200 rounded-full text-xs text-gray-700">
|
|
{{ $ref->approver_name ?? ($ref->approver?->name ?? '-') }}
|
|
</span>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
{{-- 회수 사유 표시 --}}
|
|
@if($approval->status === 'cancelled' && $approval->recall_reason)
|
|
<div class="mb-4 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
|
|
<span class="text-xs font-medium text-yellow-700">회수 사유</span>
|
|
<p class="text-sm text-yellow-800 mt-1">{{ $approval->recall_reason }}</p>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="border-t pt-4">
|
|
<h2 class="text-lg font-semibold text-gray-800 mb-2">{{ $approval->title }}</h2>
|
|
@if(!empty($approval->content) && str_starts_with($approval->form?->code ?? '', 'pr_'))
|
|
@include('approvals.partials._purchase-request-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'expense')
|
|
@include('approvals.partials._expense-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'employment_cert')
|
|
@include('approvals.partials._certificate-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'career_cert')
|
|
@include('approvals.partials._career-cert-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'appointment_cert')
|
|
@include('approvals.partials._appointment-cert-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'resignation')
|
|
@include('approvals.partials._resignation-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'seal_usage')
|
|
@include('approvals.partials._seal-usage-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'delegation')
|
|
@include('approvals.partials._delegation-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'board_minutes')
|
|
@include('approvals.partials._board-minutes-show', ['content' => $approval->content])
|
|
@elseif(!empty($approval->content) && $approval->form?->code === 'quotation')
|
|
@include('approvals.partials._quotation-show', ['content' => $approval->content])
|
|
@elseif($approval->body && preg_match('/<[a-z][\s\S]*>/i', $approval->body))
|
|
<div class="prose prose-sm max-w-none text-gray-700">
|
|
{!! strip_tags($approval->body, '<p><br><strong><b><em><i><u><s><del><h1><h2><h3><h4><h5><h6><ul><ol><li><blockquote><pre><code><a><span><div><table><thead><tbody><tr><th><td>') !!}
|
|
</div>
|
|
@else
|
|
<div class="prose prose-sm max-w-none text-gray-700 whitespace-pre-wrap">{{ $approval->body ?? '(내용 없음)' }}</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
{{-- 반려 이력 --}}
|
|
@if(!empty($approval->rejection_history))
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-3 flex items-center gap-2">
|
|
반려 이력
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-700">{{ count($approval->rejection_history) }}회</span>
|
|
</h3>
|
|
<div class="space-y-3">
|
|
@foreach($approval->rejection_history as $history)
|
|
<div class="border-l-3 border-red-300 pl-4 py-2" style="border-left: 3px solid #fca5a5;">
|
|
<div class="flex items-center gap-2 text-sm">
|
|
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-50 text-red-600">{{ $history['round'] ?? '-' }}차 반려</span>
|
|
<span class="font-medium text-gray-800">{{ $history['approver_name'] ?? '' }}</span>
|
|
@if(!empty($history['approver_position']))
|
|
<span class="text-gray-400 text-xs">{{ $history['approver_position'] }}</span>
|
|
@endif
|
|
<span class="text-gray-400 text-xs">{{ $history['rejected_at'] ?? '' }}</span>
|
|
</div>
|
|
<p class="text-sm text-gray-700 mt-1">{{ $history['comment'] ?? '' }}</p>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- 결재 의견 --}}
|
|
@php
|
|
$stepsWithComments = $approval->steps->filter(fn($s) => $s->comment);
|
|
@endphp
|
|
@if($stepsWithComments->isNotEmpty())
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-2">결재 의견</h3>
|
|
<div class="space-y-2">
|
|
@foreach($stepsWithComments as $step)
|
|
<div class="flex gap-3 p-3 bg-gray-50 rounded-lg">
|
|
<div class="shrink-0">
|
|
@if($step->status === 'approved')
|
|
@if(($step->approval_type ?? 'normal') === 'pre_decided')
|
|
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-indigo-100 text-indigo-600 text-xs">⚡</span>
|
|
@else
|
|
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-green-100 text-green-600 text-xs">✓</span>
|
|
@endif
|
|
@elseif($step->status === 'on_hold')
|
|
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-amber-100 text-amber-600 text-xs">⏸</span>
|
|
@else
|
|
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-red-100 text-red-600 text-xs">✗</span>
|
|
@endif
|
|
</div>
|
|
<div>
|
|
<div class="text-sm font-medium">
|
|
{{ $step->approver_name ?? ($step->approver?->name ?? '') }}
|
|
@if(($step->approval_type ?? 'normal') === 'pre_decided')
|
|
<span class="text-xs text-indigo-500 font-normal">(전결)</span>
|
|
@endif
|
|
@if($step->status === 'on_hold')
|
|
<span class="text-xs text-amber-500 font-normal">(보류)</span>
|
|
@endif
|
|
<span class="text-gray-400 font-normal text-xs">{{ $step->acted_at?->format('Y-m-d H:i') }}</span>
|
|
</div>
|
|
<p class="text-sm text-gray-600 mt-1">{{ $step->comment }}</p>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- 결재 처리 (승인/반려/보류/전결) --}}
|
|
@if($approval->isActionable() && $approval->isCurrentApprover(auth()->id()))
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
<h3 class="text-lg font-semibold text-gray-800 mb-4">결재 처리</h3>
|
|
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">결재 의견</label>
|
|
<textarea id="approval-comment" rows="3" placeholder="의견을 입력하세요 (반려/보류 시 필수)"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
<button onclick="processApproval('approve')"
|
|
class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
승인
|
|
</button>
|
|
<button onclick="processApproval('reject')"
|
|
class="bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
반려
|
|
</button>
|
|
<button onclick="processHold()"
|
|
class="bg-amber-500 hover:bg-amber-600 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
보류
|
|
</button>
|
|
<button onclick="processPreDecide()"
|
|
class="bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
전결
|
|
</button>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- 보류 해제 (보류 상태에서 보류한 결재자만) --}}
|
|
@if($approval->isHoldReleasable())
|
|
@php
|
|
$holdStep = $approval->steps->firstWhere('status', 'on_hold');
|
|
$canRelease = $holdStep && $holdStep->approver_id === auth()->id();
|
|
@endphp
|
|
@if($canRelease)
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
<div class="flex items-center gap-3">
|
|
<button onclick="releaseHold()"
|
|
class="bg-amber-500 hover:bg-amber-600 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
보류 해제
|
|
</button>
|
|
<span class="text-xs text-gray-500">보류를 해제하고 결재를 다시 진행합니다.</span>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@endif
|
|
|
|
{{-- 회수 (기안자 + pending/on_hold) --}}
|
|
@if($approval->isCancellable() && $approval->drafter_id === auth()->id())
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
@php
|
|
$firstStep = $approval->steps->whereIn('step_type', ['approval', 'agreement'])->sortBy('step_order')->first();
|
|
$canCancel = $firstStep && in_array($firstStep->status, ['pending', 'on_hold']);
|
|
@endphp
|
|
|
|
@if($canCancel)
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">회수 사유</label>
|
|
<textarea id="recall-reason" rows="2" placeholder="회수 사유를 입력하세요"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-yellow-500"></textarea>
|
|
</div>
|
|
<button onclick="cancelApproval()"
|
|
class="bg-yellow-500 hover:bg-yellow-600 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
결재 회수
|
|
</button>
|
|
<span class="text-xs text-gray-500 ml-2">진행 중인 결재를 취소합니다.</span>
|
|
@else
|
|
<p class="text-sm text-gray-500">첫 번째 결재자가 이미 처리하여 회수할 수 없습니다.</p>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
{{-- 복사 재기안 (완료/반려/회수 상태에서 기안자만) --}}
|
|
@if($approval->isCopyable() && $approval->drafter_id === auth()->id())
|
|
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
|
|
<button onclick="copyForRedraft()"
|
|
class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
복사하여 재기안
|
|
</button>
|
|
<span class="text-xs text-gray-500 ml-2">이 문서를 복사하여 새 결재를 작성합니다.</span>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- 삭제 (기안자: draft만 / 관리자: 모든 상태) --}}
|
|
@if($approval->isDeletableBy(auth()->user()))
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<div class="flex items-center gap-3">
|
|
<button onclick="deleteApproval()"
|
|
class="bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded-lg transition text-sm font-medium">
|
|
문서 삭제
|
|
</button>
|
|
<span class="text-xs text-gray-500">이 결재 문서를 삭제합니다. 삭제 후 복구할 수 없습니다.</span>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
{{-- 기안자가 완료 문서 열람 시 읽음 처리 --}}
|
|
@if($approval->drafter_id === auth()->id() && in_array($approval->status, ['approved', 'rejected']) && !$approval->drafter_read_at)
|
|
fetch('/api/admin/approvals/{{ $approval->id }}/mark-read-single', {
|
|
method: 'POST',
|
|
headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': '{{ csrf_token() }}' }
|
|
}).catch(() => {});
|
|
@endif
|
|
|
|
async function processApproval(action) {
|
|
const comment = document.getElementById('approval-comment')?.value || '';
|
|
|
|
if (action === 'reject' && !comment.trim()) {
|
|
showToast('반려 시 사유를 입력해주세요.', 'warning');
|
|
return;
|
|
}
|
|
|
|
if (action === 'approve' && !confirm('승인하시겠습니까?')) return;
|
|
if (action === 'reject' && !confirm('반려하시겠습니까?')) return;
|
|
|
|
try {
|
|
const response = await fetch(`/api/admin/approvals/{{ $approval->id }}/${action}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
body: JSON.stringify({ comment: comment }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
setTimeout(() => location.reload(), 500);
|
|
} else {
|
|
showToast(data.message || '처리에 실패했습니다.', 'error');
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
async function processHold() {
|
|
const comment = document.getElementById('approval-comment')?.value || '';
|
|
|
|
if (!comment.trim()) {
|
|
showToast('보류 사유를 입력해주세요.', 'warning');
|
|
return;
|
|
}
|
|
|
|
if (!confirm('이 결재를 보류하시겠습니까?')) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/approvals/{{ $approval->id }}/hold', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
body: JSON.stringify({ comment: comment }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
setTimeout(() => location.reload(), 500);
|
|
} else {
|
|
showToast(data.message || '보류에 실패했습니다.', 'error');
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
async function releaseHold() {
|
|
if (!confirm('보류를 해제하시겠습니까?')) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/approvals/{{ $approval->id }}/release-hold', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
setTimeout(() => location.reload(), 500);
|
|
} else {
|
|
showToast(data.message || '보류 해제에 실패했습니다.', 'error');
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
async function processPreDecide() {
|
|
const comment = document.getElementById('approval-comment')?.value || '';
|
|
|
|
if (!confirm('전결 처리하시겠습니까?\n이후 모든 결재를 건너뛰고 문서를 최종 승인합니다.')) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/approvals/{{ $approval->id }}/pre-decide', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
body: JSON.stringify({ comment: comment }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
setTimeout(() => location.reload(), 500);
|
|
} else {
|
|
showToast(data.message || '전결에 실패했습니다.', 'error');
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
async function cancelApproval() {
|
|
const recallReason = document.getElementById('recall-reason')?.value || '';
|
|
|
|
if (!confirm('결재를 회수하시겠습니까? 이 작업은 되돌릴 수 없습니다.')) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/approvals/{{ $approval->id }}/cancel', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
body: JSON.stringify({ recall_reason: recallReason }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
setTimeout(() => location.reload(), 500);
|
|
} else {
|
|
showToast(data.message || '회수에 실패했습니다.', 'error');
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
async function copyForRedraft() {
|
|
if (!confirm('이 문서를 복사하여 새 결재를 작성하시겠습니까?')) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/approvals/{{ $approval->id }}/copy', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
setTimeout(() => {
|
|
location.href = '/approval-mgmt/' + data.data.id + '/edit';
|
|
}, 500);
|
|
} else {
|
|
showToast(data.message || '복사에 실패했습니다.', 'error');
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
async function deleteApproval() {
|
|
const status = '{{ $approval->status }}';
|
|
const isActive = ['pending', 'on_hold'].includes(status);
|
|
|
|
if (isActive) {
|
|
if (!confirm('이 문서는 현재 진행 중입니다.\n삭제하면 연관된 휴가 등의 처리도 취소됩니다.\n정말 삭제하시겠습니까?')) return;
|
|
}
|
|
|
|
if (!confirm('결재 문서를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.')) return;
|
|
|
|
try {
|
|
const response = await fetch('/api/admin/approvals/{{ $approval->id }}', {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showToast(data.message, 'success');
|
|
setTimeout(() => {
|
|
location.href = '{{ route("approvals.drafts") }}';
|
|
}, 500);
|
|
} else {
|
|
showToast(data.message || '삭제에 실패했습니다.', 'error');
|
|
}
|
|
} catch (e) {
|
|
showToast('서버 오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// 재직증명서 미리보기 (show 페이지용)
|
|
// =========================================================================
|
|
|
|
@if(!empty($approval->content) && $approval->form?->code === 'resignation')
|
|
const _resignationContent = @json($approval->content);
|
|
|
|
function openResignationShowPreview() {
|
|
const c = _resignationContent;
|
|
const issueDate = c.issue_date || '';
|
|
const issueDateFormatted = issueDate ? issueDate.replace(/-/g, function(m, i) { return i === 4 ? '년 ' : '월 '; }) + '일' : '-';
|
|
|
|
const el = document.getElementById('resignation-show-preview-content');
|
|
el.innerHTML = _buildResignationHtml({
|
|
department: c.department || '-',
|
|
position: c.position || '-',
|
|
name: c.name || '-',
|
|
resident: c.resident_number || '-',
|
|
hireDate: c.hire_date || '-',
|
|
resignDate: c.resign_date || '-',
|
|
address: c.address || '-',
|
|
reason: c.reason || '-',
|
|
issueDateFormatted: issueDateFormatted,
|
|
company: c.company_name || '-',
|
|
ceoName: c.ceo_name || '-',
|
|
});
|
|
|
|
document.getElementById('resignation-show-preview-modal').style.display = '';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeResignationShowPreview() {
|
|
document.getElementById('resignation-show-preview-modal').style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function printResignationShowPreview() {
|
|
const content = document.getElementById('resignation-show-preview-content').innerHTML;
|
|
const win = window.open('', '_blank', 'width=800,height=1000');
|
|
win.document.write('<html><head><title>사직서</title>');
|
|
win.document.write('<style>@page{size:A4;margin:0;} body{font-family:"Pretendard","Malgun Gothic",sans-serif;margin:0;padding:0;} .cert-page{padding:100px 56px 60px;box-sizing:border-box;} table{border-collapse:collapse;width:100%;} th,td{border:1px solid #333;padding:16px 18px;font-size:16px;} th{background:#f8f9fa;font-weight:600;text-align:left;white-space:nowrap;width:140px;}</style>');
|
|
win.document.write('</head><body><div class="cert-page">');
|
|
win.document.write(content);
|
|
win.document.write('</div></body></html>');
|
|
win.document.close();
|
|
win.onload = function() { win.print(); };
|
|
}
|
|
|
|
function _buildResignationHtml(d) {
|
|
const e = (s) => { const div = document.createElement('div'); div.textContent = s; return div.innerHTML; };
|
|
const thStyle = 'border:1px solid #333; padding:16px 18px; background:#f8f9fa; font-weight:600; text-align:left; white-space:nowrap; width:140px; font-size:16px;';
|
|
const tdStyle = 'border:1px solid #333; padding:16px 18px; font-size:16px;';
|
|
|
|
return `
|
|
<h1 style="text-align:center; font-size:30px; font-weight:700; letter-spacing:14px; margin-bottom:60px;">사 직 서</h1>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:60px;">
|
|
<tr><th style="${thStyle}">소 속</th><td style="${tdStyle}">${e(d.department)}</td><th style="${thStyle}">직 위</th><td style="${tdStyle}">${e(d.position)}</td></tr>
|
|
<tr><th style="${thStyle}">성 명</th><td style="${tdStyle}">${e(d.name)}</td><th style="${thStyle}">주민등록번호</th><td style="${tdStyle}">${e(d.resident)}</td></tr>
|
|
<tr><th style="${thStyle}">입사일</th><td style="${tdStyle}">${e(d.hireDate)}</td><th style="${thStyle}">퇴사(예정)일</th><td style="${tdStyle}">${e(d.resignDate)}</td></tr>
|
|
<tr><th style="${thStyle}">주 소</th><td style="${tdStyle}" colspan="3">${e(d.address)}</td></tr>
|
|
<tr><th style="${thStyle}">사 유</th><td style="${tdStyle}" colspan="3">${e(d.reason)}</td></tr>
|
|
</table>
|
|
<p style="text-align:center; font-size:16px; line-height:2; margin:80px 0;">상기 본인은 위 사유로 인하여 사직하고자<br>이에 사직서를 제출하오니 허가하여 주시기 바랍니다.</p>
|
|
<p style="text-align:center; font-size:16px; font-weight:500; margin-bottom:40px;">${e(d.issueDateFormatted)}</p>
|
|
<p style="text-align:center; font-size:15px; margin-bottom:80px;">신청인 ${e(d.name)} (인)</p>
|
|
<div style="text-align:center; margin-top:60px;">
|
|
<p style="font-size:18px; font-weight:600;">${e(d.company)} 대표이사 귀하</p>
|
|
</div>
|
|
`;
|
|
}
|
|
@endif
|
|
|
|
@if(!empty($approval->content) && $approval->form?->code === 'appointment_cert')
|
|
const _appointmentCertContent = @json($approval->content);
|
|
|
|
function openAppointmentCertShowPreview() {
|
|
const c = _appointmentCertContent;
|
|
const issueDate = c.issue_date || '';
|
|
const issueDateFormatted = issueDate ? issueDate.replace(/-/g, function(m, i) { return i === 4 ? '년 ' : '월 '; }) + '일' : '-';
|
|
|
|
const el = document.getElementById('appointment-cert-show-preview-content');
|
|
el.innerHTML = _buildAppointmentCertHtml({
|
|
name: c.name || '-',
|
|
resident: c.resident_number || '-',
|
|
department: c.department || '-',
|
|
phone: c.phone || '-',
|
|
hireDate: c.hire_date || '-',
|
|
resignDate: c.resign_date || '현재',
|
|
contractType: c.contract_type || '-',
|
|
purpose: c.purpose || '-',
|
|
issueDateFormatted: issueDateFormatted,
|
|
company: c.company_name || '-',
|
|
ceoName: c.ceo_name || '-',
|
|
});
|
|
|
|
document.getElementById('appointment-cert-show-preview-modal').style.display = '';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeAppointmentCertShowPreview() {
|
|
document.getElementById('appointment-cert-show-preview-modal').style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function printAppointmentCertShowPreview() {
|
|
const content = document.getElementById('appointment-cert-show-preview-content').innerHTML;
|
|
const win = window.open('', '_blank', 'width=800,height=1000');
|
|
win.document.write('<html><head><title>위촉증명서</title>');
|
|
win.document.write('<style>@page{size:A4;margin:0;} body{font-family:"Pretendard","Malgun Gothic",sans-serif;margin:0;padding:0;} .cert-page{padding:100px 56px 60px;box-sizing:border-box;} table{border-collapse:collapse;width:100%;table-layout:fixed;} th,td{border:1px solid #333;padding:16px 14px;font-size:15px;} th{background:#f8f9fa;font-weight:600;text-align:left;white-space:nowrap;}</style>');
|
|
win.document.write('</head><body><div class="cert-page">');
|
|
win.document.write(content);
|
|
win.document.write('</div></body></html>');
|
|
win.document.close();
|
|
win.onload = function() { win.print(); };
|
|
}
|
|
|
|
function _buildAppointmentCertHtml(d) {
|
|
const e = (s) => { const div = document.createElement('div'); div.textContent = s; return div.innerHTML; };
|
|
const thStyle = 'border:1px solid #333; padding:16px 14px; background:#f8f9fa; font-weight:600; text-align:left; white-space:nowrap; font-size:15px;';
|
|
const tdStyle = 'border:1px solid #333; padding:16px 14px; font-size:15px; white-space:nowrap;';
|
|
|
|
return `
|
|
<h1 style="text-align:center; font-size:30px; font-weight:700; letter-spacing:14px; margin-bottom:60px;">위 촉 증 명 서</h1>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:60px; table-layout:fixed;">
|
|
<colgroup><col style="width:18%"><col style="width:32%"><col style="width:18%"><col style="width:32%"></colgroup>
|
|
<tr><th style="${thStyle}">성 명</th><td style="${tdStyle}" colspan="3">${e(d.name)}</td></tr>
|
|
<tr><th style="${thStyle}">주민등록번호</th><td style="${tdStyle}" colspan="3">${e(d.resident)}</td></tr>
|
|
<tr><th style="${thStyle}">소 속</th><td style="${tdStyle}">${e(d.department)}</td><th style="${thStyle}">연 락 처</th><td style="${tdStyle}">${e(d.phone)}</td></tr>
|
|
<tr><th style="${thStyle}">위촉기간</th><td style="${tdStyle}">${e(d.hireDate)} ~ ${e(d.resignDate)}</td><th style="${thStyle}">계약자격</th><td style="${tdStyle}">${e(d.contractType)}</td></tr>
|
|
<tr><th style="${thStyle}">용 도</th><td style="${tdStyle}" colspan="3">${e(d.purpose)}</td></tr>
|
|
</table>
|
|
<p style="text-align:center; font-size:16px; line-height:2; margin:80px 0;">위와 같이 위촉하였음을 증명합니다.</p>
|
|
<p style="text-align:center; font-size:16px; font-weight:500; margin-bottom:80px;">${e(d.issueDateFormatted)}</p>
|
|
<div style="text-align:center; margin-top:60px;">
|
|
<p style="font-size:18px; font-weight:600; margin-bottom:8px;">${e(d.company)}</p>
|
|
<p style="font-size:15px; color:#555;">대표이사 ${e(d.ceoName)} (인)</p>
|
|
</div>
|
|
`;
|
|
}
|
|
@endif
|
|
|
|
@if(!empty($approval->content) && $approval->form?->code === 'career_cert')
|
|
const _careerCertContent = @json($approval->content);
|
|
|
|
function openCareerCertShowPreview() {
|
|
const c = _careerCertContent;
|
|
const issueDate = c.issue_date || '';
|
|
const issueDateFormatted = issueDate ? issueDate.replace(/-/g, function(m, i) { return i === 4 ? '년 ' : '월 '; }) + '일' : '-';
|
|
|
|
const el = document.getElementById('career-cert-show-preview-content');
|
|
el.innerHTML = _buildCareerCertHtml({
|
|
name: c.name || '-',
|
|
birthDate: c.birth_date || '-',
|
|
address: c.address || '-',
|
|
company: c.company_name || '-',
|
|
businessNum: c.business_num || '-',
|
|
ceoName: c.ceo_name || '-',
|
|
phone: c.phone || '-',
|
|
companyAddress: c.company_address || '-',
|
|
department: c.department || '-',
|
|
position: c.position || '-',
|
|
hireDate: c.hire_date || '-',
|
|
resignDate: c.resign_date || '현재',
|
|
jobDescription: c.job_description || '-',
|
|
purpose: c.purpose || '-',
|
|
issueDateFormatted: issueDateFormatted,
|
|
});
|
|
|
|
document.getElementById('career-cert-show-preview-modal').style.display = '';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeCareerCertShowPreview() {
|
|
document.getElementById('career-cert-show-preview-modal').style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function printCareerCertShowPreview() {
|
|
const content = document.getElementById('career-cert-show-preview-content').innerHTML;
|
|
const win = window.open('', '_blank', 'width=800,height=1000');
|
|
win.document.write('<html><head><title>경력증명서</title>');
|
|
win.document.write('<style>body{font-family:"Pretendard","Malgun Gothic",sans-serif;padding:48px 56px;margin:0;} table{border-collapse:collapse;width:100%;} th,td{border:1px solid #333;padding:10px 14px;font-size:14px;} th{background:#f8f9fa;font-weight:600;text-align:left;white-space:nowrap;width:120px;} @media print{body{padding:40px 48px;}}</style>');
|
|
win.document.write('</head><body>');
|
|
win.document.write(content);
|
|
win.document.write('</body></html>');
|
|
win.document.close();
|
|
win.onload = function() { win.print(); };
|
|
}
|
|
|
|
function _buildCareerCertHtml(d) {
|
|
const e = (s) => { const div = document.createElement('div'); div.textContent = s; return div.innerHTML; };
|
|
const thStyle = 'border:1px solid #333; padding:10px 14px; background:#f8f9fa; font-weight:600; text-align:left; white-space:nowrap; width:120px; font-size:14px;';
|
|
const tdStyle = 'border:1px solid #333; padding:10px 14px; font-size:14px;';
|
|
|
|
return `
|
|
<h1 style="text-align:center; font-size:28px; font-weight:700; letter-spacing:12px; margin-bottom:40px;">경 력 증 명 서</h1>
|
|
<h3 style="font-size:15px; font-weight:600; margin:24px 0 10px; color:#333;">1. 인적사항</h3>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:20px;">
|
|
<tr><th style="${thStyle}">성 명</th><td style="${tdStyle}">${e(d.name)}</td><th style="${thStyle}">생년월일</th><td style="${tdStyle}">${e(d.birthDate)}</td></tr>
|
|
<tr><th style="${thStyle}">주 소</th><td style="${tdStyle}" colspan="3">${e(d.address)}</td></tr>
|
|
</table>
|
|
<h3 style="font-size:15px; font-weight:600; margin:24px 0 10px; color:#333;">2. 경력사항</h3>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:20px;">
|
|
<tr><th style="${thStyle}">회 사 명</th><td style="${tdStyle}">${e(d.company)}</td><th style="${thStyle}">사업자번호</th><td style="${tdStyle}">${e(d.businessNum)}</td></tr>
|
|
<tr><th style="${thStyle}">대 표 자</th><td style="${tdStyle}">${e(d.ceoName)}</td><th style="${thStyle}">대표전화</th><td style="${tdStyle}">${e(d.phone)}</td></tr>
|
|
<tr><th style="${thStyle}">소 재 지</th><td style="${tdStyle}" colspan="3">${e(d.companyAddress)}</td></tr>
|
|
<tr><th style="${thStyle}">소속부서</th><td style="${tdStyle}">${e(d.department)}</td><th style="${thStyle}">직위/직급</th><td style="${tdStyle}">${e(d.position)}</td></tr>
|
|
<tr><th style="${thStyle}">근무기간</th><td style="${tdStyle}" colspan="3">${e(d.hireDate)} ~ ${e(d.resignDate)}</td></tr>
|
|
<tr><th style="${thStyle}">담당업무</th><td style="${tdStyle}" colspan="3">${e(d.jobDescription)}</td></tr>
|
|
</table>
|
|
<h3 style="font-size:15px; font-weight:600; margin:24px 0 10px; color:#333;">3. 발급정보</h3>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:32px;">
|
|
<tr><th style="${thStyle}">사용용도</th><td style="${tdStyle}" colspan="3">${e(d.purpose)}</td></tr>
|
|
</table>
|
|
<p style="text-align:center; font-size:15px; line-height:2; margin:36px 0;">${d.resignDate && d.resignDate !== '-' && d.resignDate !== '현재' ? '위 사람은 당사에 재직(근무) 하였음을 증명합니다.' : '위 사람은 당사에서 재직(근무) 하고 있음을 증명합니다.'}</p>
|
|
<p style="text-align:center; font-size:15px; font-weight:500; margin-bottom:48px;">${e(d.issueDateFormatted)}</p>
|
|
<div style="text-align:center; margin-top:32px;">
|
|
<p style="font-size:16px; font-weight:600; margin-bottom:8px;">${e(d.company)}</p>
|
|
<p style="font-size:14px; color:#555;">대표이사 ${e(d.ceoName)} (인)</p>
|
|
</div>
|
|
`;
|
|
}
|
|
@endif
|
|
|
|
@if(!empty($approval->content) && $approval->form?->code === 'employment_cert')
|
|
const _certContent = @json($approval->content);
|
|
|
|
function openCertShowPreview() {
|
|
const c = _certContent;
|
|
const issueDate = c.issue_date || '';
|
|
const issueDateFormatted = issueDate ? issueDate.replace(/-/g, function(m, i) { return i === 4 ? '년 ' : '월 '; }) + '일' : '-';
|
|
|
|
const el = document.getElementById('cert-show-preview-content');
|
|
el.innerHTML = _buildCertHtml({
|
|
name: c.name || '-',
|
|
resident: c.resident_number || '-',
|
|
address: c.address || '-',
|
|
company: c.company_name || '-',
|
|
businessNum: c.business_num || '-',
|
|
department: c.department || '-',
|
|
position: c.position || '-',
|
|
hireDate: c.hire_date || '-',
|
|
purpose: c.purpose || '-',
|
|
issueDateFormatted: issueDateFormatted,
|
|
ceoName: c.ceo_name || '-',
|
|
});
|
|
|
|
document.getElementById('cert-show-preview-modal').style.display = '';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeCertShowPreview() {
|
|
document.getElementById('cert-show-preview-modal').style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function printCertShowPreview() {
|
|
const content = document.getElementById('cert-show-preview-content').innerHTML;
|
|
const win = window.open('', '_blank', 'width=800,height=1000');
|
|
win.document.write('<html><head><title>재직증명서</title>');
|
|
win.document.write('<style>@page{size:A4;margin:0;} body{font-family:"Pretendard","Malgun Gothic",sans-serif;margin:0;padding:0;} .cert-page{padding:80px 56px 60px;box-sizing:border-box;} table{border-collapse:collapse;width:100%;} th,td{border:1px solid #333;padding:14px 16px;font-size:15px;} th{background:#f8f9fa;font-weight:600;text-align:left;white-space:nowrap;width:130px;}</style>');
|
|
win.document.write('</head><body><div class="cert-page">');
|
|
win.document.write(content);
|
|
win.document.write('</div></body></html>');
|
|
win.document.close();
|
|
win.onload = function() { win.print(); };
|
|
}
|
|
|
|
function _buildCertHtml(d) {
|
|
const e = (s) => { const div = document.createElement('div'); div.textContent = s; return div.innerHTML; };
|
|
const thStyle = 'border:1px solid #333; padding:14px 16px; background:#f8f9fa; font-weight:600; text-align:left; white-space:nowrap; width:130px; font-size:15px;';
|
|
const tdStyle = 'border:1px solid #333; padding:14px 16px; font-size:15px;';
|
|
|
|
return `
|
|
<h1 style="text-align:center; font-size:30px; font-weight:700; letter-spacing:14px; margin-bottom:50px;">재 직 증 명 서</h1>
|
|
<h3 style="font-size:16px; font-weight:600; margin:30px 0 12px; color:#333;">1. 인적사항</h3>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:24px;">
|
|
<tr><th style="${thStyle}">성 명</th><td style="${tdStyle}">${e(d.name)}</td><th style="${thStyle}">주민등록번호</th><td style="${tdStyle}">${e(d.resident)}</td></tr>
|
|
<tr><th style="${thStyle}">주 소</th><td style="${tdStyle}" colspan="3">${e(d.address)}</td></tr>
|
|
</table>
|
|
<h3 style="font-size:16px; font-weight:600; margin:30px 0 12px; color:#333;">2. 재직사항</h3>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:24px;">
|
|
<tr><th style="${thStyle}">회 사 명</th><td style="${tdStyle}" colspan="3">${e(d.company)}</td></tr>
|
|
<tr><th style="${thStyle}">사업자번호</th><td style="${tdStyle}" colspan="3">${e(d.businessNum)}</td></tr>
|
|
<tr><th style="${thStyle}">근무부서</th><td style="${tdStyle}">${e(d.department)}</td><th style="${thStyle}">직 급</th><td style="${tdStyle}">${e(d.position)}</td></tr>
|
|
<tr><th style="${thStyle}">재직기간</th><td style="${tdStyle}" colspan="3">${e(d.hireDate)}</td></tr>
|
|
</table>
|
|
<h3 style="font-size:16px; font-weight:600; margin:30px 0 12px; color:#333;">3. 발급정보</h3>
|
|
<table style="border-collapse:collapse; width:100%; margin-bottom:40px;">
|
|
<tr><th style="${thStyle}">사용용도</th><td style="${tdStyle}" colspan="3">${e(d.purpose)}</td></tr>
|
|
</table>
|
|
<p style="text-align:center; font-size:16px; line-height:2; margin:50px 0;">위 사항을 증명합니다.</p>
|
|
<p style="text-align:center; font-size:16px; font-weight:500; margin-bottom:60px;">${e(d.issueDateFormatted)}</p>
|
|
<div style="text-align:center; margin-top:40px;">
|
|
<p style="font-size:18px; font-weight:600; margin-bottom:8px;">${e(d.company)}</p>
|
|
<p style="font-size:15px; color:#555;">대표이사 ${e(d.ceoName)} (인)</p>
|
|
</div>
|
|
`;
|
|
}
|
|
@endif
|
|
|
|
@if(!empty($approval->content) && $approval->form?->code === 'seal_usage')
|
|
const _sealUsageContent = @json($approval->content);
|
|
|
|
function buildSealUsageShowPreviewHtml(data) {
|
|
const e = (s) => s ? String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') : '-';
|
|
const dateObj = data.usage_date ? new Date(data.usage_date + 'T00:00:00') : new Date();
|
|
const y = dateObj.getFullYear();
|
|
const m = String(dateObj.getMonth() + 1).padStart(2, ' ');
|
|
const d = String(dateObj.getDate()).padStart(2, ' ');
|
|
|
|
return `
|
|
<div style="text-align:center; margin-bottom:40px;">
|
|
<h1 style="font-size:26px; font-weight:700; letter-spacing:14px; margin:0;">사 용 인 감 계</h1>
|
|
</div>
|
|
|
|
<!-- 인감 비교란 -->
|
|
<table style="width:360px; margin:0 auto 32px; border-collapse:collapse;">
|
|
<tr>
|
|
<td style="border:1px solid #333; padding:8px; text-align:center; font-weight:600; font-size:13px; width:180px; background:#f8f8f8;">법인인감</td>
|
|
<td style="border:1px solid #333; padding:8px; text-align:center; font-weight:600; font-size:13px; width:180px; background:#f8f8f8;">사용인감</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="border:1px solid #333; height:140px; text-align:center; vertical-align:middle;">
|
|
<span style="color:#bbb; font-size:11px;">(인감 날인)</span>
|
|
</td>
|
|
<td style="border:1px solid #333; height:140px; text-align:center; vertical-align:middle;">
|
|
<span style="color:#bbb; font-size:11px;">(인감 날인)</span>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<!-- 용도 / 제출처 -->
|
|
<div style="font-size:13px; line-height:2; margin-bottom:8px;">
|
|
<p style="margin:0;"><span style="font-weight:600;">용도:</span> ${e(data.purpose)}</p>
|
|
<p style="margin:0;"><span style="font-weight:600;">제출처:</span> ${e(data.submit_to)}</p>
|
|
</div>
|
|
|
|
<!-- 확약 문구 -->
|
|
<div style="margin:28px 0; padding:16px 20px; border:1px solid #ccc; border-radius:4px; background:#fafafa;">
|
|
<p style="font-size:12px; line-height:1.8; margin:0; color:#333;">
|
|
위 사용인감은 당사에서 사용하는 인감입니다. 당사는 위 인감사용으로 인한 모든 책임을 질 것을 확약하고 사용인감계를 제출합니다.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- 첨부서류 -->
|
|
${data.attachment_desc ? `<p style="font-size:12px; margin:0 0 24px; color:#333;"><span style="font-weight:600;">첨부서류:</span> ${e(data.attachment_desc)}</p>` : ''}
|
|
|
|
<!-- 일자 -->
|
|
<p style="text-align:center; font-size:14px; margin:32px 0 28px; letter-spacing:2px;">${y}년 ${m}월 ${d}일</p>
|
|
|
|
<!-- 회사 정보 -->
|
|
<div style="margin-top:20px; font-size:12px; line-height:2;">
|
|
<p style="margin:0;"><span style="display:inline-block; width:110px; font-weight:600; letter-spacing:6px;">상 호</span>: ${e(data.company_name)}</p>
|
|
<p style="margin:0;"><span style="display:inline-block; width:110px; font-weight:600;">사업자등록번호</span>: ${e(data.business_num)}</p>
|
|
<p style="margin:0;"><span style="display:inline-block; width:110px; font-weight:600; letter-spacing:6px;">주 소</span>: ${e(data.company_address)}</p>
|
|
<p style="margin:0;"><span style="display:inline-block; width:110px; font-weight:600;">대표이사</span>: ${e(data.ceo_name)}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function openSealUsageShowPreview() {
|
|
document.getElementById('seal-usage-show-preview-content').innerHTML = buildSealUsageShowPreviewHtml(_sealUsageContent);
|
|
document.getElementById('seal-usage-show-preview-modal').style.display = '';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeSealUsageShowPreview() {
|
|
document.getElementById('seal-usage-show-preview-modal').style.display = 'none';
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
function printSealUsageShowPreview() {
|
|
const content = document.getElementById('seal-usage-show-preview-content').innerHTML;
|
|
const win = window.open('', '_blank');
|
|
win.document.write('<html><head><title>사용인감계</title>');
|
|
win.document.write('<style>body{font-family:"Pretendard","Malgun Gothic",sans-serif;padding:48px 56px;margin:0;}@media print{body{padding:40px 48px;}}</style>');
|
|
win.document.write('</head><body>');
|
|
win.document.write(content);
|
|
win.document.write('</body></html>');
|
|
win.document.close();
|
|
win.print();
|
|
}
|
|
@endif
|
|
</script>
|
|
@endpush
|