diff --git a/src/components/accounting/DepositManagement/DepositDetailClientV2.tsx b/src/components/accounting/DepositManagement/DepositDetailClientV2.tsx index b5e646a5..0de82619 100644 --- a/src/components/accounting/DepositManagement/DepositDetailClientV2.tsx +++ b/src/components/accounting/DepositManagement/DepositDetailClientV2.tsx @@ -71,10 +71,6 @@ export default function DepositDetailClientV2({ toast.error('거래처를 선택해주세요.'); return { success: false, error: '거래처를 선택해주세요.' }; } - if (submitData.depositType === 'unset') { - toast.error('입금 유형을 선택해주세요.'); - return { success: false, error: '입금 유형을 선택해주세요.' }; - } const result = mode === 'create' diff --git a/src/components/accounting/WithdrawalManagement/WithdrawalDetailClientV2.tsx b/src/components/accounting/WithdrawalManagement/WithdrawalDetailClientV2.tsx index c2211b1a..97b21cc7 100644 --- a/src/components/accounting/WithdrawalManagement/WithdrawalDetailClientV2.tsx +++ b/src/components/accounting/WithdrawalManagement/WithdrawalDetailClientV2.tsx @@ -70,10 +70,6 @@ export default function WithdrawalDetailClientV2({ toast.error('거래처를 선택해주세요.'); return { success: false, error: '거래처를 선택해주세요.' }; } - if (submitData.withdrawalType === 'unset') { - toast.error('출금 유형을 선택해주세요.'); - return { success: false, error: '출금 유형을 선택해주세요.' }; - } const result = mode === 'create' diff --git a/src/components/approval/DocumentCreate/index.tsx b/src/components/approval/DocumentCreate/index.tsx index 84430909..d9b18256 100644 --- a/src/components/approval/DocumentCreate/index.tsx +++ b/src/components/approval/DocumentCreate/index.tsx @@ -19,7 +19,9 @@ import { updateApproval, updateAndSubmitApproval, deleteApproval, + getEmployees, } from './actions'; +import { useAuth } from '@/contexts/AuthContext'; import { Button } from '@/components/ui/button'; import { BasicInfoSection } from './BasicInfoSection'; import { ApprovalLineSection } from './ApprovalLineSection'; @@ -82,6 +84,7 @@ export function DocumentCreate() { const router = useRouter(); const searchParams = useSearchParams(); const [isPending, startTransition] = useTransition(); + const { currentUser } = useAuth(); // 수정 모드 / 복제 모드 상태 const documentId = searchParams.get('id'); @@ -121,21 +124,57 @@ export function DocumentCreate() { const [isPreviewOpen, setIsPreviewOpen] = useState(false); // ===== DevFill: 자동 입력 기능 ===== - useDevFill('purchaseApproval', useCallback(() => { + useDevFill('purchaseApproval', useCallback(async () => { if (!isEditMode && !isCopyMode) { const mockData = generatePurchaseApprovalData(); + + // 직원 목록 가져오기 + const employees = await getEmployees(); + + // localStorage에서 실제 로그인 사용자 이름 가져오기 (우측 상단 표시와 동일한 소스) + const userDataStr = localStorage.getItem("user"); + const currentUserName = userDataStr ? JSON.parse(userDataStr).name : currentUser?.name; + + // 현재 사용자 이름으로 결재선에 추가할 직원 찾기 + const approver = currentUserName + ? employees.find(e => e.name === currentUserName) + : null; + + // 경리/회계/재무 부서 직원 중 랜덤 1명 참조 추가 + const accountingDepts = ['경리', '회계', '재무']; + const accountingStaff = employees.filter(e => + accountingDepts.some(dept => e.department?.includes(dept)) + ); + const randomReference = accountingStaff.length > 0 + ? accountingStaff[Math.floor(Math.random() * accountingStaff.length)] + : null; + setBasicInfo(prev => ({ ...prev, ...mockData.basicInfo, draftDate: prev.draftDate || mockData.basicInfo.draftDate, })); + + // 결재선: 현재 사용자가 직원 목록에 있으면 설정, 없으면 랜덤 1명 + if (approver) { + setApprovalLine([approver]); + } else if (employees.length > 0) { + const randomApprover = employees[Math.floor(Math.random() * employees.length)]; + setApprovalLine([randomApprover]); + } + + // 참조: 경리/회계/재무 직원이 있으면 설정 + if (randomReference) { + setReferences([randomReference]); + } + setProposalData(prev => ({ ...prev, ...mockData.proposalData, })); toast.success('지출결의서 데이터가 자동 입력되었습니다.'); } - }, [isEditMode, isCopyMode])); + }, [isEditMode, isCopyMode, currentUser?.name])); // 수정 모드: 문서 로드 useEffect(() => { diff --git a/src/components/dev/generators/accountingData.ts b/src/components/dev/generators/accountingData.ts index 94d4ee51..80a085a6 100644 --- a/src/components/dev/generators/accountingData.ts +++ b/src/components/dev/generators/accountingData.ts @@ -1,5 +1,5 @@ /** - * 회계 샘플 데이터 생성기 (입금, 출금, 매입) + * 회계 샘플 데이터 생성기 (입금, 출금, 매입, 카드) */ import { @@ -187,6 +187,24 @@ const PROPOSAL_REASONS = [ '시설 유지보수', ]; +// 결재자/참조자 타입 +export interface ApprovalPerson { + id: string; + department: string; + position: string; + name: string; +} + +// 경리/회계/재무 부서 샘플 직원 (참조용) +const ACCOUNTING_STAFF: ApprovalPerson[] = [ + { id: 'acc-1', department: '경리팀', position: '대리', name: '김경리' }, + { id: 'acc-2', department: '경리팀', position: '사원', name: '박경리' }, + { id: 'acc-3', department: '회계팀', position: '과장', name: '이회계' }, + { id: 'acc-4', department: '회계팀', position: '대리', name: '최회계' }, + { id: 'acc-5', department: '재무팀', position: '차장', name: '정재무' }, + { id: 'acc-6', department: '재무팀', position: '과장', name: '강재무' }, +]; + export interface PurchaseApprovalFormData { basicInfo: { drafter: string; @@ -194,6 +212,8 @@ export interface PurchaseApprovalFormData { documentNo: string; documentType: string; }; + approvalLine: ApprovalPerson[]; + references: ApprovalPerson[]; proposalData: { vendor: string; vendorPaymentDate: string; @@ -207,19 +227,42 @@ export interface PurchaseApprovalFormData { export interface GeneratePurchaseApprovalDataOptions { vendors?: Array<{ id: string; name: string }>; documentType?: string; + currentUser?: ApprovalPerson; +} + +// 문서번호 생성 (YYYYMMDD-HHMMSS-XXX 형식) +function generateDocumentNo(): string { + const now = new Date(); + const date = now.toISOString().slice(0, 10).replace(/-/g, ''); + const time = now.toTimeString().slice(0, 8).replace(/:/g, ''); + const random = String(randomInt(100, 999)); + return `DEV-${date}-${time.slice(0, 4)}-${random}`; } export function generatePurchaseApprovalData(options: GeneratePurchaseApprovalDataOptions = {}): PurchaseApprovalFormData { const { vendors = SAMPLE_VENDORS, documentType = 'proposal' } = options; const vendor = randomPick(vendors); + // 현재 사용자를 결재선에 추가 (기본값: 홍길동) + const currentUser: ApprovalPerson = options.currentUser || { + id: 'user-1', + department: '개발팀', + position: '사원', + name: '홍길동', + }; + + // 경리/회계/재무 직원 중 랜덤으로 1명 참조 추가 + const randomReference = randomPick(ACCOUNTING_STAFF); + return { basicInfo: { - drafter: '홍길동', + drafter: currentUser.name, draftDate: new Date().toISOString().slice(0, 16).replace('T', ' '), - documentNo: '', + documentNo: generateDocumentNo(), documentType, }, + approvalLine: [currentUser], + references: [randomReference], proposalData: { vendor: vendor.name, vendorPaymentDate: today(), @@ -229,4 +272,71 @@ export function generatePurchaseApprovalData(options: GeneratePurchaseApprovalDa estimatedCost: randomInt(100000, 5000000), }, }; +} + +// ===== 카드 거래 관련 ===== + +// 가맹점명 목록 +const MERCHANT_NAMES = [ + '스타벅스 강남점', + 'GS25 역삼점', + '이마트 성수점', + '쿠팡 결제', + '네이버페이', + '배달의민족', + '카카오택시', + '주유소(SK에너지)', + '올리브영 신촌점', + '다이소 홍대점', + '맥도날드 종로점', + '교보문고 광화문점', +]; + +// 카드 적요 목록 +const CARD_MEMOS = [ + '업무용 점심식대', + '사무용품 구매', + '출장 교통비', + '고객 접대비', + '회의 다과비', + '업무 차량 주유', + '직원 간식 구매', + '택배비', + '', +]; + +// 사용유형 (usageType) - 카드 결제 분류 +const CARD_USAGE_TYPES = ['unset', 'meal', 'transport', 'supplies', 'entertainment', 'other']; + +export interface CardTransactionFormData { + cardId: string; + usedAt: string; + merchantName: string; + amount: number; + memo: string; + usageType: string; +} + +export interface GenerateCardTransactionDataOptions { + cards?: Array<{ id: string | number; name: string; cardNumber?: string }>; +} + +// datetime-local 형식으로 현재 시간 반환 (YYYY-MM-DDTHH:mm) +function nowDateTimeLocal(): string { + const now = new Date(); + return now.toISOString().slice(0, 16); +} + +export function generateCardTransactionData(options: GenerateCardTransactionDataOptions = {}): CardTransactionFormData { + const { cards } = options; + const card = cards && cards.length > 0 ? randomPick(cards) : null; + + return { + cardId: card ? String(card.id) : '', + usedAt: nowDateTimeLocal(), + merchantName: randomPick(MERCHANT_NAMES), + amount: randomInt(5000, 500000), + memo: randomPick(CARD_MEMOS), + usageType: 'unset', // 미설정으로 고정 + }; } \ No newline at end of file