fix: Correct SQL parameter indexing and improve date handling in various components

- Updated SQL query in `productionPlanService.ts` to fix parameter indexing for company code.
- Refactored date handling in `department/page.tsx`, `customer/page.tsx`, and `sales-item/page.tsx` to ensure consistent date formatting.
- Enhanced equipment list state management in `production/plan-management/page.tsx` to use more descriptive property names.

These changes aim to improve the reliability of SQL operations and ensure consistent date handling across the application.
This commit is contained in:
kjs
2026-04-08 17:45:28 +09:00
parent 2a23cadb41
commit 0345926698
29 changed files with 396 additions and 310 deletions
@@ -371,7 +371,7 @@ export async function updatePlan(
const query = `
UPDATE production_plan_mng
SET ${setClauses.join(", ")}
WHERE id = $${paramIdx - 1} AND company_code = $${paramIdx}
WHERE id = $${paramIdx} AND company_code = $${paramIdx + 1}
RETURNING *
`;
@@ -309,7 +309,8 @@ export default function DepartmentPage() {
};
// 퇴사일 기반 재직/퇴사 분리
const today = new Date().toISOString().split("T")[0];
const _now = new Date();
const today = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
const activeMembers = members.filter((m) => !m.end_date || m.end_date.substring(0, 10) >= today);
const resignedMembers = members.filter((m) => m.end_date && m.end_date.substring(0, 10) < today);
@@ -139,7 +139,7 @@ export default function ProductionPlanManagementPage() {
const [stockItems, setStockItems] = useState<StockShortageItem[]>([]);
const [finishedPlans, setFinishedPlans] = useState<ProductionPlan[]>([]);
const [semiPlans, setSemiPlans] = useState<ProductionPlan[]>([]);
const [equipmentList, setEquipmentList] = useState<{ equipment_id: string; equipment_name: string }[]>([]);
const [equipmentList, setEquipmentList] = useState<{ id: string; equipment_code: string; equipment_name: string }[]>([]);
// 선택/토글 상태
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
@@ -659,7 +659,7 @@ export default function ProductionPlanManagementPage() {
setModalManager((plan as any).manager_name || "");
setModalWorkOrderNo((plan as any).work_order_no || "");
setModalRemarks(plan.remarks || "");
setModalEquipmentId(plan.equipment_id ? String(plan.equipment_id) : "");
setModalEquipmentId((plan as any).equipment_code || (plan.equipment_id ? String(plan.equipment_id) : ""));
setScheduleModalOpen(true);
}, []);
@@ -1426,8 +1426,8 @@ export default function ProductionPlanManagementPage() {
<SelectContent>
<SelectItem value="none"></SelectItem>
{equipmentList.map((eq) => (
<SelectItem key={eq.equipment_id} value={String(eq.equipment_id)}>
{eq.equipment_name} ({eq.equipment_id})
<SelectItem key={eq.id} value={eq.equipment_code || eq.id}>
{eq.equipment_name} ({eq.equipment_code})
</SelectItem>
))}
</SelectContent>
@@ -345,7 +345,8 @@ export default function CustomerManagementPage() {
if (!code) return "";
return priceCategoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const today = new Date().toISOString().split("T")[0];
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
// 품목 기준 그룹핑 — master: 첫 매핑 + 현재 단가, details: 전체 단가 리스트
const grouped: Record<string, { master: any; details: any[] }> = {};
@@ -810,11 +811,13 @@ export default function CustomerManagementPage() {
const searchItems = async () => {
setItemSearchLoading(true);
try {
const filters: any[] = [];
const filters: any[] = [
{ columnName: "division", operator: "contains", value: "CAT_ML8ZFVEL_1TOR" },
];
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
page: 1, size: 500,
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
dataFilter: { enabled: true, filters },
autoFilter: true,
});
const allItems = res.data?.data?.data || res.data?.data?.rows || [];
@@ -402,32 +402,41 @@ export default function SalesItemPage() {
if (found) custInfo = found;
} catch { /* skip */ }
const mappingRows = [{
_id: `m_existing_${row.id}`,
customer_item_code: row.customer_item_code || "",
customer_item_name: row.customer_item_name || "",
}].filter((m) => m.customer_item_code || m.customer_item_name);
const priceRows = [{
_id: `p_existing_${row.id}`,
start_date: row.start_date || "",
end_date: row.end_date || "",
currency_code: row.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: row.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: row.base_price ? String(row.base_price) : "",
discount_type: row.discount_type || "",
discount_value: row.discount_value ? String(row.discount_value) : "",
rounding_type: row.rounding_type || "",
rounding_unit_value: row.rounding_unit_value || "",
calculated_price: row.calculated_price ? String(row.calculated_price) : "",
}].filter((p) => p.base_price || p.start_date);
if (priceRows.length === 0) {
priceRows.push({
_id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "",
let mappingRows: any[] = [];
try {
const mapRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allMappings = mapRes.data?.data?.data || mapRes.data?.data?.rows || [];
mappingRows = allMappings
.filter((m: any) => m.customer_item_code || m.customer_item_name)
.map((m: any) => ({ _id: `m_existing_${m.id}`, customer_item_code: m.customer_item_code || "", customer_item_name: m.customer_item_name || "" }));
} catch { /* skip */ }
let priceRows: any[] = [];
try {
const priceRes = await apiClient.post(`/table-management/tables/customer_item_prices/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allPriceData = priceRes.data?.data?.data || priceRes.data?.data?.rows || [];
priceRows = allPriceData.map((p: any) => ({
_id: `p_existing_${p.id}`, start_date: p.start_date ? String(p.start_date).split("T")[0] : "", end_date: p.end_date ? String(p.end_date).split("T")[0] : "",
currency_code: p.currency_code || "CAT_MLAMDKVN_PZJI", base_price_type: p.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: p.base_price ? String(p.base_price) : "", discount_type: p.discount_type || "", discount_value: p.discount_value ? String(p.discount_value) : "",
rounding_type: p.rounding_type || "", rounding_unit_value: p.rounding_unit_value || "", calculated_price: p.calculated_price ? String(p.calculated_price) : "",
}));
} catch { /* skip */ }
if (priceRows.length === 0) {
priceRows.push({ _id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "" });
}
setSelectedCustsForDetail([custInfo]);
@@ -782,23 +791,17 @@ export default function SalesItemPage() {
"cursor-pointer h-[41px]",
customerCheckedIds.includes(row.id) ? "bg-primary/[0.08]" : "hover:bg-accent"
)}
onClick={() => {
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
onDoubleClick={() => openEditCust(row)}
>
<TableCell
className="text-center px-2"
onClick={(e) => {
e.stopPropagation();
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
>
<TableCell className="text-center px-2">
<Checkbox
checked={customerCheckedIds.includes(row.id)}
onCheckedChange={(checked) => {
if (checked === true) setCustomerCheckedIds((prev) => [...prev, row.id]);
else setCustomerCheckedIds((prev) => prev.filter((id) => id !== row.id));
}}
onCheckedChange={() => {}}
/>
</TableCell>
<TableCell className="text-[13px] font-mono text-muted-foreground">{row.customer_code}</TableCell>
@@ -309,7 +309,8 @@ export default function DepartmentPage() {
};
// 퇴사일 기반 재직/퇴사 분리
const today = new Date().toISOString().split("T")[0];
const _now = new Date();
const today = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
const activeMembers = members.filter((m) => !m.end_date || m.end_date.substring(0, 10) >= today);
const resignedMembers = members.filter((m) => m.end_date && m.end_date.substring(0, 10) < today);
@@ -139,7 +139,7 @@ export default function ProductionPlanManagementPage() {
const [stockItems, setStockItems] = useState<StockShortageItem[]>([]);
const [finishedPlans, setFinishedPlans] = useState<ProductionPlan[]>([]);
const [semiPlans, setSemiPlans] = useState<ProductionPlan[]>([]);
const [equipmentList, setEquipmentList] = useState<{ equipment_id: string; equipment_name: string }[]>([]);
const [equipmentList, setEquipmentList] = useState<{ id: string; equipment_code: string; equipment_name: string }[]>([]);
// 선택/토글 상태
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
@@ -659,7 +659,7 @@ export default function ProductionPlanManagementPage() {
setModalManager((plan as any).manager_name || "");
setModalWorkOrderNo((plan as any).work_order_no || "");
setModalRemarks(plan.remarks || "");
setModalEquipmentId(plan.equipment_id ? String(plan.equipment_id) : "");
setModalEquipmentId((plan as any).equipment_code || (plan.equipment_id ? String(plan.equipment_id) : ""));
setScheduleModalOpen(true);
}, []);
@@ -674,7 +674,10 @@ export default function ProductionPlanManagementPage() {
manager_name: modalManager,
work_order_no: modalWorkOrderNo,
remarks: modalRemarks,
equipment_id: modalEquipmentId ? Number(modalEquipmentId) : null,
equipment_code: modalEquipmentId && modalEquipmentId !== "none" ? modalEquipmentId : null,
equipment_name: modalEquipmentId && modalEquipmentId !== "none"
? equipmentList.find((eq) => (eq.equipment_code || eq.id) === modalEquipmentId)?.equipment_name || null
: null,
} as any);
if (res.success) {
toast.success("생산계획이 수정되었습니다");
@@ -1426,8 +1429,8 @@ export default function ProductionPlanManagementPage() {
<SelectContent>
<SelectItem value="none"></SelectItem>
{equipmentList.map((eq) => (
<SelectItem key={eq.equipment_id} value={String(eq.equipment_id)}>
{eq.equipment_name} ({eq.equipment_id})
<SelectItem key={eq.id} value={eq.equipment_code || eq.id}>
{eq.equipment_name} ({eq.equipment_code})
</SelectItem>
))}
</SelectContent>
@@ -345,7 +345,8 @@ export default function CustomerManagementPage() {
if (!code) return "";
return priceCategoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const today = new Date().toISOString().split("T")[0];
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
// 품목 기준 그룹핑 — master: 첫 매핑 + 현재 단가, details: 전체 단가 리스트
const grouped: Record<string, { master: any; details: any[] }> = {};
@@ -810,11 +811,13 @@ export default function CustomerManagementPage() {
const searchItems = async () => {
setItemSearchLoading(true);
try {
const filters: any[] = [];
const filters: any[] = [
{ columnName: "division", operator: "contains", value: "CAT_ML8ZFVEL_1TOR" },
];
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
page: 1, size: 500,
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
dataFilter: { enabled: true, filters },
autoFilter: true,
});
const allItems = res.data?.data?.data || res.data?.data?.rows || [];
@@ -402,25 +402,51 @@ export default function SalesItemPage() {
if (found) custInfo = found;
} catch { /* skip */ }
const mappingRows = [{
_id: `m_existing_${row.id}`,
customer_item_code: row.customer_item_code || "",
customer_item_name: row.customer_item_name || "",
}].filter((m) => m.customer_item_code || m.customer_item_name);
// 매핑 조회
let mappingRows: any[] = [];
try {
const mapRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allMappings = mapRes.data?.data?.data || mapRes.data?.data?.rows || [];
mappingRows = allMappings
.filter((m: any) => m.customer_item_code || m.customer_item_name)
.map((m: any) => ({
_id: `m_existing_${m.id}`,
customer_item_code: m.customer_item_code || "",
customer_item_name: m.customer_item_name || "",
}));
} catch { /* skip */ }
const priceRows = [{
_id: `p_existing_${row.id}`,
start_date: row.start_date || "",
end_date: row.end_date || "",
currency_code: row.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: row.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: row.base_price ? String(row.base_price) : "",
discount_type: row.discount_type || "",
discount_value: row.discount_value ? String(row.discount_value) : "",
rounding_type: row.rounding_type || "",
rounding_unit_value: row.rounding_unit_value || "",
calculated_price: row.calculated_price ? String(row.calculated_price) : "",
}].filter((p) => p.base_price || p.start_date);
// 단가 전체 조회
let priceRows: any[] = [];
try {
const priceRes = await apiClient.post(`/table-management/tables/customer_item_prices/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allPriceData = priceRes.data?.data?.data || priceRes.data?.data?.rows || [];
priceRows = allPriceData.map((p: any) => ({
_id: `p_existing_${p.id}`,
start_date: p.start_date ? String(p.start_date).split("T")[0] : "",
end_date: p.end_date ? String(p.end_date).split("T")[0] : "",
currency_code: p.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: p.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: p.base_price ? String(p.base_price) : "",
discount_type: p.discount_type || "",
discount_value: p.discount_value ? String(p.discount_value) : "",
rounding_type: p.rounding_type || "",
rounding_unit_value: p.rounding_unit_value || "",
calculated_price: p.calculated_price ? String(p.calculated_price) : "",
}));
} catch { /* skip */ }
if (priceRows.length === 0) {
priceRows.push({
@@ -782,23 +808,17 @@ export default function SalesItemPage() {
"cursor-pointer h-[41px]",
customerCheckedIds.includes(row.id) ? "bg-primary/[0.08]" : "hover:bg-accent"
)}
onClick={() => {
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
onDoubleClick={() => openEditCust(row)}
>
<TableCell
className="text-center px-2"
onClick={(e) => {
e.stopPropagation();
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
>
<TableCell className="text-center px-2">
<Checkbox
checked={customerCheckedIds.includes(row.id)}
onCheckedChange={(checked) => {
if (checked === true) setCustomerCheckedIds((prev) => [...prev, row.id]);
else setCustomerCheckedIds((prev) => prev.filter((id) => id !== row.id));
}}
onCheckedChange={() => {}}
/>
</TableCell>
<TableCell className="text-[13px] font-mono text-muted-foreground">{row.customer_code}</TableCell>
@@ -309,7 +309,8 @@ export default function DepartmentPage() {
};
// 퇴사일 기반 재직/퇴사 분리
const today = new Date().toISOString().split("T")[0];
const _now = new Date();
const today = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
const activeMembers = members.filter((m) => !m.end_date || m.end_date.substring(0, 10) >= today);
const resignedMembers = members.filter((m) => m.end_date && m.end_date.substring(0, 10) < today);
@@ -139,7 +139,7 @@ export default function ProductionPlanManagementPage() {
const [stockItems, setStockItems] = useState<StockShortageItem[]>([]);
const [finishedPlans, setFinishedPlans] = useState<ProductionPlan[]>([]);
const [semiPlans, setSemiPlans] = useState<ProductionPlan[]>([]);
const [equipmentList, setEquipmentList] = useState<{ equipment_id: string; equipment_name: string }[]>([]);
const [equipmentList, setEquipmentList] = useState<{ id: string; equipment_code: string; equipment_name: string }[]>([]);
// 선택/토글 상태
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
@@ -659,7 +659,7 @@ export default function ProductionPlanManagementPage() {
setModalManager((plan as any).manager_name || "");
setModalWorkOrderNo((plan as any).work_order_no || "");
setModalRemarks(plan.remarks || "");
setModalEquipmentId(plan.equipment_id ? String(plan.equipment_id) : "");
setModalEquipmentId((plan as any).equipment_code || (plan.equipment_id ? String(plan.equipment_id) : ""));
setScheduleModalOpen(true);
}, []);
@@ -1426,8 +1426,8 @@ export default function ProductionPlanManagementPage() {
<SelectContent>
<SelectItem value="none"></SelectItem>
{equipmentList.map((eq) => (
<SelectItem key={eq.equipment_id} value={String(eq.equipment_id)}>
{eq.equipment_name} ({eq.equipment_id})
<SelectItem key={eq.id} value={eq.equipment_code || eq.id}>
{eq.equipment_name} ({eq.equipment_code})
</SelectItem>
))}
</SelectContent>
@@ -345,7 +345,8 @@ export default function CustomerManagementPage() {
if (!code) return "";
return priceCategoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const today = new Date().toISOString().split("T")[0];
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
// 품목 기준 그룹핑 — master: 첫 매핑 + 현재 단가, details: 전체 단가 리스트
const grouped: Record<string, { master: any; details: any[] }> = {};
@@ -810,11 +811,13 @@ export default function CustomerManagementPage() {
const searchItems = async () => {
setItemSearchLoading(true);
try {
const filters: any[] = [];
const filters: any[] = [
{ columnName: "division", operator: "contains", value: "CAT_ML8ZFVEL_1TOR" },
];
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
page: 1, size: 500,
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
dataFilter: { enabled: true, filters },
autoFilter: true,
});
const allItems = res.data?.data?.data || res.data?.data?.rows || [];
@@ -402,32 +402,41 @@ export default function SalesItemPage() {
if (found) custInfo = found;
} catch { /* skip */ }
const mappingRows = [{
_id: `m_existing_${row.id}`,
customer_item_code: row.customer_item_code || "",
customer_item_name: row.customer_item_name || "",
}].filter((m) => m.customer_item_code || m.customer_item_name);
const priceRows = [{
_id: `p_existing_${row.id}`,
start_date: row.start_date || "",
end_date: row.end_date || "",
currency_code: row.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: row.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: row.base_price ? String(row.base_price) : "",
discount_type: row.discount_type || "",
discount_value: row.discount_value ? String(row.discount_value) : "",
rounding_type: row.rounding_type || "",
rounding_unit_value: row.rounding_unit_value || "",
calculated_price: row.calculated_price ? String(row.calculated_price) : "",
}].filter((p) => p.base_price || p.start_date);
if (priceRows.length === 0) {
priceRows.push({
_id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "",
let mappingRows: any[] = [];
try {
const mapRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allMappings = mapRes.data?.data?.data || mapRes.data?.data?.rows || [];
mappingRows = allMappings
.filter((m: any) => m.customer_item_code || m.customer_item_name)
.map((m: any) => ({ _id: `m_existing_${m.id}`, customer_item_code: m.customer_item_code || "", customer_item_name: m.customer_item_name || "" }));
} catch { /* skip */ }
let priceRows: any[] = [];
try {
const priceRes = await apiClient.post(`/table-management/tables/customer_item_prices/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allPriceData = priceRes.data?.data?.data || priceRes.data?.data?.rows || [];
priceRows = allPriceData.map((p: any) => ({
_id: `p_existing_${p.id}`, start_date: p.start_date ? String(p.start_date).split("T")[0] : "", end_date: p.end_date ? String(p.end_date).split("T")[0] : "",
currency_code: p.currency_code || "CAT_MLAMDKVN_PZJI", base_price_type: p.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: p.base_price ? String(p.base_price) : "", discount_type: p.discount_type || "", discount_value: p.discount_value ? String(p.discount_value) : "",
rounding_type: p.rounding_type || "", rounding_unit_value: p.rounding_unit_value || "", calculated_price: p.calculated_price ? String(p.calculated_price) : "",
}));
} catch { /* skip */ }
if (priceRows.length === 0) {
priceRows.push({ _id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "" });
}
setSelectedCustsForDetail([custInfo]);
@@ -782,23 +791,17 @@ export default function SalesItemPage() {
"cursor-pointer h-[41px]",
customerCheckedIds.includes(row.id) ? "bg-primary/[0.08]" : "hover:bg-accent"
)}
onClick={() => {
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
onDoubleClick={() => openEditCust(row)}
>
<TableCell
className="text-center px-2"
onClick={(e) => {
e.stopPropagation();
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
>
<TableCell className="text-center px-2">
<Checkbox
checked={customerCheckedIds.includes(row.id)}
onCheckedChange={(checked) => {
if (checked === true) setCustomerCheckedIds((prev) => [...prev, row.id]);
else setCustomerCheckedIds((prev) => prev.filter((id) => id !== row.id));
}}
onCheckedChange={() => {}}
/>
</TableCell>
<TableCell className="text-[13px] font-mono text-muted-foreground">{row.customer_code}</TableCell>
@@ -309,7 +309,8 @@ export default function DepartmentPage() {
};
// 퇴사일 기반 재직/퇴사 분리
const today = new Date().toISOString().split("T")[0];
const _now = new Date();
const today = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
const activeMembers = members.filter((m) => !m.end_date || m.end_date.substring(0, 10) >= today);
const resignedMembers = members.filter((m) => m.end_date && m.end_date.substring(0, 10) < today);
@@ -139,7 +139,7 @@ export default function ProductionPlanManagementPage() {
const [stockItems, setStockItems] = useState<StockShortageItem[]>([]);
const [finishedPlans, setFinishedPlans] = useState<ProductionPlan[]>([]);
const [semiPlans, setSemiPlans] = useState<ProductionPlan[]>([]);
const [equipmentList, setEquipmentList] = useState<{ equipment_id: string; equipment_name: string }[]>([]);
const [equipmentList, setEquipmentList] = useState<{ id: string; equipment_code: string; equipment_name: string }[]>([]);
// 선택/토글 상태
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
@@ -659,7 +659,7 @@ export default function ProductionPlanManagementPage() {
setModalManager((plan as any).manager_name || "");
setModalWorkOrderNo((plan as any).work_order_no || "");
setModalRemarks(plan.remarks || "");
setModalEquipmentId(plan.equipment_id ? String(plan.equipment_id) : "");
setModalEquipmentId((plan as any).equipment_code || (plan.equipment_id ? String(plan.equipment_id) : ""));
setScheduleModalOpen(true);
}, []);
@@ -1426,8 +1426,8 @@ export default function ProductionPlanManagementPage() {
<SelectContent>
<SelectItem value="none"></SelectItem>
{equipmentList.map((eq) => (
<SelectItem key={eq.equipment_id} value={String(eq.equipment_id)}>
{eq.equipment_name} ({eq.equipment_id})
<SelectItem key={eq.id} value={eq.equipment_code || eq.id}>
{eq.equipment_name} ({eq.equipment_code})
</SelectItem>
))}
</SelectContent>
@@ -345,7 +345,8 @@ export default function CustomerManagementPage() {
if (!code) return "";
return priceCategoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const today = new Date().toISOString().split("T")[0];
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
// 품목 기준 그룹핑 — master: 첫 매핑 + 현재 단가, details: 전체 단가 리스트
const grouped: Record<string, { master: any; details: any[] }> = {};
@@ -810,11 +811,13 @@ export default function CustomerManagementPage() {
const searchItems = async () => {
setItemSearchLoading(true);
try {
const filters: any[] = [];
const filters: any[] = [
{ columnName: "division", operator: "contains", value: "CAT_ML8ZFVEL_1TOR" },
];
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
page: 1, size: 500,
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
dataFilter: { enabled: true, filters },
autoFilter: true,
});
const allItems = res.data?.data?.data || res.data?.data?.rows || [];
@@ -402,32 +402,41 @@ export default function SalesItemPage() {
if (found) custInfo = found;
} catch { /* skip */ }
const mappingRows = [{
_id: `m_existing_${row.id}`,
customer_item_code: row.customer_item_code || "",
customer_item_name: row.customer_item_name || "",
}].filter((m) => m.customer_item_code || m.customer_item_name);
const priceRows = [{
_id: `p_existing_${row.id}`,
start_date: row.start_date || "",
end_date: row.end_date || "",
currency_code: row.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: row.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: row.base_price ? String(row.base_price) : "",
discount_type: row.discount_type || "",
discount_value: row.discount_value ? String(row.discount_value) : "",
rounding_type: row.rounding_type || "",
rounding_unit_value: row.rounding_unit_value || "",
calculated_price: row.calculated_price ? String(row.calculated_price) : "",
}].filter((p) => p.base_price || p.start_date);
if (priceRows.length === 0) {
priceRows.push({
_id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "",
let mappingRows: any[] = [];
try {
const mapRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allMappings = mapRes.data?.data?.data || mapRes.data?.data?.rows || [];
mappingRows = allMappings
.filter((m: any) => m.customer_item_code || m.customer_item_name)
.map((m: any) => ({ _id: `m_existing_${m.id}`, customer_item_code: m.customer_item_code || "", customer_item_name: m.customer_item_name || "" }));
} catch { /* skip */ }
let priceRows: any[] = [];
try {
const priceRes = await apiClient.post(`/table-management/tables/customer_item_prices/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allPriceData = priceRes.data?.data?.data || priceRes.data?.data?.rows || [];
priceRows = allPriceData.map((p: any) => ({
_id: `p_existing_${p.id}`, start_date: p.start_date ? String(p.start_date).split("T")[0] : "", end_date: p.end_date ? String(p.end_date).split("T")[0] : "",
currency_code: p.currency_code || "CAT_MLAMDKVN_PZJI", base_price_type: p.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: p.base_price ? String(p.base_price) : "", discount_type: p.discount_type || "", discount_value: p.discount_value ? String(p.discount_value) : "",
rounding_type: p.rounding_type || "", rounding_unit_value: p.rounding_unit_value || "", calculated_price: p.calculated_price ? String(p.calculated_price) : "",
}));
} catch { /* skip */ }
if (priceRows.length === 0) {
priceRows.push({ _id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "" });
}
setSelectedCustsForDetail([custInfo]);
@@ -782,23 +791,17 @@ export default function SalesItemPage() {
"cursor-pointer h-[41px]",
customerCheckedIds.includes(row.id) ? "bg-primary/[0.08]" : "hover:bg-accent"
)}
onClick={() => {
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
onDoubleClick={() => openEditCust(row)}
>
<TableCell
className="text-center px-2"
onClick={(e) => {
e.stopPropagation();
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
>
<TableCell className="text-center px-2">
<Checkbox
checked={customerCheckedIds.includes(row.id)}
onCheckedChange={(checked) => {
if (checked === true) setCustomerCheckedIds((prev) => [...prev, row.id]);
else setCustomerCheckedIds((prev) => prev.filter((id) => id !== row.id));
}}
onCheckedChange={() => {}}
/>
</TableCell>
<TableCell className="text-[13px] font-mono text-muted-foreground">{row.customer_code}</TableCell>
@@ -309,7 +309,8 @@ export default function DepartmentPage() {
};
// 퇴사일 기반 재직/퇴사 분리
const today = new Date().toISOString().split("T")[0];
const _now = new Date();
const today = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
const activeMembers = members.filter((m) => !m.end_date || m.end_date.substring(0, 10) >= today);
const resignedMembers = members.filter((m) => m.end_date && m.end_date.substring(0, 10) < today);
@@ -139,7 +139,7 @@ export default function ProductionPlanManagementPage() {
const [stockItems, setStockItems] = useState<StockShortageItem[]>([]);
const [finishedPlans, setFinishedPlans] = useState<ProductionPlan[]>([]);
const [semiPlans, setSemiPlans] = useState<ProductionPlan[]>([]);
const [equipmentList, setEquipmentList] = useState<{ equipment_id: string; equipment_name: string }[]>([]);
const [equipmentList, setEquipmentList] = useState<{ id: string; equipment_code: string; equipment_name: string }[]>([]);
// 선택/토글 상태
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
@@ -659,7 +659,7 @@ export default function ProductionPlanManagementPage() {
setModalManager((plan as any).manager_name || "");
setModalWorkOrderNo((plan as any).work_order_no || "");
setModalRemarks(plan.remarks || "");
setModalEquipmentId(plan.equipment_id ? String(plan.equipment_id) : "");
setModalEquipmentId((plan as any).equipment_code || (plan.equipment_id ? String(plan.equipment_id) : ""));
setScheduleModalOpen(true);
}, []);
@@ -1426,8 +1426,8 @@ export default function ProductionPlanManagementPage() {
<SelectContent>
<SelectItem value="none"></SelectItem>
{equipmentList.map((eq) => (
<SelectItem key={eq.equipment_id} value={String(eq.equipment_id)}>
{eq.equipment_name} ({eq.equipment_id})
<SelectItem key={eq.id} value={eq.equipment_code || eq.id}>
{eq.equipment_name} ({eq.equipment_code})
</SelectItem>
))}
</SelectContent>
@@ -345,7 +345,8 @@ export default function CustomerManagementPage() {
if (!code) return "";
return priceCategoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const today = new Date().toISOString().split("T")[0];
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
// 품목 기준 그룹핑 — master: 첫 매핑 + 현재 단가, details: 전체 단가 리스트
const grouped: Record<string, { master: any; details: any[] }> = {};
@@ -810,11 +811,13 @@ export default function CustomerManagementPage() {
const searchItems = async () => {
setItemSearchLoading(true);
try {
const filters: any[] = [];
const filters: any[] = [
{ columnName: "division", operator: "contains", value: "CAT_ML8ZFVEL_1TOR" },
];
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
page: 1, size: 500,
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
dataFilter: { enabled: true, filters },
autoFilter: true,
});
const allItems = res.data?.data?.data || res.data?.data?.rows || [];
@@ -402,25 +402,51 @@ export default function SalesItemPage() {
if (found) custInfo = found;
} catch { /* skip */ }
const mappingRows = [{
_id: `m_existing_${row.id}`,
customer_item_code: row.customer_item_code || "",
customer_item_name: row.customer_item_name || "",
}].filter((m) => m.customer_item_code || m.customer_item_name);
// 매핑 조회
let mappingRows: any[] = [];
try {
const mapRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allMappings = mapRes.data?.data?.data || mapRes.data?.data?.rows || [];
mappingRows = allMappings
.filter((m: any) => m.customer_item_code || m.customer_item_name)
.map((m: any) => ({
_id: `m_existing_${m.id}`,
customer_item_code: m.customer_item_code || "",
customer_item_name: m.customer_item_name || "",
}));
} catch { /* skip */ }
const priceRows = [{
_id: `p_existing_${row.id}`,
start_date: row.start_date || "",
end_date: row.end_date || "",
currency_code: row.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: row.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: row.base_price ? String(row.base_price) : "",
discount_type: row.discount_type || "",
discount_value: row.discount_value ? String(row.discount_value) : "",
rounding_type: row.rounding_type || "",
rounding_unit_value: row.rounding_unit_value || "",
calculated_price: row.calculated_price ? String(row.calculated_price) : "",
}].filter((p) => p.base_price || p.start_date);
// 단가 전체 조회
let priceRows: any[] = [];
try {
const priceRes = await apiClient.post(`/table-management/tables/customer_item_prices/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allPriceData = priceRes.data?.data?.data || priceRes.data?.data?.rows || [];
priceRows = allPriceData.map((p: any) => ({
_id: `p_existing_${p.id}`,
start_date: p.start_date ? String(p.start_date).split("T")[0] : "",
end_date: p.end_date ? String(p.end_date).split("T")[0] : "",
currency_code: p.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: p.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: p.base_price ? String(p.base_price) : "",
discount_type: p.discount_type || "",
discount_value: p.discount_value ? String(p.discount_value) : "",
rounding_type: p.rounding_type || "",
rounding_unit_value: p.rounding_unit_value || "",
calculated_price: p.calculated_price ? String(p.calculated_price) : "",
}));
} catch { /* skip */ }
if (priceRows.length === 0) {
priceRows.push({
@@ -782,23 +808,17 @@ export default function SalesItemPage() {
"cursor-pointer h-[41px]",
customerCheckedIds.includes(row.id) ? "bg-primary/[0.08]" : "hover:bg-accent"
)}
onClick={() => {
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
onDoubleClick={() => openEditCust(row)}
>
<TableCell
className="text-center px-2"
onClick={(e) => {
e.stopPropagation();
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
>
<TableCell className="text-center px-2">
<Checkbox
checked={customerCheckedIds.includes(row.id)}
onCheckedChange={(checked) => {
if (checked === true) setCustomerCheckedIds((prev) => [...prev, row.id]);
else setCustomerCheckedIds((prev) => prev.filter((id) => id !== row.id));
}}
onCheckedChange={() => {}}
/>
</TableCell>
<TableCell className="text-[13px] font-mono text-muted-foreground">{row.customer_code}</TableCell>
@@ -309,7 +309,8 @@ export default function DepartmentPage() {
};
// 퇴사일 기반 재직/퇴사 분리
const today = new Date().toISOString().split("T")[0];
const _now = new Date();
const today = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
const activeMembers = members.filter((m) => !m.end_date || m.end_date.substring(0, 10) >= today);
const resignedMembers = members.filter((m) => m.end_date && m.end_date.substring(0, 10) < today);
@@ -139,7 +139,7 @@ export default function ProductionPlanManagementPage() {
const [stockItems, setStockItems] = useState<StockShortageItem[]>([]);
const [finishedPlans, setFinishedPlans] = useState<ProductionPlan[]>([]);
const [semiPlans, setSemiPlans] = useState<ProductionPlan[]>([]);
const [equipmentList, setEquipmentList] = useState<{ equipment_id: string; equipment_name: string }[]>([]);
const [equipmentList, setEquipmentList] = useState<{ id: string; equipment_code: string; equipment_name: string }[]>([]);
// 선택/토글 상태
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
@@ -659,7 +659,7 @@ export default function ProductionPlanManagementPage() {
setModalManager((plan as any).manager_name || "");
setModalWorkOrderNo((plan as any).work_order_no || "");
setModalRemarks(plan.remarks || "");
setModalEquipmentId(plan.equipment_id ? String(plan.equipment_id) : "");
setModalEquipmentId((plan as any).equipment_code || (plan.equipment_id ? String(plan.equipment_id) : ""));
setScheduleModalOpen(true);
}, []);
@@ -1426,8 +1426,8 @@ export default function ProductionPlanManagementPage() {
<SelectContent>
<SelectItem value="none"></SelectItem>
{equipmentList.map((eq) => (
<SelectItem key={eq.equipment_id} value={String(eq.equipment_id)}>
{eq.equipment_name} ({eq.equipment_id})
<SelectItem key={eq.id} value={eq.equipment_code || eq.id}>
{eq.equipment_name} ({eq.equipment_code})
</SelectItem>
))}
</SelectContent>
@@ -345,7 +345,8 @@ export default function CustomerManagementPage() {
if (!code) return "";
return priceCategoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const today = new Date().toISOString().split("T")[0];
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
// 품목 기준 그룹핑 — master: 첫 매핑 + 현재 단가, details: 전체 단가 리스트
const grouped: Record<string, { master: any; details: any[] }> = {};
@@ -810,11 +811,13 @@ export default function CustomerManagementPage() {
const searchItems = async () => {
setItemSearchLoading(true);
try {
const filters: any[] = [];
const filters: any[] = [
{ columnName: "division", operator: "contains", value: "CAT_ML8ZFVEL_1TOR" },
];
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
page: 1, size: 500,
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
dataFilter: { enabled: true, filters },
autoFilter: true,
});
const allItems = res.data?.data?.data || res.data?.data?.rows || [];
@@ -402,32 +402,41 @@ export default function SalesItemPage() {
if (found) custInfo = found;
} catch { /* skip */ }
const mappingRows = [{
_id: `m_existing_${row.id}`,
customer_item_code: row.customer_item_code || "",
customer_item_name: row.customer_item_name || "",
}].filter((m) => m.customer_item_code || m.customer_item_name);
const priceRows = [{
_id: `p_existing_${row.id}`,
start_date: row.start_date || "",
end_date: row.end_date || "",
currency_code: row.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: row.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: row.base_price ? String(row.base_price) : "",
discount_type: row.discount_type || "",
discount_value: row.discount_value ? String(row.discount_value) : "",
rounding_type: row.rounding_type || "",
rounding_unit_value: row.rounding_unit_value || "",
calculated_price: row.calculated_price ? String(row.calculated_price) : "",
}].filter((p) => p.base_price || p.start_date);
if (priceRows.length === 0) {
priceRows.push({
_id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "",
let mappingRows: any[] = [];
try {
const mapRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allMappings = mapRes.data?.data?.data || mapRes.data?.data?.rows || [];
mappingRows = allMappings
.filter((m: any) => m.customer_item_code || m.customer_item_name)
.map((m: any) => ({ _id: `m_existing_${m.id}`, customer_item_code: m.customer_item_code || "", customer_item_name: m.customer_item_name || "" }));
} catch { /* skip */ }
let priceRows: any[] = [];
try {
const priceRes = await apiClient.post(`/table-management/tables/customer_item_prices/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allPriceData = priceRes.data?.data?.data || priceRes.data?.data?.rows || [];
priceRows = allPriceData.map((p: any) => ({
_id: `p_existing_${p.id}`, start_date: p.start_date ? String(p.start_date).split("T")[0] : "", end_date: p.end_date ? String(p.end_date).split("T")[0] : "",
currency_code: p.currency_code || "CAT_MLAMDKVN_PZJI", base_price_type: p.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: p.base_price ? String(p.base_price) : "", discount_type: p.discount_type || "", discount_value: p.discount_value ? String(p.discount_value) : "",
rounding_type: p.rounding_type || "", rounding_unit_value: p.rounding_unit_value || "", calculated_price: p.calculated_price ? String(p.calculated_price) : "",
}));
} catch { /* skip */ }
if (priceRows.length === 0) {
priceRows.push({ _id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "" });
}
setSelectedCustsForDetail([custInfo]);
@@ -782,23 +791,17 @@ export default function SalesItemPage() {
"cursor-pointer h-[41px]",
customerCheckedIds.includes(row.id) ? "bg-primary/[0.08]" : "hover:bg-accent"
)}
onClick={() => {
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
onDoubleClick={() => openEditCust(row)}
>
<TableCell
className="text-center px-2"
onClick={(e) => {
e.stopPropagation();
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
>
<TableCell className="text-center px-2">
<Checkbox
checked={customerCheckedIds.includes(row.id)}
onCheckedChange={(checked) => {
if (checked === true) setCustomerCheckedIds((prev) => [...prev, row.id]);
else setCustomerCheckedIds((prev) => prev.filter((id) => id !== row.id));
}}
onCheckedChange={() => {}}
/>
</TableCell>
<TableCell className="text-[13px] font-mono text-muted-foreground">{row.customer_code}</TableCell>
@@ -309,7 +309,8 @@ export default function DepartmentPage() {
};
// 퇴사일 기반 재직/퇴사 분리
const today = new Date().toISOString().split("T")[0];
const _now = new Date();
const today = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
const activeMembers = members.filter((m) => !m.end_date || m.end_date.substring(0, 10) >= today);
const resignedMembers = members.filter((m) => m.end_date && m.end_date.substring(0, 10) < today);
@@ -139,7 +139,7 @@ export default function ProductionPlanManagementPage() {
const [stockItems, setStockItems] = useState<StockShortageItem[]>([]);
const [finishedPlans, setFinishedPlans] = useState<ProductionPlan[]>([]);
const [semiPlans, setSemiPlans] = useState<ProductionPlan[]>([]);
const [equipmentList, setEquipmentList] = useState<{ equipment_id: string; equipment_name: string }[]>([]);
const [equipmentList, setEquipmentList] = useState<{ id: string; equipment_code: string; equipment_name: string }[]>([]);
// 선택/토글 상태
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
@@ -659,7 +659,7 @@ export default function ProductionPlanManagementPage() {
setModalManager((plan as any).manager_name || "");
setModalWorkOrderNo((plan as any).work_order_no || "");
setModalRemarks(plan.remarks || "");
setModalEquipmentId(plan.equipment_id ? String(plan.equipment_id) : "");
setModalEquipmentId((plan as any).equipment_code || (plan.equipment_id ? String(plan.equipment_id) : ""));
setScheduleModalOpen(true);
}, []);
@@ -1426,8 +1426,8 @@ export default function ProductionPlanManagementPage() {
<SelectContent>
<SelectItem value="none"></SelectItem>
{equipmentList.map((eq) => (
<SelectItem key={eq.equipment_id} value={String(eq.equipment_id)}>
{eq.equipment_name} ({eq.equipment_id})
<SelectItem key={eq.id} value={eq.equipment_code || eq.id}>
{eq.equipment_name} ({eq.equipment_code})
</SelectItem>
))}
</SelectContent>
@@ -345,7 +345,8 @@ export default function CustomerManagementPage() {
if (!code) return "";
return priceCategoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const today = new Date().toISOString().split("T")[0];
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
// 품목 기준 그룹핑 — master: 첫 매핑 + 현재 단가, details: 전체 단가 리스트
const grouped: Record<string, { master: any; details: any[] }> = {};
@@ -810,11 +811,13 @@ export default function CustomerManagementPage() {
const searchItems = async () => {
setItemSearchLoading(true);
try {
const filters: any[] = [];
const filters: any[] = [
{ columnName: "division", operator: "contains", value: "CAT_ML8ZFVEL_1TOR" },
];
if (itemSearchKeyword) filters.push({ columnName: "item_name", operator: "contains", value: itemSearchKeyword });
const res = await apiClient.post(`/table-management/tables/item_info/data`, {
page: 1, size: 500,
dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined,
dataFilter: { enabled: true, filters },
autoFilter: true,
});
const allItems = res.data?.data?.data || res.data?.data?.rows || [];
@@ -402,32 +402,41 @@ export default function SalesItemPage() {
if (found) custInfo = found;
} catch { /* skip */ }
const mappingRows = [{
_id: `m_existing_${row.id}`,
customer_item_code: row.customer_item_code || "",
customer_item_name: row.customer_item_name || "",
}].filter((m) => m.customer_item_code || m.customer_item_name);
const priceRows = [{
_id: `p_existing_${row.id}`,
start_date: row.start_date || "",
end_date: row.end_date || "",
currency_code: row.currency_code || "CAT_MLAMDKVN_PZJI",
base_price_type: row.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: row.base_price ? String(row.base_price) : "",
discount_type: row.discount_type || "",
discount_value: row.discount_value ? String(row.discount_value) : "",
rounding_type: row.rounding_type || "",
rounding_unit_value: row.rounding_unit_value || "",
calculated_price: row.calculated_price ? String(row.calculated_price) : "",
}].filter((p) => p.base_price || p.start_date);
if (priceRows.length === 0) {
priceRows.push({
_id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "",
let mappingRows: any[] = [];
try {
const mapRes = await apiClient.post(`/table-management/tables/customer_item_mapping/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allMappings = mapRes.data?.data?.data || mapRes.data?.data?.rows || [];
mappingRows = allMappings
.filter((m: any) => m.customer_item_code || m.customer_item_name)
.map((m: any) => ({ _id: `m_existing_${m.id}`, customer_item_code: m.customer_item_code || "", customer_item_name: m.customer_item_name || "" }));
} catch { /* skip */ }
let priceRows: any[] = [];
try {
const priceRes = await apiClient.post(`/table-management/tables/customer_item_prices/data`, {
page: 1, size: 100,
dataFilter: { enabled: true, filters: [
{ columnName: "customer_id", operator: "equals", value: custKey },
{ columnName: "item_id", operator: "equals", value: selectedItem!.item_number },
]}, autoFilter: true,
});
const allPriceData = priceRes.data?.data?.data || priceRes.data?.data?.rows || [];
priceRows = allPriceData.map((p: any) => ({
_id: `p_existing_${p.id}`, start_date: p.start_date ? String(p.start_date).split("T")[0] : "", end_date: p.end_date ? String(p.end_date).split("T")[0] : "",
currency_code: p.currency_code || "CAT_MLAMDKVN_PZJI", base_price_type: p.base_price_type || "CAT_MLAMFGFT_4RZW",
base_price: p.base_price ? String(p.base_price) : "", discount_type: p.discount_type || "", discount_value: p.discount_value ? String(p.discount_value) : "",
rounding_type: p.rounding_type || "", rounding_unit_value: p.rounding_unit_value || "", calculated_price: p.calculated_price ? String(p.calculated_price) : "",
}));
} catch { /* skip */ }
if (priceRows.length === 0) {
priceRows.push({ _id: `p_${Date.now()}`, start_date: "", end_date: "", currency_code: "CAT_MLAMDKVN_PZJI",
base_price_type: "CAT_MLAMFGFT_4RZW", base_price: "", discount_type: "", discount_value: "",
rounding_type: "", rounding_unit_value: "", calculated_price: "" });
}
setSelectedCustsForDetail([custInfo]);
@@ -782,23 +791,17 @@ export default function SalesItemPage() {
"cursor-pointer h-[41px]",
customerCheckedIds.includes(row.id) ? "bg-primary/[0.08]" : "hover:bg-accent"
)}
onClick={() => {
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
onDoubleClick={() => openEditCust(row)}
>
<TableCell
className="text-center px-2"
onClick={(e) => {
e.stopPropagation();
setCustomerCheckedIds((prev) =>
prev.includes(row.id) ? prev.filter((id) => id !== row.id) : [...prev, row.id]
);
}}
>
<TableCell className="text-center px-2">
<Checkbox
checked={customerCheckedIds.includes(row.id)}
onCheckedChange={(checked) => {
if (checked === true) setCustomerCheckedIds((prev) => [...prev, row.id]);
else setCustomerCheckedIds((prev) => prev.filter((id) => id !== row.id));
}}
onCheckedChange={() => {}}
/>
</TableCell>
<TableCell className="text-[13px] font-mono text-muted-foreground">{row.customer_code}</TableCell>