이슈 #1: 리스트 화면 담당자/비고 컬럼 표시 이슈 #2: 상세 화면 담당자/연락처 표시 이슈 #7: 수정 화면 기본정보 필드 표시 주요 변경: - types.ts: Quote/QuoteApiData에 manager, contact, remarks 필드 추가 - types.ts: CalculationInputs, BomMaterial 타입 추가 - types.ts: transformApiToFrontend에서 새 필드 변환 로직 추가 - types.ts: transformQuoteToFormData에서 calculation_inputs 기반 폼 복원 - actions.ts: API 요청/응답 필드 매핑 개선 - api/quote.ts: API 엔드포인트 호출 개선
252 lines
6.1 KiB
TypeScript
252 lines
6.1 KiB
TypeScript
// lib/api/quote.ts
|
|
// 견적 자동산출 API 클라이언트
|
|
import { ApiClient } from './client';
|
|
import { AUTH_CONFIG } from './auth/auth-config';
|
|
|
|
// ===================================
|
|
// 타입 정의
|
|
// ===================================
|
|
|
|
/**
|
|
* 산출된 변수 정보
|
|
*/
|
|
interface CalculationVariable {
|
|
name: string;
|
|
value: number;
|
|
category: string;
|
|
type: string;
|
|
}
|
|
|
|
/**
|
|
* 산출된 품목 정보
|
|
*/
|
|
interface CalculationItem {
|
|
item_code: string;
|
|
item_name: string;
|
|
specification?: string;
|
|
unit?: string;
|
|
quantity: number;
|
|
unit_price: number;
|
|
total_price: number;
|
|
formula_variable: string;
|
|
formula_category?: string;
|
|
}
|
|
|
|
/**
|
|
* 비용 합계
|
|
*/
|
|
interface CalculationCosts {
|
|
material_cost: number;
|
|
labor_cost: number;
|
|
install_cost: number;
|
|
subtotal: number;
|
|
}
|
|
|
|
/**
|
|
* 견적 산출 결과
|
|
*/
|
|
export interface CalculationResult {
|
|
inputs: Record<string, number | string>;
|
|
outputs: Record<string, CalculationVariable>;
|
|
items: CalculationItem[];
|
|
costs: CalculationCosts;
|
|
errors: string[];
|
|
}
|
|
|
|
/**
|
|
* 견적 산출 요청
|
|
*/
|
|
export interface CalculateRequest {
|
|
inputs: {
|
|
W0: number; // 오픈사이즈 가로 (mm)
|
|
H0: number; // 오픈사이즈 세로 (mm)
|
|
QTY?: number; // 수량
|
|
INSTALL_TYPE?: string; // 설치 유형 (wall, ceiling, floor)
|
|
CONTROL_TYPE?: string; // 제어 방식 (switch, remote, smart)
|
|
MOTOR_TYPE?: string; // 모터 유형 (standard, heavy)
|
|
CHAIN_SIDE?: string; // 체인 위치 (left, right)
|
|
};
|
|
product_category: 'screen' | 'steel';
|
|
}
|
|
|
|
/**
|
|
* 입력 스키마 필드 정의
|
|
*/
|
|
interface InputSchemaField {
|
|
label: string;
|
|
type: 'number' | 'integer' | 'select' | 'text';
|
|
unit?: string;
|
|
required?: boolean;
|
|
min?: number;
|
|
max?: number;
|
|
step?: number;
|
|
default?: string | number;
|
|
options?: Array<{ value: string; label: string }>;
|
|
description?: string;
|
|
}
|
|
|
|
/**
|
|
* BOM 계산 요청 (단건)
|
|
*/
|
|
export interface CalculateBomRequest {
|
|
finished_goods_code: string; // 완제품 코드
|
|
input_variables: {
|
|
W0: number; // 오픈사이즈 가로 (mm)
|
|
H0: number; // 오픈사이즈 세로 (mm)
|
|
QTY?: number; // 수량
|
|
GT?: string; // 가이드레일 설치 유형 (벽면형/측면형)
|
|
MP?: string; // 모터 전원 (220V/380V)
|
|
CT?: string; // 연동제어기 (단독/연동)
|
|
WS?: number; // 마구리 날개치수
|
|
INSP?: number; // 검사비
|
|
};
|
|
}
|
|
|
|
/**
|
|
* BOM 계산 요청 (다건)
|
|
*/
|
|
export interface CalculateBomBulkRequest {
|
|
items: CalculateBomRequest[];
|
|
}
|
|
|
|
/**
|
|
* BOM 계산 결과 항목
|
|
*/
|
|
interface BomResultItem {
|
|
item_code: string;
|
|
item_name: string;
|
|
specification?: string;
|
|
unit?: string;
|
|
quantity: number;
|
|
unit_price: number;
|
|
total_price: number;
|
|
process_group?: string;
|
|
}
|
|
|
|
/**
|
|
* BOM 계산 결과
|
|
*/
|
|
export interface BomCalculationResult {
|
|
finished_goods: {
|
|
code: string;
|
|
name: string;
|
|
item_category?: string;
|
|
};
|
|
items: BomResultItem[];
|
|
subtotals: Record<string, number>;
|
|
grand_total: number;
|
|
debug_steps?: Array<{
|
|
step: number;
|
|
label: string;
|
|
description: string;
|
|
data?: Record<string, unknown>;
|
|
}>;
|
|
}
|
|
|
|
/**
|
|
* API 응답 공통 형식
|
|
*/
|
|
interface ApiResponse<T> {
|
|
success: boolean;
|
|
message: string;
|
|
data: T;
|
|
}
|
|
|
|
// ===================================
|
|
// Quote API 클라이언트
|
|
// ===================================
|
|
|
|
/**
|
|
* 견적 자동산출 API 클라이언트
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // 자동 산출
|
|
* const result = await quoteApi.calculate({
|
|
* inputs: { W0: 3000, H0: 2500 },
|
|
* product_category: 'screen'
|
|
* });
|
|
*
|
|
* // 입력 스키마 조회
|
|
* const schema = await quoteApi.getCalculationSchema('screen');
|
|
* ```
|
|
*/
|
|
class QuoteApiClient extends ApiClient {
|
|
constructor() {
|
|
super({
|
|
mode: 'bearer',
|
|
apiKey: process.env.NEXT_PUBLIC_API_KEY,
|
|
getToken: () => {
|
|
if (typeof window !== 'undefined') {
|
|
return localStorage.getItem('auth_token');
|
|
}
|
|
return null;
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 자동 견적 산출
|
|
*
|
|
* @param request - 산출 요청 파라미터
|
|
* @returns 산출 결과 (변수, 품목, 비용)
|
|
*/
|
|
async calculate(request: CalculateRequest): Promise<ApiResponse<CalculationResult>> {
|
|
return this.post<ApiResponse<CalculationResult>>('/api/v1/quotes/calculate', request);
|
|
}
|
|
|
|
/**
|
|
* 견적 미리보기 (저장 없이 계산만)
|
|
*
|
|
* @param request - 산출 요청 파라미터
|
|
* @returns 미리보기 결과
|
|
*/
|
|
async preview(request: CalculateRequest): Promise<ApiResponse<CalculationResult>> {
|
|
return this.post<ApiResponse<CalculationResult>>('/api/v1/quotes/preview', request);
|
|
}
|
|
|
|
/**
|
|
* 입력 스키마 조회 (동적 폼 생성용)
|
|
*
|
|
* @param productCategory - 제품 카테고리 (screen, steel)
|
|
* @returns 입력 필드 스키마
|
|
*/
|
|
async getCalculationSchema(
|
|
productCategory?: 'screen' | 'steel'
|
|
): Promise<ApiResponse<Record<string, InputSchemaField>>> {
|
|
const query = productCategory ? `?product_category=${productCategory}` : '';
|
|
return this.get<ApiResponse<Record<string, InputSchemaField>>>(`/api/v1/quotes/calculation-schema${query}`);
|
|
}
|
|
|
|
/**
|
|
* BOM 기반 자동 견적 산출 (단건)
|
|
*
|
|
* @param request - BOM 산출 요청 파라미터
|
|
* @returns BOM 계산 결과
|
|
*/
|
|
async calculateBom(request: CalculateBomRequest): Promise<ApiResponse<BomCalculationResult>> {
|
|
return this.post<ApiResponse<BomCalculationResult>>('/api/v1/quotes/calculate/bom', request);
|
|
}
|
|
|
|
/**
|
|
* BOM 기반 자동 견적 산출 (다건)
|
|
*
|
|
* @param request - 다건 BOM 산출 요청 파라미터
|
|
* @returns BOM 계산 결과 배열
|
|
*/
|
|
async calculateBomBulk(request: CalculateBomBulkRequest): Promise<ApiResponse<BomCalculationResult[]>> {
|
|
return this.post<ApiResponse<BomCalculationResult[]>>('/api/v1/quotes/calculate/bom/bulk', request);
|
|
}
|
|
}
|
|
|
|
// 싱글톤 인스턴스 내보내기
|
|
export const quoteApi = new QuoteApiClient();
|
|
|
|
// 타입 내보내기
|
|
export type {
|
|
CalculationVariable,
|
|
CalculationItem,
|
|
CalculationCosts,
|
|
InputSchemaField,
|
|
ApiResponse
|
|
}; |