Files
sam-react-prod/src/lib/api/quote.ts
kent bf08447cd6 fix(WEB): 견적 타입 및 API 연동 개선 (#1, #2, #7)
이슈 #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 엔드포인트 호출 개선
2026-01-06 21:20:41 +09:00

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
};