feat(WEB): 회계 DevFill 개선 - 유형 검증 제거, 매입 결재선/참조 자동설정

- 입금/출금: depositType/withdrawalType 'unset' 검증 제거 (미설정 상태로 등록 가능)
- 매입(지출결의서):
  - 문서번호 자동 생성 (DEV-YYYYMMDD-HHMM-XXX)
  - 결재선: localStorage에서 현재 사용자 이름 매칭, 없으면 랜덤 선택
  - 참조: 경리/회계/재무 부서 직원 중 랜덤 1명 선택
This commit is contained in:
2026-01-22 20:38:03 +09:00
parent 0a133f7890
commit 0dd00d17f3
4 changed files with 154 additions and 13 deletions

View File

@@ -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', // 미설정으로 고정
};
}