Files
sam-react-prod/src/components/dev/generators/accountingData.ts
권혁성 e7fb3b1f96 fix(WEB): 기타 버그 수정 및 개선
- CardTransactionInquiry: account_code 'unset' 처리 개선
- WorkOrderCreate: DevFill 공정 옵션 로딩 타이밍 수정
- accountingData: 생성기 개선
- api/errors: 에러 처리 개선
2026-01-22 23:19:54 +09:00

347 lines
9.3 KiB
TypeScript

/**
* 회계 샘플 데이터 생성기 (입금, 출금, 매입, 카드)
*/
import {
randomPick,
randomInt,
today,
randomRemark,
} from './index';
// ===== 공통 상수 =====
// 거래처 목록 (실제로는 API에서 가져옴)
const SAMPLE_VENDORS = [
{ id: '1', name: '(주)삼성전자' },
{ id: '2', name: '(주)LG화학' },
{ id: '3', name: '현대중공업' },
{ id: '4', name: '(주)포스코' },
{ id: '5', name: '한화솔루션' },
];
// 계좌명 목록
const ACCOUNT_NAMES = [
'기업은행 1234-5678-9012',
'국민은행 111-22-33333',
'신한은행 110-123-456789',
'우리은행 1002-123-456789',
'하나은행 888-123456-78901',
];
// ===== 입금 관련 =====
// 입금 유형
const DEPOSIT_TYPES = ['revenue', 'deposit', 'sales', 'other', 'unset'];
// 입금자명
const DEPOSITOR_NAMES = [
'홍길동',
'김철수',
'이영희',
'박민수',
'최지영',
'(주)삼성전자',
'(주)LG화학',
'현대중공업',
];
// 입금 적요
const DEPOSIT_NOTES = [
'제품 판매대금',
'선수금 입금',
'용역비 입금',
'대금 회수',
'계약금 입금',
'잔금 입금',
'기타 입금',
'',
];
export interface DepositFormData {
depositDate: string;
bankAccountId: string;
depositorName: string;
depositAmount: number;
note: string;
vendorId: string;
depositType: string;
}
export interface GenerateDepositDataOptions {
vendors?: Array<{ id: string; name: string }>;
bankAccounts?: Array<{ id: string; name: string }>;
}
export function generateDepositData(options: GenerateDepositDataOptions = {}): DepositFormData {
const { vendors = SAMPLE_VENDORS, bankAccounts = [] } = options;
const vendor = randomPick(vendors);
const bankAccount = bankAccounts.length > 0 ? randomPick(bankAccounts) : null;
return {
depositDate: today(),
bankAccountId: bankAccount?.id || '',
depositorName: randomPick(DEPOSITOR_NAMES),
depositAmount: randomInt(100000, 10000000),
note: randomPick(DEPOSIT_NOTES),
vendorId: vendor.id,
depositType: 'unset', // 미설정으로 고정
};
}
// ===== 출금 관련 =====
// 출금 유형
const WITHDRAWAL_TYPES = ['expense', 'payment', 'purchase', 'salary', 'other', 'unset'];
// 수취인명
const RECIPIENT_NAMES = [
'(주)삼성전자',
'(주)LG화학',
'현대중공업',
'(주)포스코',
'한화솔루션',
'홍길동',
'김철수',
'국세청',
];
// 출금 적요
const WITHDRAWAL_NOTES = [
'자재 구매대금',
'외주 가공비',
'임대료 지급',
'전기요금',
'수도요금',
'통신비',
'급여 지급',
'세금 납부',
'',
];
export interface WithdrawalFormData {
withdrawalDate: string;
bankAccountId: string;
recipientName: string;
withdrawalAmount: number;
note: string;
vendorId: string;
withdrawalType: string;
}
export interface GenerateWithdrawalDataOptions {
vendors?: Array<{ id: string; name: string }>;
bankAccounts?: Array<{ id: string; name: string }>;
}
export function generateWithdrawalData(options: GenerateWithdrawalDataOptions = {}): WithdrawalFormData {
const { vendors = SAMPLE_VENDORS, bankAccounts = [] } = options;
const vendor = randomPick(vendors);
const bankAccount = bankAccounts.length > 0 ? randomPick(bankAccounts) : null;
return {
withdrawalDate: today(),
bankAccountId: bankAccount?.id || '',
recipientName: randomPick(RECIPIENT_NAMES),
withdrawalAmount: randomInt(50000, 5000000),
note: randomPick(WITHDRAWAL_NOTES),
vendorId: vendor.id,
withdrawalType: 'unset', // 미설정으로 고정
};
}
// ===== 매입(지출결의서) 관련 =====
// 문서 유형
const DOCUMENT_TYPES = ['proposal', 'expenseReport', 'expenseEstimate'];
// 지출결의서 제목
const PROPOSAL_TITLES = [
'사무용품 구매 요청',
'장비 수리비 지출 요청',
'출장비 정산 요청',
'회의비 지출 요청',
'교육비 지출 요청',
'소프트웨어 라이선스 구매',
'마케팅 비용 지출 요청',
'복리후생비 지출 요청',
];
// 지출결의서 내용
const PROPOSAL_DESCRIPTIONS = [
'업무 효율 향상을 위한 사무용품 구매가 필요합니다.',
'노후화된 장비의 수리가 필요하여 지출을 요청드립니다.',
'고객 미팅을 위한 출장 경비를 정산해주시기 바랍니다.',
'팀 회의 진행을 위한 다과 비용입니다.',
'직원 역량 강화를 위한 교육비 지출입니다.',
'업무용 소프트웨어 라이선스 갱신 비용입니다.',
'신규 고객 유치를 위한 마케팅 활동 비용입니다.',
'직원 복지 증진을 위한 지출입니다.',
];
// 지출 사유
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;
draftDate: string;
documentNo: string;
documentType: string;
};
approvalLine: ApprovalPerson[];
references: ApprovalPerson[];
proposalData: {
vendor: string;
vendorPaymentDate: string;
title: string;
description: string;
reason: string;
estimatedCost: number;
};
}
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: currentUser.name,
draftDate: new Date().toISOString().slice(0, 16).replace('T', ' '),
documentNo: generateDocumentNo(),
documentType,
},
approvalLine: [currentUser],
references: [randomReference],
proposalData: {
vendorId: vendor.id,
vendor: vendor.name,
vendorPaymentDate: today(),
title: randomPick(PROPOSAL_TITLES),
description: randomPick(PROPOSAL_DESCRIPTIONS),
reason: randomPick(PROPOSAL_REASONS),
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', // 미설정으로 고정
};
}