summaryrefslogtreecommitdiff
path: root/lib/vendors/approval-actions.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendors/approval-actions.ts')
-rw-r--r--lib/vendors/approval-actions.ts177
1 files changed, 133 insertions, 44 deletions
diff --git a/lib/vendors/approval-actions.ts b/lib/vendors/approval-actions.ts
index 69d09caa..1ce96078 100644
--- a/lib/vendors/approval-actions.ts
+++ b/lib/vendors/approval-actions.ts
@@ -2,9 +2,132 @@
import { ApprovalSubmissionSaga } from '@/lib/approval'
import type { ApprovalConfig } from '@/lib/approval/types'
+import { htmlTableConverter, htmlDescriptionList } from '@/lib/approval'
import db from '@/db/db'
-import { vendors } from '@/db/schema/vendors'
-import { inArray } from 'drizzle-orm'
+import { vendors, vendorTypes } from '@/db/schema/vendors'
+import { inArray, eq } from 'drizzle-orm'
+
+/**
+ * 벤더 승인 결재 템플릿 변수 생성
+ *
+ * @param vendorIds - 벤더 ID 목록
+ * @param currentUserEmail - 현재 사용자 이메일
+ * @returns 템플릿 변수 객체 및 벤더 정보
+ */
+export async function prepareVendorApprovalVariables(
+ vendorIds: number[],
+ currentUserEmail?: string
+) {
+ // 벤더 정보 조회
+ const vendorRecords = await db
+ .select({
+ id: vendors.id,
+ vendorName: vendors.vendorName,
+ vendorCode: vendors.vendorCode,
+ email: vendors.email,
+ status: vendors.status,
+ taxId: vendors.taxId,
+ country: vendors.country,
+ representativeName: vendors.representativeName,
+ phone: vendors.phone,
+ website: vendors.website,
+ representativeEmail: vendors.representativeEmail,
+ postalCode: vendors.postalCode,
+ address: vendors.address,
+ addressDetail: vendors.addressDetail,
+ corporateRegistrationNumber: vendors.corporateRegistrationNumber,
+ businessSize: vendors.businessSize,
+ vendorTypeId: vendors.vendorTypeId,
+ vendorTypeName: vendorTypes.nameKo,
+ })
+ .from(vendors)
+ .leftJoin(vendorTypes, eq(vendors.vendorTypeId, vendorTypes.id))
+ .where(inArray(vendors.id, vendorIds))
+
+ if (vendorRecords.length === 0) {
+ throw new Error(`벤더를 찾을 수 없습니다: ${vendorIds.join(', ')}`)
+ }
+
+ // PENDING_REVIEW 상태가 아닌 벤더 확인
+ const invalidVendors = vendorRecords.filter(v => v.status !== 'PENDING_REVIEW')
+ if (invalidVendors.length > 0) {
+ throw new Error(
+ `가입 신청 중(PENDING_REVIEW) 상태의 벤더만 승인할 수 있습니다. ` +
+ `잘못된 상태: ${invalidVendors.map(v => `${v.vendorName}(${v.status})`).join(', ')}`
+ )
+ }
+
+ // 업체규모 매핑
+ const businessSizeMap: Record<string, string> = {
+ 'A': '대기업',
+ 'B': '중견기업',
+ 'C': '중소기업',
+ 'D': '소기업',
+ }
+
+ // 벤더 목록 테이블 생성
+ const vendorListTable = await htmlTableConverter(
+ vendorRecords.map(v => ({
+ vendorName: v.vendorName || '-',
+ representativeName: v.representativeName || '-',
+ vendorType: v.vendorTypeName || '-',
+ businessSize: v.businessSize ? businessSizeMap[v.businessSize] || v.businessSize : '-',
+ phone: v.phone || '-',
+ email: v.representativeEmail || '-',
+ })),
+ [
+ { key: 'vendorName', label: '업체명' },
+ { key: 'representativeName', label: '대표자명' },
+ { key: 'vendorType', label: '업체유형' },
+ { key: 'businessSize', label: '기업규모' },
+ { key: 'phone', label: '전화번호' },
+ { key: 'email', label: '이메일' },
+ ]
+ )
+
+ // 벤더별 상세 정보
+ const vendorDetailsHtml = (
+ await Promise.all(
+ vendorRecords.map(async (v) => {
+ const details = await htmlDescriptionList([
+ { label: '업체명', value: v.vendorName || '-' },
+ { label: '대표자명', value: v.representativeName || '-' },
+ { label: '업체유형', value: v.vendorTypeName || '-' },
+ { label: '기업규모', value: v.businessSize ? businessSizeMap[v.businessSize] || v.businessSize : '-' },
+ { label: '국가', value: v.country || '-' },
+ { label: '우편번호', value: v.postalCode || '-' },
+ { label: '주소', value: v.address || '-' },
+ { label: '상세주소', value: v.addressDetail || '-' },
+ { label: '전화번호', value: v.phone || '-' },
+ { label: '이메일', value: v.representativeEmail || '-' },
+ { label: '홈페이지', value: v.website || '-' },
+ { label: '사업자등록번호', value: v.taxId || '-' },
+ { label: '법인등록번호', value: v.corporateRegistrationNumber || '-' },
+ ])
+ return `<div style="margin-bottom: 30px;"><h3 style="font-size: 16px; font-weight: bold; margin-bottom: 10px; color: #333;">${v.vendorName}</h3>${details}</div>`
+ })
+ )
+ ).join('')
+
+ const variables: Record<string, string> = {
+ '업체수': String(vendorRecords.length),
+ '업체목록': vendorRecords.map(v => v.vendorName).join(', '),
+ '업체목록테이블': vendorListTable,
+ '업체상세정보': vendorDetailsHtml,
+ '요청일': new Date().toLocaleDateString('ko-KR', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ }),
+ '요청자': currentUserEmail || '시스템',
+ }
+
+ return {
+ variables,
+ vendorRecords,
+ vendorNames: vendorRecords.map(v => v.vendorName).join(', '),
+ }
+}
/**
* 벤더 가입 승인 결재 상신
@@ -33,49 +156,15 @@ export async function approveVendorsWithApproval(input: {
throw new Error('승인할 벤더를 선택해주세요.')
}
- // 2. 벤더 정보 조회
- const vendorRecords = await db
- .select({
- id: vendors.id,
- vendorName: vendors.vendorName,
- vendorCode: vendors.vendorCode,
- email: vendors.email,
- status: vendors.status,
- taxId: vendors.taxId,
- country: vendors.country,
- })
- .from(vendors)
- .where(inArray(vendors.id, input.vendorIds))
-
- if (vendorRecords.length === 0) {
- throw new Error(`벤더를 찾을 수 없습니다: ${input.vendorIds.join(', ')}`)
- }
-
- // 3. PENDING_REVIEW 상태가 아닌 벤더 확인
- const invalidVendors = vendorRecords.filter(v => v.status !== 'PENDING_REVIEW')
- if (invalidVendors.length > 0) {
- throw new Error(
- `가입 신청 중(PENDING_REVIEW) 상태의 벤더만 승인할 수 있습니다. ` +
- `잘못된 상태: ${invalidVendors.map(v => `${v.vendorName}(${v.status})`).join(', ')}`
- )
- }
-
- console.log(`[Vendor Approval Action] ${vendorRecords.length}개 벤더 조회 완료`)
-
- // 4. 템플릿 변수 준비 (TODO: 실제 템플릿에 맞게 수정 필요)
- const variables: Record<string, string> = {
- // TODO: 다음 대화에서 제공될 템플릿에 맞게 변수 매핑
- '업체수': String(vendorRecords.length),
- '업체목록': vendorRecords.map(v =>
- `${v.vendorName} (${v.vendorCode || '코드 미할당'})`
- ).join('\n'),
- '요청일': new Date().toLocaleDateString('ko-KR'),
- '요청자': input.currentUser.email || 'Unknown',
- }
+ // 2. 템플릿 변수 준비
+ const { variables, vendorRecords, vendorNames } = await prepareVendorApprovalVariables(
+ input.vendorIds,
+ input.currentUser.email
+ )
- console.log(`[Vendor Approval Action] 템플릿 변수 준비 완료`)
+ console.log(`[Vendor Approval Action] ${vendorRecords.length}개 벤더 조회 및 템플릿 변수 준비 완료`)
- // 5. 결재 상신 (Saga 패턴)
+ // 3. 결재 상신 (Saga 패턴)
const saga = new ApprovalSubmissionSaga(
'vendor_approval', // 핸들러 타입 (handlers-registry에 등록될 키)
{
@@ -84,7 +173,7 @@ export async function approveVendorsWithApproval(input: {
},
{
title: `벤더 가입 승인 요청 - ${vendorRecords.length}개 업체`,
- description: `${vendorRecords.map(v => v.vendorName).join(', ')} 의 가입을 승인합니다.`,
+ description: `${vendorNames} 의 가입을 승인합니다.`,
templateName: '벤더 가입 승인 요청',
variables,
approvers: input.approvers,