diff --git a/src/app/[locale]/(protected)/sales/order-management-sales/[id]/page.tsx b/src/app/[locale]/(protected)/sales/order-management-sales/[id]/page.tsx index c44c889b..7500cc2d 100644 --- a/src/app/[locale]/(protected)/sales/order-management-sales/[id]/page.tsx +++ b/src/app/[locale]/(protected)/sales/order-management-sales/[id]/page.tsx @@ -35,6 +35,7 @@ import { ChevronDown, ChevronRight, ChevronsUpDown, + Trash2, } from "lucide-react"; import { toast } from "sonner"; import { IntegratedDetailTemplate } from "@/components/templates/IntegratedDetailTemplate"; @@ -64,6 +65,7 @@ import { updateOrderStatus, revertProductionOrder, revertOrderConfirmation, + deleteOrder, type Order, type OrderStatus, } from "@/components/orders"; @@ -115,6 +117,8 @@ export default function OrderDetailPage() { const [isReverting, setIsReverting] = useState(false); const [isRevertConfirmDialogOpen, setIsRevertConfirmDialogOpen] = useState(false); const [isRevertingConfirm, setIsRevertingConfirm] = useState(false); + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); // 취소 폼 상태 const [cancelReason, setCancelReason] = useState(""); @@ -296,6 +300,31 @@ export default function OrderDetailPage() { } }; + // 수주 삭제 + const handleDelete = () => { + setIsDeleteDialogOpen(true); + }; + + const handleDeleteSubmit = async () => { + if (order) { + setIsDeleting(true); + try { + const result = await deleteOrder(order.id); + if (result.success) { + toast.success("수주가 삭제되었습니다."); + router.push("/sales/order-management-sales"); + } else { + toast.error(result.error || "수주 삭제에 실패했습니다."); + } + } catch (error) { + console.error("Error deleting order:", error); + toast.error("수주 삭제 중 오류가 발생했습니다."); + } finally { + setIsDeleting(false); + } + } + }; + // 문서 모달 열기 const openDocumentModal = (type: OrderDocumentType) => { setDocumentType(type); @@ -727,6 +756,8 @@ export default function OrderDetailPage() { order.status !== "shipped" && order.status !== "cancelled" && order.status !== "production_ordered"; + // 삭제 버튼은 수주등록 또는 취소 상태에서 표시 + const showDeleteButton = order.status === "order_registered" || order.status === "cancelled"; return (
@@ -772,9 +803,15 @@ export default function OrderDetailPage() { 취소 )} + {showDeleteButton && ( + + )}
); - }, [order, handleEdit, handleConfirmOrder, handleProductionOrder, handleViewProductionOrder, handleRevertProduction, handleRevertConfirmation, handleCancel]); + }, [order, handleEdit, handleConfirmOrder, handleProductionOrder, handleViewProductionOrder, handleRevertProduction, handleRevertConfirmation, handleCancel, handleDelete]); return ( <> @@ -1116,6 +1153,80 @@ export default function OrderDetailPage() { + + {/* 수주 삭제 다이얼로그 */} + + + + + + 수주 삭제 + + + +
+ {/* 수주 정보 박스 */} +
+
+ 수주번호 + {order.lotNumber} +
+
+ 발주처 + {order.client} +
+
+ 현장명 + {order.siteName} +
+
+ 총금액 + + {formatAmount(order.totalAmount || 0)}원 + +
+
+ 현재 상태 + {getOrderStatusBadge(order.status)} +
+
+ + {/* 경고 메시지 */} +
+

⚠️ 삭제 시 주의사항

+
    +
  • • 삭제된 수주는 복구할 수 없습니다
  • +
  • • 관련된 모든 품목 정보가 함께 삭제됩니다
  • +
  • • 이 작업은 되돌릴 수 없습니다
  • +
+
+ + {/* 확인 안내 */} +
+

+ 정말로 이 수주를 삭제하시겠습니까? +

+
+
+ + + + + +
+
)} diff --git a/src/components/outbound/ShipmentManagement/ShipmentList.tsx b/src/components/outbound/ShipmentManagement/ShipmentList.tsx index e8f58907..ae2b1b74 100644 --- a/src/components/outbound/ShipmentManagement/ShipmentList.tsx +++ b/src/components/outbound/ShipmentManagement/ShipmentList.tsx @@ -41,7 +41,6 @@ import { getShipments, getShipmentStats, getShipmentStatsByStatus } from './acti import { SHIPMENT_STATUS_LABELS, SHIPMENT_STATUS_STYLES, - DELIVERY_METHOD_LABELS, } from './types'; import type { ShipmentItem, ShipmentStatus, ShipmentStats, ShipmentStatusStats } from './types'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; @@ -287,7 +286,7 @@ export function ShipmentList() { )} - {DELIVERY_METHOD_LABELS[item.deliveryMethod]} + {item.deliveryMethodLabel} {item.customerName} {item.siteName} {item.manager || '-'} @@ -331,7 +330,7 @@ export function ShipmentList() { - + } diff --git a/src/components/outbound/ShipmentManagement/actions.ts b/src/components/outbound/ShipmentManagement/actions.ts index f9304d07..8e4a6ed1 100644 --- a/src/components/outbound/ShipmentManagement/actions.ts +++ b/src/components/outbound/ShipmentManagement/actions.ts @@ -144,8 +144,10 @@ function transformApiToListItem(data: ShipmentApiData): ShipmentItem { status: data.status, priority: data.priority, deliveryMethod: data.delivery_method, - customerName: data.customer_name || '', - siteName: data.site_name || '', + deliveryMethodLabel: data.delivery_method_label || data.delivery_method, + // 발주처/배송 정보: order_info 우선 참조 (Order가 Single Source of Truth) + customerName: data.order_info?.customer_name || data.customer_name || '', + siteName: data.order_info?.site_name || data.site_name || '', manager: data.loading_manager, canShip: data.can_ship, depositConfirmed: data.deposit_confirmed, diff --git a/src/components/outbound/ShipmentManagement/types.ts b/src/components/outbound/ShipmentManagement/types.ts index 7bb54935..bbe7d1a7 100644 --- a/src/components/outbound/ShipmentManagement/types.ts +++ b/src/components/outbound/ShipmentManagement/types.ts @@ -58,6 +58,7 @@ export interface ShipmentItem { status: ShipmentStatus; // 상태 priority: ShipmentPriority; // 우선순위 deliveryMethod: DeliveryMethod; // 배송방식 + deliveryMethodLabel: string; // 배송방식 라벨 (API에서 조회) customerName: string; // 발주처 siteName: string; // 현장명 manager?: string; // 담당 diff --git a/src/lib/api/common-codes.ts b/src/lib/api/common-codes.ts index bc3ab97e..7fc60810 100644 --- a/src/lib/api/common-codes.ts +++ b/src/lib/api/common-codes.ts @@ -118,4 +118,30 @@ export async function getItemTypeCodes() { */ export async function getItemTypeOptions() { return getCommonCodeOptions('item_type'); +} + +/** + * 배송방식 코드 조회 + */ +export async function getDeliveryMethodCodes() { + return getCommonCodes('delivery_method'); +} + +/** + * 배송방식 옵션 조회 + */ +export async function getDeliveryMethodOptions() { + return getCommonCodeOptions('delivery_method'); +} + +/** + * 코드값으로 라벨 조회 (code → name 매핑) + */ +export async function getCodeLabel(group: string, code: string): Promise { + const result = await getCommonCodes(group); + if (result.success && result.data) { + const found = result.data.find((item) => item.code === code); + return found?.name || code; + } + return code; } \ No newline at end of file