From 77b7a0cdbbf5f187d0fd752871f3bdf673d531e6 Mon Sep 17 00:00:00 2001 From: kjs Date: Mon, 13 Apr 2026 13:38:44 +0900 Subject: [PATCH] refactor: Update table column widths and improve pagination settings in purchase and sales order pages - Changed column width definitions from fixed to minimum widths for better responsiveness in the purchase order and sales order pages. - Increased the pagination size from 500 to 5000 for supplier and user data fetching to accommodate larger datasets. - Enhanced item search functionality by including management item filters in server queries, improving data handling and user experience. - These changes aim to provide a more flexible and user-friendly interface across multiple company implementations. --- .../(main)/COMPANY_10/purchase/order/page.tsx | 91 ++++++----- .../(main)/COMPANY_10/sales/order/page.tsx | 52 +++---- .../(main)/COMPANY_16/purchase/order/page.tsx | 91 ++++++----- .../(main)/COMPANY_16/sales/order/page.tsx | 52 +++---- .../(main)/COMPANY_29/purchase/order/page.tsx | 91 ++++++----- .../(main)/COMPANY_29/sales/order/page.tsx | 52 +++---- .../(main)/COMPANY_30/purchase/order/page.tsx | 91 ++++++----- .../(main)/COMPANY_30/sales/order/page.tsx | 146 +++++++++--------- .../(main)/COMPANY_7/purchase/order/page.tsx | 91 ++++++----- .../app/(main)/COMPANY_7/sales/order/page.tsx | 52 +++---- .../(main)/COMPANY_8/purchase/order/page.tsx | 91 ++++++----- .../app/(main)/COMPANY_8/sales/order/page.tsx | 52 +++---- .../(main)/COMPANY_9/purchase/order/page.tsx | 91 ++++++----- .../app/(main)/COMPANY_9/sales/order/page.tsx | 146 +++++++++--------- 14 files changed, 585 insertions(+), 604 deletions(-) diff --git a/frontend/app/(main)/COMPANY_10/purchase/order/page.tsx b/frontend/app/(main)/COMPANY_10/purchase/order/page.tsx index 90116bac..f712d3ed 100644 --- a/frontend/app/(main)/COMPANY_10/purchase/order/page.tsx +++ b/frontend/app/(main)/COMPANY_10/purchase/order/page.tsx @@ -88,18 +88,18 @@ const GRID_COLUMNS_CONFIG = [ ]; const MODAL_DETAIL_COLUMNS = [ - { key: "item_code", label: "품번", width: "w-[120px]" }, - { key: "item_name", label: "품명", width: "w-[120px]" }, - { key: "supplier", label: "공급업체", width: "w-[150px]" }, - { key: "spec", label: "규격", width: "w-[80px]" }, - { key: "unit", label: "단위", width: "w-[60px]" }, - { key: "order_qty", label: "발주수량", width: "w-[90px]" }, - { key: "received_qty", label: "입고수량", width: "w-[90px]" }, - { key: "remain_qty", label: "잔량", width: "w-[80px]" }, - { key: "unit_price", label: "단가", width: "w-[100px]" }, - { key: "amount", label: "금액", width: "w-[100px]" }, - { key: "due_date", label: "납기일", width: "w-[160px]" }, - { key: "memo", label: "메모", width: "w-[120px]" }, + { key: "item_code", label: "품번", width: "min-w-[120px]" }, + { key: "item_name", label: "품명", width: "min-w-[150px]" }, + { key: "supplier", label: "공급업체", width: "min-w-[150px]" }, + { key: "spec", label: "규격", width: "min-w-[80px]" }, + { key: "unit", label: "단위", width: "min-w-[90px]" }, + { key: "order_qty", label: "발주수량", width: "min-w-[90px]" }, + { key: "received_qty", label: "입고수량", width: "min-w-[90px]" }, + { key: "remain_qty", label: "잔량", width: "min-w-[80px]" }, + { key: "unit_price", label: "단가", width: "min-w-[100px]" }, + { key: "amount", label: "금액", width: "min-w-[100px]" }, + { key: "due_date", label: "납기일", width: "min-w-[160px]" }, + { key: "memo", label: "메모", width: "min-w-[120px]" }, ]; const MODAL_COL_ORDER_KEY = "purchase_order_modal_col_order_c16"; @@ -237,7 +237,7 @@ export default function PurchaseOrderPage() { ); try { const suppRes = await apiClient.post(`/table-management/tables/supplier_mng/data`, { - page: 1, size: 500, autoFilter: true, + page: 1, size: 5000, autoFilter: true, }); const supps = suppRes.data?.data?.data || suppRes.data?.data?.rows || []; optMap["supplier_code"] = supps.map((s: any) => ({ @@ -247,7 +247,7 @@ export default function PurchaseOrderPage() { } catch { /* skip */ } try { const userRes = await apiClient.post(`/table-management/tables/user_info/data`, { - page: 1, size: 500, autoFilter: true, + page: 1, size: 5000, autoFilter: true, }); const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; optMap["manager"] = users.map((u: any) => ({ @@ -293,7 +293,7 @@ export default function PurchaseOrderPage() { } const res = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: detailFilters.length > 0 ? { enabled: true, filters: detailFilters } : undefined, autoFilter: true, sort: { columnName: "purchase_no", order: "desc" }, @@ -538,7 +538,7 @@ export default function PurchaseOrderPage() { } }; - // 품목 검색 + // 품목 검색 (수주관리와 동일한 서버 페이징 방식) const searchItems = async (page?: number, size?: number) => { const p = page ?? itemPage; const s = size ?? itemPageSize; @@ -548,25 +548,24 @@ export default function PurchaseOrderPage() { if (itemSearchKeyword) { filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword }); } + // 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응) + if (itemSearchDivision !== "all") { + const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; + const divValues = [itemSearchDivision]; + if (divLabel) divValues.push(divLabel); + filters.push({ columnName: "division", operator: "in", value: divValues }); + } const res = await apiClient.post(`/table-management/tables/item_info/data`, { - page: 1, size: 500, + page: p, size: s, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, }); const resData = res.data?.data; - let allRows = resData?.data || resData?.rows || []; - if (itemSearchDivision !== "all") { - const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; - allRows = allRows.filter((item: any) => { - const div = item.division || ""; - return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel)); - }); - } - const total = allRows.length; - const start = (p - 1) * s; - setItemSearchResults(allRows.slice(start, start + s)); - setItemTotal(total); - setItemTotalPages(Math.max(1, Math.ceil(total / s))); + const rows = resData?.data || resData?.rows || []; + const serverTotal = resData?.total || resData?.totalCount || rows.length; + setItemSearchResults(rows); + setItemTotal(serverTotal); + setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s))); } catch { /* skip */ } finally { setItemSearchLoading(false); } @@ -608,7 +607,7 @@ export default function PurchaseOrderPage() { try { const itemIds = selected.map((item) => item.item_number || item.id); const res = await apiClient.post(`/table-management/tables/supplier_item_prices/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [ @@ -671,7 +670,7 @@ export default function PurchaseOrderPage() { if (itemCodes.length === 0) return; try { const res = await apiClient.post(`/table-management/tables/item_info/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [{ columnName: "item_number", operator: "in", value: itemCodes }] }, autoFilter: true, }); @@ -693,7 +692,7 @@ export default function PurchaseOrderPage() { if (itemCodes.length === 0) return; try { const res = await apiClient.post(`/table-management/tables/supplier_item_prices/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [ { columnName: "supplier_id", operator: "equals", value: supplierCode }, { columnName: "item_id", operator: "in", value: itemCodes }, @@ -1037,7 +1036,7 @@ export default function PurchaseOrderPage() { ) : (
- +
c.key)} strategy={horizontalListSortingStrategy}> @@ -1061,12 +1060,12 @@ export default function PurchaseOrderPage() { {visibleModalColumns.map((col) => { switch (col.key) { case "item_code": - return {row.item_code}; + return {row.item_code}; case "item_name": - return {row.item_name}; + return {row.item_name}; case "supplier": return ( - + {isReadOnly ? ( {(categoryOptions["supplier_code"] || []).find(o => o.code === row.supplier_code)?.label || row.supplier_code || "-"} ) : ( @@ -1076,7 +1075,7 @@ export default function PurchaseOrderPage() { updateDetailRow(idx, "supplier_code", v); updateDetailRow(idx, "supplier_name", name); }}> - + {(categoryOptions["supplier_code"] || []).map(o => ( {o.label} @@ -1092,11 +1091,11 @@ export default function PurchaseOrderPage() { return {row.unit}; case "order_qty": return ( - + {isReadOnly ? ( {row.order_qty ? Number(row.order_qty).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1106,11 +1105,11 @@ export default function PurchaseOrderPage() { return {row.remain_qty ? Number(row.remain_qty).toLocaleString() : "0"}; case "unit_price": return ( - + {isReadOnly ? ( {row.unit_price ? Number(row.unit_price).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1118,21 +1117,21 @@ export default function PurchaseOrderPage() { return {row.amount ? Number(row.amount).toLocaleString() : ""}; case "due_date": return ( - + {isReadOnly ? ( {row.due_date} ) : ( - updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs w-full" /> )} ); case "memo": return ( - + {isReadOnly ? ( {row.memo} ) : ( - updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs w-full" /> )} ); diff --git a/frontend/app/(main)/COMPANY_10/sales/order/page.tsx b/frontend/app/(main)/COMPANY_10/sales/order/page.tsx index cc11fc59..8f137c7a 100644 --- a/frontend/app/(main)/COMPANY_10/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_10/sales/order/page.tsx @@ -1320,44 +1320,44 @@ export default function SalesOrderPage() {
- No - 품번 - 품명 - 규격 - 재질 - 포장재 - 단위 - 수량 - 포장수량 - 단가 - 금액 - 납기일 - 분할/삭제 + No + 품번 + 품명 + 규격 + 재질 + 포장재 + 단위 + 수량 + 포장수량 + 단가 + 금액 + 납기일 + 분할/삭제 {detailRows.map((row, idx) => ( {idx + 1} - - {row.part_code} + + {row.part_code} - - {row.part_name} + + {row.part_name} - {row.spec} - {row.material} + {row.spec} + {row.material} updateDetailRow(idx, "packing_material", e.target.value)} placeholder="포장재" - className="h-8 text-xs" + className="h-8 text-xs w-full" />
+
c.key)} strategy={horizontalListSortingStrategy}> @@ -1061,12 +1060,12 @@ export default function PurchaseOrderPage() { {visibleModalColumns.map((col) => { switch (col.key) { case "item_code": - return {row.item_code}; + return {row.item_code}; case "item_name": - return {row.item_name}; + return {row.item_name}; case "supplier": return ( - + {isReadOnly ? ( {(categoryOptions["supplier_code"] || []).find(o => o.code === row.supplier_code)?.label || row.supplier_code || "-"} ) : ( @@ -1076,7 +1075,7 @@ export default function PurchaseOrderPage() { updateDetailRow(idx, "supplier_code", v); updateDetailRow(idx, "supplier_name", name); }}> - + {(categoryOptions["supplier_code"] || []).map(o => ( {o.label} @@ -1092,11 +1091,11 @@ export default function PurchaseOrderPage() { return {row.unit}; case "order_qty": return ( - + {isReadOnly ? ( {row.order_qty ? Number(row.order_qty).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1106,11 +1105,11 @@ export default function PurchaseOrderPage() { return {row.remain_qty ? Number(row.remain_qty).toLocaleString() : "0"}; case "unit_price": return ( - + {isReadOnly ? ( {row.unit_price ? Number(row.unit_price).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1118,21 +1117,21 @@ export default function PurchaseOrderPage() { return {row.amount ? Number(row.amount).toLocaleString() : ""}; case "due_date": return ( - + {isReadOnly ? ( {row.due_date} ) : ( - updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs w-full" /> )} ); case "memo": return ( - + {isReadOnly ? ( {row.memo} ) : ( - updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs w-full" /> )} ); diff --git a/frontend/app/(main)/COMPANY_16/sales/order/page.tsx b/frontend/app/(main)/COMPANY_16/sales/order/page.tsx index cc11fc59..8f137c7a 100644 --- a/frontend/app/(main)/COMPANY_16/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_16/sales/order/page.tsx @@ -1320,44 +1320,44 @@ export default function SalesOrderPage() {
- No - 품번 - 품명 - 규격 - 재질 - 포장재 - 단위 - 수량 - 포장수량 - 단가 - 금액 - 납기일 - 분할/삭제 + No + 품번 + 품명 + 규격 + 재질 + 포장재 + 단위 + 수량 + 포장수량 + 단가 + 금액 + 납기일 + 분할/삭제 {detailRows.map((row, idx) => ( {idx + 1} - - {row.part_code} + + {row.part_code} - - {row.part_name} + + {row.part_name} - {row.spec} - {row.material} + {row.spec} + {row.material} updateDetailRow(idx, "packing_material", e.target.value)} placeholder="포장재" - className="h-8 text-xs" + className="h-8 text-xs w-full" />
+
c.key)} strategy={horizontalListSortingStrategy}> @@ -1061,12 +1060,12 @@ export default function PurchaseOrderPage() { {visibleModalColumns.map((col) => { switch (col.key) { case "item_code": - return {row.item_code}; + return {row.item_code}; case "item_name": - return {row.item_name}; + return {row.item_name}; case "supplier": return ( - + {isReadOnly ? ( {(categoryOptions["supplier_code"] || []).find(o => o.code === row.supplier_code)?.label || row.supplier_code || "-"} ) : ( @@ -1076,7 +1075,7 @@ export default function PurchaseOrderPage() { updateDetailRow(idx, "supplier_code", v); updateDetailRow(idx, "supplier_name", name); }}> - + {(categoryOptions["supplier_code"] || []).map(o => ( {o.label} @@ -1092,11 +1091,11 @@ export default function PurchaseOrderPage() { return {row.unit}; case "order_qty": return ( - + {isReadOnly ? ( {row.order_qty ? Number(row.order_qty).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1106,11 +1105,11 @@ export default function PurchaseOrderPage() { return {row.remain_qty ? Number(row.remain_qty).toLocaleString() : "0"}; case "unit_price": return ( - + {isReadOnly ? ( {row.unit_price ? Number(row.unit_price).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1118,21 +1117,21 @@ export default function PurchaseOrderPage() { return {row.amount ? Number(row.amount).toLocaleString() : ""}; case "due_date": return ( - + {isReadOnly ? ( {row.due_date} ) : ( - updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs w-full" /> )} ); case "memo": return ( - + {isReadOnly ? ( {row.memo} ) : ( - updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs w-full" /> )} ); diff --git a/frontend/app/(main)/COMPANY_29/sales/order/page.tsx b/frontend/app/(main)/COMPANY_29/sales/order/page.tsx index cc11fc59..8f137c7a 100644 --- a/frontend/app/(main)/COMPANY_29/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_29/sales/order/page.tsx @@ -1320,44 +1320,44 @@ export default function SalesOrderPage() {
- No - 품번 - 품명 - 규격 - 재질 - 포장재 - 단위 - 수량 - 포장수량 - 단가 - 금액 - 납기일 - 분할/삭제 + No + 품번 + 품명 + 규격 + 재질 + 포장재 + 단위 + 수량 + 포장수량 + 단가 + 금액 + 납기일 + 분할/삭제 {detailRows.map((row, idx) => ( {idx + 1} - - {row.part_code} + + {row.part_code} - - {row.part_name} + + {row.part_name} - {row.spec} - {row.material} + {row.spec} + {row.material} updateDetailRow(idx, "packing_material", e.target.value)} placeholder="포장재" - className="h-8 text-xs" + className="h-8 text-xs w-full" />
+
c.key)} strategy={horizontalListSortingStrategy}> @@ -1061,12 +1060,12 @@ export default function PurchaseOrderPage() { {visibleModalColumns.map((col) => { switch (col.key) { case "item_code": - return {row.item_code}; + return {row.item_code}; case "item_name": - return {row.item_name}; + return {row.item_name}; case "supplier": return ( - + {isReadOnly ? ( {(categoryOptions["supplier_code"] || []).find(o => o.code === row.supplier_code)?.label || row.supplier_code || "-"} ) : ( @@ -1076,7 +1075,7 @@ export default function PurchaseOrderPage() { updateDetailRow(idx, "supplier_code", v); updateDetailRow(idx, "supplier_name", name); }}> - + {(categoryOptions["supplier_code"] || []).map(o => ( {o.label} @@ -1092,11 +1091,11 @@ export default function PurchaseOrderPage() { return {row.unit}; case "order_qty": return ( - + {isReadOnly ? ( {row.order_qty ? Number(row.order_qty).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1106,11 +1105,11 @@ export default function PurchaseOrderPage() { return {row.remain_qty ? Number(row.remain_qty).toLocaleString() : "0"}; case "unit_price": return ( - + {isReadOnly ? ( {row.unit_price ? Number(row.unit_price).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1118,21 +1117,21 @@ export default function PurchaseOrderPage() { return {row.amount ? Number(row.amount).toLocaleString() : ""}; case "due_date": return ( - + {isReadOnly ? ( {row.due_date} ) : ( - updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs w-full" /> )} ); case "memo": return ( - + {isReadOnly ? ( {row.memo} ) : ( - updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs w-full" /> )} ); diff --git a/frontend/app/(main)/COMPANY_30/sales/order/page.tsx b/frontend/app/(main)/COMPANY_30/sales/order/page.tsx index 3bcab8bc..8f137c7a 100644 --- a/frontend/app/(main)/COMPANY_30/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_30/sales/order/page.tsx @@ -517,42 +517,44 @@ export default function SalesOrderPage() { } }; - // 삭제 (마스터 + 디테일) + // 삭제 (선택한 디테일 삭제 → 디테일 0건인 마스터 자동 삭제) const handleDelete = async () => { - if (checkedIds.length === 0) { toast.error("삭제할 수주를 선택해주세요."); return; } - const selectedItems = orders.filter((o) => checkedIds.includes(o.id)); - const orderNos = [...new Set(selectedItems.map((o) => o.order_no))]; - const ok = await confirm(`${orderNos.length}건의 수주를 삭제하시겠습니까?`, { + if (checkedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; } + const ok = await confirm(`${checkedIds.length}건의 수주 항목을 삭제하시겠습니까?`, { description: "삭제된 데이터는 복구할 수 없습니다.", variant: "destructive", confirmText: "삭제", }); if (!ok) return; try { + // 1. 선택한 디테일 삭제 + await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, { + data: checkedIds.map((id) => ({ id })), + }); + + // 2. 영향받는 수주번호의 잔여 디테일 확인 → 0건이면 마스터도 삭제 + const selectedItems = orders.filter((o) => checkedIds.includes(o.id)); + const orderNos = [...new Set(selectedItems.map((o) => o.order_no))]; for (const orderNo of orderNos) { - // 디테일 삭제 - const detailRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, { - page: 1, size: 9999, - dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] }, - autoFilter: true, - }); - const details = detailRes.data?.data?.data || detailRes.data?.data?.rows || []; - if (details.length > 0) { - await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, { - data: details.map((d: any) => ({ id: d.id })), - }); - } - // 마스터 삭제 - const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, { + const remainRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, { page: 1, size: 1, dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] }, autoFilter: true, }); - const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || []; - if (masters.length > 0) { - await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, { - data: masters.map((m: any) => ({ id: m.id })), + const remaining = remainRes.data?.data?.data || remainRes.data?.data?.rows || []; + if (remaining.length === 0) { + // 디테일 0건 → 마스터 삭제 + const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, { + page: 1, size: 1, + dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] }, + autoFilter: true, }); + const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || []; + if (masters.length > 0) { + await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, { + data: masters.map((m: any) => ({ id: m.id })), + }); + } } } toast.success("삭제되었습니다."); @@ -622,6 +624,15 @@ export default function SalesOrderPage() { const filters: any[] = []; if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword }); + // 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응) + if (itemSearchDivision !== "all") { + const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; + // 코드 또는 라벨이 저장된 경우 모두 조회하기 위해 in 연산자 사용 + const divValues = [itemSearchDivision]; + if (divLabel) divValues.push(divLabel); + filters.push({ columnName: "division", operator: "in", value: divValues }); + } + // 거래처우선 단가방식일 때 거래처에 연결된 품목만 필터링 const isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI"; const partnerId = masterForm.partner_id; @@ -630,7 +641,7 @@ export default function SalesOrderPage() { if (isCustomerPrice && partnerId) { try { const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] }, autoFilter: true, }); @@ -640,31 +651,22 @@ export default function SalesOrderPage() { } const res = await apiClient.post(`/table-management/tables/item_info/data`, { - page: 1, size: 500, + page: p, size: s, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, }); const resData = res.data?.data; - let allRows = resData?.data || resData?.rows || []; + let rows = resData?.data || resData?.rows || []; + const serverTotal = resData?.total || resData?.totalCount || rows.length; - // 거래처우선일 때 연결된 품목만 표시 + // 거래처우선일 때 연결된 품목만 표시 (클라이언트 필터) if (customerItemIds) { - allRows = allRows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id)); + rows = rows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id)); } - // 관리품목 필터 (코드/라벨 혼재 대응) - if (itemSearchDivision !== "all") { - const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; - allRows = allRows.filter((item: any) => { - const div = item.division || ""; - return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel)); - }); - } - const total = allRows.length; - const start = (p - 1) * s; - setItemSearchResults(allRows.slice(start, start + s)); - setItemTotal(total); - setItemTotalPages(Math.max(1, Math.ceil(total / s))); + setItemSearchResults(rows); + setItemTotal(serverTotal); + setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s))); } catch { /* skip */ } finally { setItemSearchLoading(false); } @@ -1318,44 +1320,44 @@ export default function SalesOrderPage() {
- No - 품번 - 품명 - 규격 - 재질 - 포장재 - 단위 - 수량 - 포장수량 - 단가 - 금액 - 납기일 - 분할/삭제 + No + 품번 + 품명 + 규격 + 재질 + 포장재 + 단위 + 수량 + 포장수량 + 단가 + 금액 + 납기일 + 분할/삭제 {detailRows.map((row, idx) => ( {idx + 1} - - {row.part_code} + + {row.part_code} - - {row.part_name} + + {row.part_name} - {row.spec} - {row.material} + {row.spec} + {row.material} updateDetailRow(idx, "packing_material", e.target.value)} placeholder="포장재" - className="h-8 text-xs" + className="h-8 text-xs w-full" /> - - - 전체 - {(categoryOptions["item_division"] || []).map((o) => ( - {o.label} - ))} - - +
영업관리
diff --git a/frontend/app/(main)/COMPANY_7/purchase/order/page.tsx b/frontend/app/(main)/COMPANY_7/purchase/order/page.tsx index 90116bac..f712d3ed 100644 --- a/frontend/app/(main)/COMPANY_7/purchase/order/page.tsx +++ b/frontend/app/(main)/COMPANY_7/purchase/order/page.tsx @@ -88,18 +88,18 @@ const GRID_COLUMNS_CONFIG = [ ]; const MODAL_DETAIL_COLUMNS = [ - { key: "item_code", label: "품번", width: "w-[120px]" }, - { key: "item_name", label: "품명", width: "w-[120px]" }, - { key: "supplier", label: "공급업체", width: "w-[150px]" }, - { key: "spec", label: "규격", width: "w-[80px]" }, - { key: "unit", label: "단위", width: "w-[60px]" }, - { key: "order_qty", label: "발주수량", width: "w-[90px]" }, - { key: "received_qty", label: "입고수량", width: "w-[90px]" }, - { key: "remain_qty", label: "잔량", width: "w-[80px]" }, - { key: "unit_price", label: "단가", width: "w-[100px]" }, - { key: "amount", label: "금액", width: "w-[100px]" }, - { key: "due_date", label: "납기일", width: "w-[160px]" }, - { key: "memo", label: "메모", width: "w-[120px]" }, + { key: "item_code", label: "품번", width: "min-w-[120px]" }, + { key: "item_name", label: "품명", width: "min-w-[150px]" }, + { key: "supplier", label: "공급업체", width: "min-w-[150px]" }, + { key: "spec", label: "규격", width: "min-w-[80px]" }, + { key: "unit", label: "단위", width: "min-w-[90px]" }, + { key: "order_qty", label: "발주수량", width: "min-w-[90px]" }, + { key: "received_qty", label: "입고수량", width: "min-w-[90px]" }, + { key: "remain_qty", label: "잔량", width: "min-w-[80px]" }, + { key: "unit_price", label: "단가", width: "min-w-[100px]" }, + { key: "amount", label: "금액", width: "min-w-[100px]" }, + { key: "due_date", label: "납기일", width: "min-w-[160px]" }, + { key: "memo", label: "메모", width: "min-w-[120px]" }, ]; const MODAL_COL_ORDER_KEY = "purchase_order_modal_col_order_c16"; @@ -237,7 +237,7 @@ export default function PurchaseOrderPage() { ); try { const suppRes = await apiClient.post(`/table-management/tables/supplier_mng/data`, { - page: 1, size: 500, autoFilter: true, + page: 1, size: 5000, autoFilter: true, }); const supps = suppRes.data?.data?.data || suppRes.data?.data?.rows || []; optMap["supplier_code"] = supps.map((s: any) => ({ @@ -247,7 +247,7 @@ export default function PurchaseOrderPage() { } catch { /* skip */ } try { const userRes = await apiClient.post(`/table-management/tables/user_info/data`, { - page: 1, size: 500, autoFilter: true, + page: 1, size: 5000, autoFilter: true, }); const users = userRes.data?.data?.data || userRes.data?.data?.rows || []; optMap["manager"] = users.map((u: any) => ({ @@ -293,7 +293,7 @@ export default function PurchaseOrderPage() { } const res = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: detailFilters.length > 0 ? { enabled: true, filters: detailFilters } : undefined, autoFilter: true, sort: { columnName: "purchase_no", order: "desc" }, @@ -538,7 +538,7 @@ export default function PurchaseOrderPage() { } }; - // 품목 검색 + // 품목 검색 (수주관리와 동일한 서버 페이징 방식) const searchItems = async (page?: number, size?: number) => { const p = page ?? itemPage; const s = size ?? itemPageSize; @@ -548,25 +548,24 @@ export default function PurchaseOrderPage() { if (itemSearchKeyword) { filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword }); } + // 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응) + if (itemSearchDivision !== "all") { + const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; + const divValues = [itemSearchDivision]; + if (divLabel) divValues.push(divLabel); + filters.push({ columnName: "division", operator: "in", value: divValues }); + } const res = await apiClient.post(`/table-management/tables/item_info/data`, { - page: 1, size: 500, + page: p, size: s, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, }); const resData = res.data?.data; - let allRows = resData?.data || resData?.rows || []; - if (itemSearchDivision !== "all") { - const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; - allRows = allRows.filter((item: any) => { - const div = item.division || ""; - return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel)); - }); - } - const total = allRows.length; - const start = (p - 1) * s; - setItemSearchResults(allRows.slice(start, start + s)); - setItemTotal(total); - setItemTotalPages(Math.max(1, Math.ceil(total / s))); + const rows = resData?.data || resData?.rows || []; + const serverTotal = resData?.total || resData?.totalCount || rows.length; + setItemSearchResults(rows); + setItemTotal(serverTotal); + setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s))); } catch { /* skip */ } finally { setItemSearchLoading(false); } @@ -608,7 +607,7 @@ export default function PurchaseOrderPage() { try { const itemIds = selected.map((item) => item.item_number || item.id); const res = await apiClient.post(`/table-management/tables/supplier_item_prices/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [ @@ -671,7 +670,7 @@ export default function PurchaseOrderPage() { if (itemCodes.length === 0) return; try { const res = await apiClient.post(`/table-management/tables/item_info/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [{ columnName: "item_number", operator: "in", value: itemCodes }] }, autoFilter: true, }); @@ -693,7 +692,7 @@ export default function PurchaseOrderPage() { if (itemCodes.length === 0) return; try { const res = await apiClient.post(`/table-management/tables/supplier_item_prices/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [ { columnName: "supplier_id", operator: "equals", value: supplierCode }, { columnName: "item_id", operator: "in", value: itemCodes }, @@ -1037,7 +1036,7 @@ export default function PurchaseOrderPage() { ) : (
-
+
c.key)} strategy={horizontalListSortingStrategy}> @@ -1061,12 +1060,12 @@ export default function PurchaseOrderPage() { {visibleModalColumns.map((col) => { switch (col.key) { case "item_code": - return {row.item_code}; + return {row.item_code}; case "item_name": - return {row.item_name}; + return {row.item_name}; case "supplier": return ( - + {isReadOnly ? ( {(categoryOptions["supplier_code"] || []).find(o => o.code === row.supplier_code)?.label || row.supplier_code || "-"} ) : ( @@ -1076,7 +1075,7 @@ export default function PurchaseOrderPage() { updateDetailRow(idx, "supplier_code", v); updateDetailRow(idx, "supplier_name", name); }}> - + {(categoryOptions["supplier_code"] || []).map(o => ( {o.label} @@ -1092,11 +1091,11 @@ export default function PurchaseOrderPage() { return {row.unit}; case "order_qty": return ( - + {isReadOnly ? ( {row.order_qty ? Number(row.order_qty).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1106,11 +1105,11 @@ export default function PurchaseOrderPage() { return {row.remain_qty ? Number(row.remain_qty).toLocaleString() : "0"}; case "unit_price": return ( - + {isReadOnly ? ( {row.unit_price ? Number(row.unit_price).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1118,21 +1117,21 @@ export default function PurchaseOrderPage() { return {row.amount ? Number(row.amount).toLocaleString() : ""}; case "due_date": return ( - + {isReadOnly ? ( {row.due_date} ) : ( - updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs w-full" /> )} ); case "memo": return ( - + {isReadOnly ? ( {row.memo} ) : ( - updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs w-full" /> )} ); diff --git a/frontend/app/(main)/COMPANY_7/sales/order/page.tsx b/frontend/app/(main)/COMPANY_7/sales/order/page.tsx index cc11fc59..8f137c7a 100644 --- a/frontend/app/(main)/COMPANY_7/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_7/sales/order/page.tsx @@ -1320,44 +1320,44 @@ export default function SalesOrderPage() {
- No - 품번 - 품명 - 규격 - 재질 - 포장재 - 단위 - 수량 - 포장수량 - 단가 - 금액 - 납기일 - 분할/삭제 + No + 품번 + 품명 + 규격 + 재질 + 포장재 + 단위 + 수량 + 포장수량 + 단가 + 금액 + 납기일 + 분할/삭제 {detailRows.map((row, idx) => ( {idx + 1} - - {row.part_code} + + {row.part_code} - - {row.part_name} + + {row.part_name} - {row.spec} - {row.material} + {row.spec} + {row.material} updateDetailRow(idx, "packing_material", e.target.value)} placeholder="포장재" - className="h-8 text-xs" + className="h-8 text-xs w-full" />
+
c.key)} strategy={horizontalListSortingStrategy}> @@ -1061,12 +1060,12 @@ export default function PurchaseOrderPage() { {visibleModalColumns.map((col) => { switch (col.key) { case "item_code": - return {row.item_code}; + return {row.item_code}; case "item_name": - return {row.item_name}; + return {row.item_name}; case "supplier": return ( - + {isReadOnly ? ( {(categoryOptions["supplier_code"] || []).find(o => o.code === row.supplier_code)?.label || row.supplier_code || "-"} ) : ( @@ -1076,7 +1075,7 @@ export default function PurchaseOrderPage() { updateDetailRow(idx, "supplier_code", v); updateDetailRow(idx, "supplier_name", name); }}> - + {(categoryOptions["supplier_code"] || []).map(o => ( {o.label} @@ -1092,11 +1091,11 @@ export default function PurchaseOrderPage() { return {row.unit}; case "order_qty": return ( - + {isReadOnly ? ( {row.order_qty ? Number(row.order_qty).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1106,11 +1105,11 @@ export default function PurchaseOrderPage() { return {row.remain_qty ? Number(row.remain_qty).toLocaleString() : "0"}; case "unit_price": return ( - + {isReadOnly ? ( {row.unit_price ? Number(row.unit_price).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1118,21 +1117,21 @@ export default function PurchaseOrderPage() { return {row.amount ? Number(row.amount).toLocaleString() : ""}; case "due_date": return ( - + {isReadOnly ? ( {row.due_date} ) : ( - updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs w-full" /> )} ); case "memo": return ( - + {isReadOnly ? ( {row.memo} ) : ( - updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs w-full" /> )} ); diff --git a/frontend/app/(main)/COMPANY_8/sales/order/page.tsx b/frontend/app/(main)/COMPANY_8/sales/order/page.tsx index cc11fc59..8f137c7a 100644 --- a/frontend/app/(main)/COMPANY_8/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_8/sales/order/page.tsx @@ -1320,44 +1320,44 @@ export default function SalesOrderPage() {
- No - 품번 - 품명 - 규격 - 재질 - 포장재 - 단위 - 수량 - 포장수량 - 단가 - 금액 - 납기일 - 분할/삭제 + No + 품번 + 품명 + 규격 + 재질 + 포장재 + 단위 + 수량 + 포장수량 + 단가 + 금액 + 납기일 + 분할/삭제 {detailRows.map((row, idx) => ( {idx + 1} - - {row.part_code} + + {row.part_code} - - {row.part_name} + + {row.part_name} - {row.spec} - {row.material} + {row.spec} + {row.material} updateDetailRow(idx, "packing_material", e.target.value)} placeholder="포장재" - className="h-8 text-xs" + className="h-8 text-xs w-full" />
+
c.key)} strategy={horizontalListSortingStrategy}> @@ -1061,12 +1060,12 @@ export default function PurchaseOrderPage() { {visibleModalColumns.map((col) => { switch (col.key) { case "item_code": - return {row.item_code}; + return {row.item_code}; case "item_name": - return {row.item_name}; + return {row.item_name}; case "supplier": return ( - + {isReadOnly ? ( {(categoryOptions["supplier_code"] || []).find(o => o.code === row.supplier_code)?.label || row.supplier_code || "-"} ) : ( @@ -1076,7 +1075,7 @@ export default function PurchaseOrderPage() { updateDetailRow(idx, "supplier_code", v); updateDetailRow(idx, "supplier_name", name); }}> - + {(categoryOptions["supplier_code"] || []).map(o => ( {o.label} @@ -1092,11 +1091,11 @@ export default function PurchaseOrderPage() { return {row.unit}; case "order_qty": return ( - + {isReadOnly ? ( {row.order_qty ? Number(row.order_qty).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "order_qty", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1106,11 +1105,11 @@ export default function PurchaseOrderPage() { return {row.remain_qty ? Number(row.remain_qty).toLocaleString() : "0"}; case "unit_price": return ( - + {isReadOnly ? ( {row.unit_price ? Number(row.unit_price).toLocaleString() : ""} ) : ( - updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono" /> + updateDetailRow(idx, "unit_price", parseNumber(e.target.value))} className="h-8 text-xs text-right font-mono w-full" /> )} ); @@ -1118,21 +1117,21 @@ export default function PurchaseOrderPage() { return {row.amount ? Number(row.amount).toLocaleString() : ""}; case "due_date": return ( - + {isReadOnly ? ( {row.due_date} ) : ( - updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "due_date", e.target.value)} className="h-8 text-xs w-full" /> )} ); case "memo": return ( - + {isReadOnly ? ( {row.memo} ) : ( - updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs" /> + updateDetailRow(idx, "memo", e.target.value)} className="h-8 text-xs w-full" /> )} ); diff --git a/frontend/app/(main)/COMPANY_9/sales/order/page.tsx b/frontend/app/(main)/COMPANY_9/sales/order/page.tsx index 3bcab8bc..8f137c7a 100644 --- a/frontend/app/(main)/COMPANY_9/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_9/sales/order/page.tsx @@ -517,42 +517,44 @@ export default function SalesOrderPage() { } }; - // 삭제 (마스터 + 디테일) + // 삭제 (선택한 디테일 삭제 → 디테일 0건인 마스터 자동 삭제) const handleDelete = async () => { - if (checkedIds.length === 0) { toast.error("삭제할 수주를 선택해주세요."); return; } - const selectedItems = orders.filter((o) => checkedIds.includes(o.id)); - const orderNos = [...new Set(selectedItems.map((o) => o.order_no))]; - const ok = await confirm(`${orderNos.length}건의 수주를 삭제하시겠습니까?`, { + if (checkedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; } + const ok = await confirm(`${checkedIds.length}건의 수주 항목을 삭제하시겠습니까?`, { description: "삭제된 데이터는 복구할 수 없습니다.", variant: "destructive", confirmText: "삭제", }); if (!ok) return; try { + // 1. 선택한 디테일 삭제 + await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, { + data: checkedIds.map((id) => ({ id })), + }); + + // 2. 영향받는 수주번호의 잔여 디테일 확인 → 0건이면 마스터도 삭제 + const selectedItems = orders.filter((o) => checkedIds.includes(o.id)); + const orderNos = [...new Set(selectedItems.map((o) => o.order_no))]; for (const orderNo of orderNos) { - // 디테일 삭제 - const detailRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, { - page: 1, size: 9999, - dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] }, - autoFilter: true, - }); - const details = detailRes.data?.data?.data || detailRes.data?.data?.rows || []; - if (details.length > 0) { - await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, { - data: details.map((d: any) => ({ id: d.id })), - }); - } - // 마스터 삭제 - const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, { + const remainRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, { page: 1, size: 1, dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] }, autoFilter: true, }); - const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || []; - if (masters.length > 0) { - await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, { - data: masters.map((m: any) => ({ id: m.id })), + const remaining = remainRes.data?.data?.data || remainRes.data?.data?.rows || []; + if (remaining.length === 0) { + // 디테일 0건 → 마스터 삭제 + const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, { + page: 1, size: 1, + dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] }, + autoFilter: true, }); + const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || []; + if (masters.length > 0) { + await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, { + data: masters.map((m: any) => ({ id: m.id })), + }); + } } } toast.success("삭제되었습니다."); @@ -622,6 +624,15 @@ export default function SalesOrderPage() { const filters: any[] = []; if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword }); + // 관리품목 필터를 서버 쿼리에 포함 (코드 + 라벨 양쪽 대응) + if (itemSearchDivision !== "all") { + const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; + // 코드 또는 라벨이 저장된 경우 모두 조회하기 위해 in 연산자 사용 + const divValues = [itemSearchDivision]; + if (divLabel) divValues.push(divLabel); + filters.push({ columnName: "division", operator: "in", value: divValues }); + } + // 거래처우선 단가방식일 때 거래처에 연결된 품목만 필터링 const isCustomerPrice = masterForm.price_mode === "CAT_MM0BV3OS_41DX" || masterForm.price_mode === "CAT_MLKG7D8K_N8SI"; const partnerId = masterForm.partner_id; @@ -630,7 +641,7 @@ export default function SalesOrderPage() { if (isCustomerPrice && partnerId) { try { const mappingRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, { - page: 1, size: 500, + page: 1, size: 5000, dataFilter: { enabled: true, filters: [{ columnName: "customer_id", operator: "equals", value: partnerId }] }, autoFilter: true, }); @@ -640,31 +651,22 @@ export default function SalesOrderPage() { } const res = await apiClient.post(`/table-management/tables/item_info/data`, { - page: 1, size: 500, + page: p, size: s, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, }); const resData = res.data?.data; - let allRows = resData?.data || resData?.rows || []; + let rows = resData?.data || resData?.rows || []; + const serverTotal = resData?.total || resData?.totalCount || rows.length; - // 거래처우선일 때 연결된 품목만 표시 + // 거래처우선일 때 연결된 품목만 표시 (클라이언트 필터) if (customerItemIds) { - allRows = allRows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id)); + rows = rows.filter((item: any) => customerItemIds!.has(item.item_number) || customerItemIds!.has(item.id)); } - // 관리품목 필터 (코드/라벨 혼재 대응) - if (itemSearchDivision !== "all") { - const divLabel = categoryOptions["item_division"]?.find((o) => o.code === itemSearchDivision)?.label || ""; - allRows = allRows.filter((item: any) => { - const div = item.division || ""; - return div.includes(itemSearchDivision) || (divLabel && div.includes(divLabel)); - }); - } - const total = allRows.length; - const start = (p - 1) * s; - setItemSearchResults(allRows.slice(start, start + s)); - setItemTotal(total); - setItemTotalPages(Math.max(1, Math.ceil(total / s))); + setItemSearchResults(rows); + setItemTotal(serverTotal); + setItemTotalPages(Math.max(1, Math.ceil(serverTotal / s))); } catch { /* skip */ } finally { setItemSearchLoading(false); } @@ -1318,44 +1320,44 @@ export default function SalesOrderPage() {
- No - 품번 - 품명 - 규격 - 재질 - 포장재 - 단위 - 수량 - 포장수량 - 단가 - 금액 - 납기일 - 분할/삭제 + No + 품번 + 품명 + 규격 + 재질 + 포장재 + 단위 + 수량 + 포장수량 + 단가 + 금액 + 납기일 + 분할/삭제 {detailRows.map((row, idx) => ( {idx + 1} - - {row.part_code} + + {row.part_code} - - {row.part_name} + + {row.part_name} - {row.spec} - {row.material} + {row.spec} + {row.material} updateDetailRow(idx, "packing_material", e.target.value)} placeholder="포장재" - className="h-8 text-xs" + className="h-8 text-xs w-full" /> - - - 전체 - {(categoryOptions["item_division"] || []).map((o) => ( - {o.label} - ))} - - +
영업관리