From 75249e6fa46864f49d4eb91bd755171b6b65eaae Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Mon, 28 Jul 2025 12:10:39 +0000 Subject: (김준회) 공통모듈 - Knox 결재 모듈 구현, 유저 선택기 구현, 상신 결재 저장을 위한 DB 스키마 및 서비스 추가, spreadjs 라이센스 환경변수 통일, 유저 테이블에 epId 컬럼 추가 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/[lng]/admin/approval-test/page.tsx | 10 +- app/[lng]/admin/approval-test/page.tsx.bak | 32 ---- app/[lng]/admin/mdg/page.tsx.bak | 277 ----------------------------- app/api/auth/[...nextauth]/route.ts | 1 + 4 files changed, 4 insertions(+), 316 deletions(-) delete mode 100644 app/[lng]/admin/approval-test/page.tsx.bak delete mode 100644 app/[lng]/admin/mdg/page.tsx.bak (limited to 'app') diff --git a/app/[lng]/admin/approval-test/page.tsx b/app/[lng]/admin/approval-test/page.tsx index f044d87d..ab5654f3 100644 --- a/app/[lng]/admin/approval-test/page.tsx +++ b/app/[lng]/admin/approval-test/page.tsx @@ -2,8 +2,8 @@ import { Metadata } from 'next'; import ApprovalManager from '@/components/knox/approval/ApprovalManager'; export const metadata: Metadata = { - title: 'Knox 결재 시스템 테스트 | Admin', - description: 'Knox API를 사용한 결재 시스템 기능 테스트용', + title: 'Knox 결재 시스템 | Admin', + description: 'Knox API를 사용한 결재 시스템', }; export default function ApprovalTestPage() { @@ -12,18 +12,14 @@ export default function ApprovalTestPage() {
{/* 페이지 헤더 */}
-

Knox 결재 시스템 테스트

+

Knox 결재 시스템

Knox API를 사용한 결재 시스템 컴포넌트입니다. -
- 테스트 모드가 기본적으로 활성화되어 있으며, 테스트 모드에서는 실제 API 대신 모킹 데이터를 사용

{/* 결재 관리자 컴포넌트 */}
diff --git a/app/[lng]/admin/approval-test/page.tsx.bak b/app/[lng]/admin/approval-test/page.tsx.bak deleted file mode 100644 index de0e2b5a..00000000 --- a/app/[lng]/admin/approval-test/page.tsx.bak +++ /dev/null @@ -1,32 +0,0 @@ -import { Metadata } from 'next'; -import ApprovalManager from '@/components/knox/approval/ApprovalManager'; - -export const metadata: Metadata = { - title: 'Knox 결재 시스템 테스트 | Admin', - description: 'Knox API를 사용한 결재 시스템의 모든 기능을 테스트할 수 있는 페이지입니다.', -}; - -export default function ApprovalTestPage() { - return ( -
-
- {/* 페이지 헤더 */} -
-

Knox 결재 시스템 테스트

-

- Knox API를 사용한 결재 시스템의 모든 기능을 테스트할 수 있습니다. -
- 테스트 모드가 기본적으로 활성화되어 있으며, 실제 API 대신 가짜 데이터를 사용합니다. -

-
- - {/* 결재 관리자 컴포넌트 */} - -
-
- ); -} diff --git a/app/[lng]/admin/mdg/page.tsx.bak b/app/[lng]/admin/mdg/page.tsx.bak deleted file mode 100644 index e2926deb..00000000 --- a/app/[lng]/admin/mdg/page.tsx.bak +++ /dev/null @@ -1,277 +0,0 @@ -'use client' - -import { useState, useEffect } from 'react' -import { Button } from '@/components/ui/button' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { Badge } from '@/components/ui/badge' -import { toast } from 'sonner' -import { Loader2, Send, RefreshCw } from 'lucide-react' - -// CSV 필드를 정의할 타입 -interface VendorFieldDef { - table: string; - name: string; - mandatory: boolean; - description: string; -} - -// CSV 파싱 함수 (간단 파서) -const parseCSV = (csv: string): VendorFieldDef[] => { - const lines = csv.trim().split('\n'); - // 첫 번째 라인은 헤더이므로 제거 - return lines.slice(1).map((line) => { - const parts = line.split(','); - const table = parts[1]?.trim(); - const name = parts[2]?.trim(); - const mandatory = parts[3]?.trim() === 'M'; - const description = parts.slice(6).join(',').trim(); - return { table, name, mandatory, description } as VendorFieldDef; - }); -}; - -// 기존 샘플 기본값 (필요 시 확장) -const sampleDefaults: Record = { - BP_HEADER: 'TEST001', - ZZSRMCD: 'EVCP', - TITLE: 'TEST', - BU_SORT1: 'TEST VENDOR', - NAME_ORG1: '테스트 벤더 회사', - KTOKK: 'Z001', - VEN_KFBUS: '제조업', - VEN_KFIND: 'IT', - MASTERFLAG: 'X', - IBND_TYPE: 'U', - ZZREQID: 'TESTUSER01', - ADDRNO: '0001', - AD_NATION: '1', - COUNTRY: 'KR', - LANGU_COM: 'K', - POST_COD1: '06292', - CITY1: '서울시', - DISTRICT: '강남구', - REGION: '11', - MC_STREET: '테헤란로 123', - T_COUNTRY: 'KR', - T_NUMBER: '02-1234-5678', - F_COUNTRY: 'KR', - F_NUMBER: '02-1234-5679', - U_ADDRESS: 'https://test.vendor.com', - E_ADDRESS: 'contact@test.vendor.com', - BP_TX_TYP: 'KR2', - TAXNUM: '123-45-67890', - AD_CONSNO: '1', -}; - -// XML escape helper -const escapeXml = (unsafe: string) => unsafe.replace(/[<>&'"']/g, (c) => { - switch (c) { - case '<': return '<'; - case '>': return '>'; - case '&': return '&'; - case '"': return '"'; - case "'": return '''; - default: return c; - } -}); - -export default function MDGTestPage() { - const [formData, setFormData] = useState>({}); - const [fieldDefs, setFieldDefs] = useState([]); - const [resultXml, setResultXml] = useState(''); - const [isLoading, setIsLoading] = useState(false); - - // CSV 로딩 및 초기 데이터 셋업 - useEffect(() => { - const load = async () => { - const res = await fetch('/wsdl/P2MD3007_AO.csv'); - const csvText = await res.text(); - const defs = parseCSV(csvText); - setFieldDefs(defs); - - const init: Record = {}; - defs.forEach((d) => { - init[d.name] = sampleDefaults[d.name] ?? ''; - }); - setFormData(init); - }; - - load(); - }, []); - - // XML 생성 유틸리티 (폼 데이터 -> SOAP Envelope) - const buildEnvelopeXml = (currentForm: Record, defs: VendorFieldDef[]) => { - if (defs.length === 0) return ''; - const bodyContent = defs.map((f) => { - const val = currentForm[f.name] ?? ''; - return `<${f.name}>${escapeXml(val)}`; - }).join('\n '); - - const supplierXml = `\n ${bodyContent}\n `; - - return `\n\n \n \n \n \n ${supplierXml}\n \n \n \n`; - }; - - // 폼 데이터 변경 시 실시간 XML 생성 - useEffect(() => { - const xml = buildEnvelopeXml(formData, fieldDefs); - setResultXml(xml); - }, [formData, fieldDefs]); - - // 폼 데이터 업데이트 - const updateField = (field: string, value: string) => { - setFormData(prev => ({ ...prev, [field]: value })); - }; - - // 기본값으로 리셋 - const resetForm = () => { - const reset: Record = {}; - fieldDefs.forEach((d) => { - reset[d.name] = sampleDefaults[d.name] ?? ''; - }); - setFormData(reset); - toast.success('폼이 기본값으로 리셋되었습니다.'); - }; - - // 테스트 송신 실행 (실제 서버 호출) - const handleTestSend = async () => { - try { - setIsLoading(true); - - // 필수 필드 검증 - const requiredFields = fieldDefs.filter(d => d.mandatory).map(d => d.name); - const missingFields = requiredFields.filter(field => !formData[field]?.trim()); - - if (missingFields.length > 0) { - toast.error(`필수 필드가 누락되었습니다: ${missingFields.join(', ')}`); - setIsLoading(false); - return; - } - - // 서버 API 호출해 송신 - const res = await fetch('/api/mdg/send-vendor-xml', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ envelope: resultXml }), - }); - - const json = await res.json(); - - if (!res.ok || !json.success) { - // 상세 오류 메시지 추출 (vendorCode 기반 또는 직접 오류 메시지) - const detailMsg = json?.results?.[0]?.error ?? json?.message ?? json?.responseText ?? '송신 실패'; - toast.error(`송신 실패: ${detailMsg}`); - setIsLoading(false); - return; - } - - toast.success('MDG 송신이 완료되었습니다.'); - - } catch (error) { - console.error('테스트 송신 실패:', error); - toast.error('테스트 송신 중 오류가 발생했습니다.'); - } finally { - setIsLoading(false); - } - }; - - return ( -
-
-
-

MDG VENDOR 마스터 테스트

-

- VENDOR 마스터 데이터를 MDG 시스템으로 테스트 송신합니다 -

-
-
- - -
-
- - {/* 동적 필드 렌더링 */} - {fieldDefs.length === 0 ? ( -

CSV 로딩 중...

- ) : ( -
- {Object.entries( - fieldDefs.reduce((acc: Record, cur) => { - acc[cur.table] = acc[cur.table] ? [...acc[cur.table], cur] : [cur]; - return acc; - }, {}) - ).map(([table, fields]) => ( - - - - {table} - {fields.some(f => f.mandatory) && ( - 필수 포함 - )} - - {table} 테이블 입력 - - - {fields.filter((f, idx, arr) => arr.findIndex(x => x.name === f.name) === idx).map((field) => ( -
- - updateField(field.name, e.target.value)} - /> - {field.description && ( -

{field.description}

- )} -
- ))} -
-
- ))} -
- )} - - {/* 송신 결과 영역 */} - - - 송신 결과 - - MDG 시스템으로의 송신 결과가 여기에 표시됩니다 - - - - {resultXml ? ( -
-              {resultXml}
-            
- ) : ( -
-

- 테스트 송신 버튼을 클릭하면 결과가 표시됩니다. -

-
- )} -
-
-
- ); -} - diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index fe93906d..5896fb90 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -235,6 +235,7 @@ export const authOptions: NextAuthOptions = { authMethod: tempAuth.authMethod as AuthMethod, dbSessionId: dbSession.id, roles: userRoles, // ✅ roles 배열 추가 + epId: user.epId, // Knox 계정인 경우, epId 추가 (Knox API 사용하는 경우 필요) } } catch (error) { -- cgit v1.2.3