feat: 수주 관리 Phase 3 - Frontend API 연동
- createOrderFromQuote(): 견적→수주 변환 API 호출 - createProductionOrder(): 생산지시 생성 API 호출 - WorkOrder 타입 및 변환 함수 추가 - 변경 내역 문서 작성
This commit is contained in:
@@ -66,9 +66,34 @@ interface ApiClient {
|
||||
interface ApiQuote {
|
||||
id: number;
|
||||
quote_no: string;
|
||||
quote_number?: string;
|
||||
site_name: string | null;
|
||||
}
|
||||
|
||||
interface ApiWorkOrder {
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
work_order_no: string;
|
||||
sales_order_id: number;
|
||||
project_name: string | null;
|
||||
process_type: string;
|
||||
status: string;
|
||||
assignee_id: number | null;
|
||||
team_id: number | null;
|
||||
scheduled_date: string | null;
|
||||
memo: string | null;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
assignee?: { id: number; name: string } | null;
|
||||
team?: { id: number; name: string } | null;
|
||||
}
|
||||
|
||||
interface ApiProductionOrderResponse {
|
||||
work_order: ApiWorkOrder;
|
||||
order: ApiOrder;
|
||||
}
|
||||
|
||||
interface ApiOrderStats {
|
||||
total: number;
|
||||
draft: number;
|
||||
@@ -188,6 +213,46 @@ export interface OrderStats {
|
||||
confirmedAmount: number;
|
||||
}
|
||||
|
||||
// 견적→수주 변환용
|
||||
export interface CreateFromQuoteData {
|
||||
deliveryDate?: string;
|
||||
memo?: string;
|
||||
}
|
||||
|
||||
// 생산지시 생성용
|
||||
export interface CreateProductionOrderData {
|
||||
processType?: 'screen' | 'slat' | 'bending';
|
||||
assigneeId?: number;
|
||||
teamId?: number;
|
||||
scheduledDate?: string;
|
||||
memo?: string;
|
||||
}
|
||||
|
||||
// 생산지시(작업지시) 타입
|
||||
export interface WorkOrder {
|
||||
id: string;
|
||||
workOrderNo: string;
|
||||
salesOrderId: number;
|
||||
projectName: string | null;
|
||||
processType: string;
|
||||
status: string;
|
||||
assigneeId?: number;
|
||||
assigneeName?: string;
|
||||
teamId?: number;
|
||||
teamName?: string;
|
||||
scheduledDate?: string;
|
||||
memo?: string;
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// 생산지시 생성 결과
|
||||
export interface ProductionOrderResult {
|
||||
workOrder: WorkOrder;
|
||||
order: Order;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 상태 매핑
|
||||
// ============================================================================
|
||||
@@ -287,6 +352,26 @@ function transformFrontendToApi(data: OrderFormData): Record<string, unknown> {
|
||||
};
|
||||
}
|
||||
|
||||
function transformWorkOrderApiToFrontend(apiData: ApiWorkOrder): WorkOrder {
|
||||
return {
|
||||
id: String(apiData.id),
|
||||
workOrderNo: apiData.work_order_no,
|
||||
salesOrderId: apiData.sales_order_id,
|
||||
projectName: apiData.project_name,
|
||||
processType: apiData.process_type,
|
||||
status: apiData.status,
|
||||
assigneeId: apiData.assignee_id ?? undefined,
|
||||
assigneeName: apiData.assignee?.name ?? undefined,
|
||||
teamId: apiData.team_id ?? undefined,
|
||||
teamName: apiData.team?.name ?? undefined,
|
||||
scheduledDate: apiData.scheduled_date ?? undefined,
|
||||
memo: apiData.memo ?? undefined,
|
||||
isActive: apiData.is_active,
|
||||
createdAt: apiData.created_at,
|
||||
updatedAt: apiData.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API 함수
|
||||
// ============================================================================
|
||||
@@ -628,3 +713,98 @@ export async function deleteOrders(ids: string[]): Promise<{
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적에서 수주 생성
|
||||
*/
|
||||
export async function createOrderFromQuote(
|
||||
quoteId: number,
|
||||
data?: CreateFromQuoteData
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: Order;
|
||||
error?: string;
|
||||
__authError?: boolean;
|
||||
}> {
|
||||
try {
|
||||
const apiData: Record<string, unknown> = {};
|
||||
if (data?.deliveryDate) apiData.delivery_date = data.deliveryDate;
|
||||
if (data?.memo) apiData.memo = data.memo;
|
||||
|
||||
const { response, error } = await serverFetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/orders/from-quote/${quoteId}`,
|
||||
{ method: 'POST', body: JSON.stringify(apiData) }
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' };
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
return { success: false, error: '수주 생성에 실패했습니다.' };
|
||||
}
|
||||
|
||||
const result: ApiResponse<ApiOrder> = await response.json();
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
return { success: false, error: result.message || '수주 생성에 실패했습니다.' };
|
||||
}
|
||||
|
||||
return { success: true, data: transformApiToFrontend(result.data) };
|
||||
} catch (error) {
|
||||
console.error('[createOrderFromQuote] Error:', error);
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 생산지시 생성
|
||||
*/
|
||||
export async function createProductionOrder(
|
||||
orderId: string,
|
||||
data?: CreateProductionOrderData
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data?: ProductionOrderResult;
|
||||
error?: string;
|
||||
__authError?: boolean;
|
||||
}> {
|
||||
try {
|
||||
const apiData: Record<string, unknown> = {};
|
||||
if (data?.processType) apiData.process_type = data.processType;
|
||||
if (data?.assigneeId) apiData.assignee_id = data.assigneeId;
|
||||
if (data?.teamId) apiData.team_id = data.teamId;
|
||||
if (data?.scheduledDate) apiData.scheduled_date = data.scheduledDate;
|
||||
if (data?.memo) apiData.memo = data.memo;
|
||||
|
||||
const { response, error } = await serverFetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/orders/${orderId}/production-order`,
|
||||
{ method: 'POST', body: JSON.stringify(apiData) }
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' };
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
return { success: false, error: '생산지시 생성에 실패했습니다.' };
|
||||
}
|
||||
|
||||
const result: ApiResponse<ApiProductionOrderResponse> = await response.json();
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
return { success: false, error: result.message || '생산지시 생성에 실패했습니다.' };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
workOrder: transformWorkOrderApiToFrontend(result.data.work_order),
|
||||
order: transformApiToFrontend(result.data.order),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[createProductionOrder] Error:', error);
|
||||
return { success: false, error: '서버 오류가 발생했습니다.' };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user