179 lines
5.0 KiB
TypeScript
179 lines
5.0 KiB
TypeScript
/**
|
|
* 생산계획 API 클라이언트
|
|
*/
|
|
|
|
import apiClient from "./client";
|
|
|
|
// ─── 타입 정의 ───
|
|
|
|
export interface OrderSummaryItem {
|
|
item_code: string;
|
|
item_name: string;
|
|
total_order_qty: number;
|
|
total_ship_qty: number;
|
|
total_balance_qty: number;
|
|
order_count: number;
|
|
earliest_due_date: string | null;
|
|
current_stock: number;
|
|
safety_stock: number;
|
|
existing_plan_qty: number;
|
|
in_progress_qty: number;
|
|
required_plan_qty: number;
|
|
orders: OrderDetail[];
|
|
}
|
|
|
|
export interface OrderDetail {
|
|
id: string;
|
|
order_no: string;
|
|
part_code: string;
|
|
part_name: string;
|
|
order_qty: number;
|
|
ship_qty: number;
|
|
balance_qty: number;
|
|
due_date: string | null;
|
|
status: string;
|
|
customer_name: string | null;
|
|
}
|
|
|
|
export interface StockShortageItem {
|
|
item_code: string;
|
|
item_name: string;
|
|
current_qty: number;
|
|
safety_qty: number;
|
|
shortage_qty: number;
|
|
recommended_qty: number;
|
|
last_in_date: string | null;
|
|
}
|
|
|
|
export interface ProductionPlan {
|
|
id: number;
|
|
company_code: string;
|
|
plan_no: string;
|
|
plan_date: string;
|
|
item_code: string;
|
|
item_name: string;
|
|
product_type: string;
|
|
plan_qty: number;
|
|
completed_qty: number;
|
|
progress_rate: number;
|
|
start_date: string;
|
|
end_date: string;
|
|
due_date: string | null;
|
|
equipment_id: number | null;
|
|
equipment_code: string | null;
|
|
equipment_name: string | null;
|
|
status: string;
|
|
priority: string | null;
|
|
order_no: string | null;
|
|
parent_plan_id: number | null;
|
|
remarks: string | null;
|
|
}
|
|
|
|
export interface GenerateScheduleRequest {
|
|
items: {
|
|
item_code: string;
|
|
item_name: string;
|
|
required_qty: number;
|
|
earliest_due_date: string;
|
|
hourly_capacity?: number;
|
|
daily_capacity?: number;
|
|
lead_time?: number;
|
|
}[];
|
|
options?: {
|
|
safety_lead_time?: number;
|
|
recalculate_unstarted?: boolean;
|
|
product_type?: string;
|
|
};
|
|
}
|
|
|
|
export interface GenerateScheduleResponse {
|
|
summary: {
|
|
total: number;
|
|
new_count: number;
|
|
kept_count: number;
|
|
deleted_count: number;
|
|
};
|
|
schedules: ProductionPlan[];
|
|
}
|
|
|
|
// ─── API 함수 ───
|
|
|
|
/** 수주 데이터 조회 (품목별 그룹핑) */
|
|
export async function getOrderSummary(params?: {
|
|
excludePlanned?: boolean;
|
|
itemCode?: string;
|
|
itemName?: string;
|
|
}) {
|
|
const queryParams = new URLSearchParams();
|
|
if (params?.excludePlanned) queryParams.set("excludePlanned", "true");
|
|
if (params?.itemCode) queryParams.set("itemCode", params.itemCode);
|
|
if (params?.itemName) queryParams.set("itemName", params.itemName);
|
|
|
|
const qs = queryParams.toString();
|
|
const url = `/api/production/order-summary${qs ? `?${qs}` : ""}`;
|
|
const response = await apiClient.get(url);
|
|
return response.data as { success: boolean; data: OrderSummaryItem[] };
|
|
}
|
|
|
|
/** 안전재고 부족분 조회 */
|
|
export async function getStockShortage() {
|
|
const response = await apiClient.get("/api/production/stock-shortage");
|
|
return response.data as { success: boolean; data: StockShortageItem[] };
|
|
}
|
|
|
|
/** 생산계획 상세 조회 */
|
|
export async function getPlanById(planId: number) {
|
|
const response = await apiClient.get(`/api/production/plan/${planId}`);
|
|
return response.data as { success: boolean; data: ProductionPlan };
|
|
}
|
|
|
|
/** 생산계획 수정 */
|
|
export async function updatePlan(planId: number, data: Partial<ProductionPlan>) {
|
|
const response = await apiClient.put(`/api/production/plan/${planId}`, data);
|
|
return response.data as { success: boolean; data: ProductionPlan };
|
|
}
|
|
|
|
/** 생산계획 삭제 */
|
|
export async function deletePlan(planId: number) {
|
|
const response = await apiClient.delete(`/api/production/plan/${planId}`);
|
|
return response.data as { success: boolean; message: string };
|
|
}
|
|
|
|
/** 자동 스케줄 생성 */
|
|
export async function generateSchedule(request: GenerateScheduleRequest) {
|
|
const response = await apiClient.post("/api/production/generate-schedule", request);
|
|
return response.data as { success: boolean; data: GenerateScheduleResponse };
|
|
}
|
|
|
|
/** 스케줄 병합 */
|
|
export async function mergeSchedules(scheduleIds: number[], productType?: string) {
|
|
const response = await apiClient.post("/api/production/merge-schedules", {
|
|
schedule_ids: scheduleIds,
|
|
product_type: productType || "완제품",
|
|
});
|
|
return response.data as { success: boolean; data: ProductionPlan };
|
|
}
|
|
|
|
/** 반제품 계획 자동 생성 */
|
|
export async function generateSemiSchedule(
|
|
planIds: number[],
|
|
options?: { considerStock?: boolean; excludeUsed?: boolean }
|
|
) {
|
|
const response = await apiClient.post("/api/production/generate-semi-schedule", {
|
|
plan_ids: planIds,
|
|
options: options || {},
|
|
});
|
|
return response.data as { success: boolean; data: { count: number; schedules: ProductionPlan[] } };
|
|
}
|
|
|
|
/** 스케줄 분할 */
|
|
export async function splitSchedule(planId: number, splitQty: number) {
|
|
const response = await apiClient.post(`/api/production/plan/${planId}/split`, {
|
|
split_qty: splitQty,
|
|
});
|
|
return response.data as {
|
|
success: boolean;
|
|
data: { original: { id: number; plan_qty: number }; split: ProductionPlan };
|
|
};
|
|
}
|