From b82cba0cc91a7dbffbcd7c62aff0c24b6f7ce52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Thu, 5 Mar 2026 19:17:21 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[approval]=20=EC=9E=AC=EC=A7=81=EC=A6=9D?= =?UTF-8?q?=EB=AA=85=EC=84=9C=20DOCX=20=EC=83=9D=EC=84=B1=EC=9D=84=20PhpWo?= =?UTF-8?q?rd=20=EC=A7=81=EC=A0=91=20=EC=83=9D=EC=84=B1=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 외부 템플릿 파일(employment_cert.docx) 의존성 제거 - PhpWord로 테이블/텍스트 직접 생성하여 서버 배포 시 템플릿 누락 문제 해결 --- app/Services/EmploymentCertService.php | 141 ++++++++++++++++++++----- 1 file changed, 117 insertions(+), 24 deletions(-) diff --git a/app/Services/EmploymentCertService.php b/app/Services/EmploymentCertService.php index c5f9cb1e..1029b516 100644 --- a/app/Services/EmploymentCertService.php +++ b/app/Services/EmploymentCertService.php @@ -6,7 +6,9 @@ use App\Models\HR\Employee; use App\Models\Tenants\Tenant; use Illuminate\Support\Str; -use PhpOffice\PhpWord\TemplateProcessor; +use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\SimpleType\Jc; +use PhpOffice\PhpWord\SimpleType\TblWidth; class EmploymentCertService { @@ -45,17 +47,20 @@ public function getCertInfo(int $userId, int $tenantId): array } /** - * DOCX 생성 + * DOCX 생성 (PhpWord 직접 생성 - 외부 템플릿 불필요) */ public function generateDocx(array $data, int $tenantId): string { - $templatePath = storage_path('app/templates/employment_cert.docx'); + $phpWord = new PhpWord; + $phpWord->setDefaultFontName('맑은 고딕'); + $phpWord->setDefaultFontSize(11); - if (! file_exists($templatePath)) { - throw new \RuntimeException('재직증명서 템플릿 파일이 없습니다.'); - } - - $template = new TemplateProcessor($templatePath); + $section = $phpWord->addSection([ + 'marginTop' => 1440, // 1 inch + 'marginBottom' => 1440, + 'marginLeft' => 1440, + 'marginRight' => 1440, + ]); $hireDateFormatted = ''; if (! empty($data['hire_date'])) { @@ -65,34 +70,122 @@ public function generateDocx(array $data, int $tenantId): string $hireDateFormatted = $data['hire_date']; } } - $issueDateFormatted = date('Y년 m월 d일'); - $template->setValue('name', $data['name'] ?? ''); - $template->setValue('resident_number', $data['resident_number_full'] ?? ''); - $template->setValue('address', $data['address'] ?? ''); - $template->setValue('company_name', $data['company_name'] ?? ''); - $template->setValue('business_num', $data['business_num'] ?? ''); - $template->setValue('ceo_name', $data['ceo_name'] ?? ''); - $template->setValue('phone', $data['phone'] ?? ''); - $template->setValue('company_address', $data['company_address'] ?? ''); - $template->setValue('department', $data['department'] ?? ''); - $template->setValue('position', $data['position'] ?? ''); - $template->setValue('hire_date', $hireDateFormatted); - $template->setValue('purpose', $data['purpose'] ?? ''); - $template->setValue('issue_date', $issueDateFormatted); + // 제목 + $section->addText('재 직 증 명 서', [ + 'size' => 22, + 'bold' => true, + ], ['alignment' => Jc::CENTER, 'spaceAfter' => 400]); + $section->addTextBreak(); + + // === 1. 인적사항 === + $section->addText('1. 인적사항', ['size' => 12, 'bold' => true], ['spaceAfter' => 120]); + + $tableStyle = [ + 'borderSize' => 6, + 'borderColor' => '333333', + 'cellMargin' => 80, + 'width' => 100 * 50, + 'unit' => TblWidth::PERCENT, + ]; + $thStyle = ['bgColor' => 'F8F9FA', 'valign' => 'center']; + $thFont = ['size' => 10, 'bold' => true]; + $tdFont = ['size' => 10]; + + $table = $section->addTable($tableStyle); + + $table->addRow(400); + $table->addCell(1800, $thStyle)->addText('성 명', $thFont); + $table->addCell(3200)->addText($data['name'] ?? '', $tdFont); + $table->addCell(1800, $thStyle)->addText('주민등록번호', $thFont); + $table->addCell(3200)->addText($data['resident_number_full'] ?? '', $tdFont); + + $table->addRow(400); + $table->addCell(1800, $thStyle)->addText('주 소', $thFont); + $table->addCell(8200, ['gridSpan' => 3])->addText($data['address'] ?? '', $tdFont); + + $section->addTextBreak(); + + // === 2. 재직사항 === + $section->addText('2. 재직사항', ['size' => 12, 'bold' => true], ['spaceAfter' => 120]); + + $table2 = $section->addTable($tableStyle); + + $table2->addRow(400); + $table2->addCell(1800, $thStyle)->addText('회 사 명', $thFont); + $table2->addCell(8200, ['gridSpan' => 3])->addText($data['company_name'] ?? '', $tdFont); + + $table2->addRow(400); + $table2->addCell(1800, $thStyle)->addText('사업자번호', $thFont); + $table2->addCell(8200, ['gridSpan' => 3])->addText($data['business_num'] ?? '', $tdFont); + + $table2->addRow(400); + $table2->addCell(1800, $thStyle)->addText('근무부서', $thFont); + $table2->addCell(3200)->addText($data['department'] ?? '', $tdFont); + $table2->addCell(1800, $thStyle)->addText('직 급', $thFont); + $table2->addCell(3200)->addText($data['position'] ?? '', $tdFont); + + $table2->addRow(400); + $table2->addCell(1800, $thStyle)->addText('재직기간', $thFont); + $table2->addCell(8200, ['gridSpan' => 3])->addText($hireDateFormatted.' ~ 현재', $tdFont); + + $section->addTextBreak(); + + // === 3. 발급정보 === + $section->addText('3. 발급정보', ['size' => 12, 'bold' => true], ['spaceAfter' => 120]); + + $table3 = $section->addTable($tableStyle); + + $table3->addRow(400); + $table3->addCell(1800, $thStyle)->addText('사용용도', $thFont); + $table3->addCell(8200, ['gridSpan' => 3])->addText($data['purpose'] ?? '', $tdFont); + + $section->addTextBreak(2); + + // 증명 문구 + $section->addText( + '위 사항을 증명합니다.', + ['size' => 12], + ['alignment' => Jc::CENTER, 'spaceBefore' => 400, 'spaceAfter' => 400] + ); + + $section->addTextBreak(); + + // 발급일 + $section->addText( + $issueDateFormatted, + ['size' => 12, 'bold' => true], + ['alignment' => Jc::CENTER, 'spaceAfter' => 600] + ); + + $section->addTextBreak(); + + // 회사명 + 대표이사 + $section->addText( + ($data['company_name'] ?? ''), + ['size' => 14, 'bold' => true], + ['alignment' => Jc::CENTER] + ); + $section->addText( + '대표이사 '.($data['ceo_name'] ?? '').' (인)', + ['size' => 12], + ['alignment' => Jc::CENTER] + ); + + // 파일 저장 $outputDir = storage_path("app/approvals/{$tenantId}"); if (! is_dir($outputDir)) { mkdir($outputDir, 0755, true); } - $fileName = '재직증명서_'.($data['name'] ?? 'unknown').'_'.date('Ymd').'.docx'; $storedName = Str::random(40).'.docx'; $storagePath = "approvals/{$tenantId}/{$storedName}"; $fullPath = storage_path("app/{$storagePath}"); - $template->saveAs($fullPath); + $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); + $objWriter->save($fullPath); return $storagePath; }