feat: Allow duplicate items at the same level in BOM management
- Updated the logic in the BomManagementPage to permit the registration of duplicate items at the same level, enabling separate rows for items with different requirements or processes. - Removed the previous check for duplicate items at the same level, enhancing flexibility in item management within the BOM structure.
This commit is contained in:
@@ -834,14 +834,7 @@ export default function BomManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 레벨(같은 부모) 중복 품목 체크
|
||||
const siblings = addTargetParentId
|
||||
? (findNodeById(editingTree, addTargetParentId)?.children || [])
|
||||
: editingTree;
|
||||
if (siblings.some((n) => n.child_item_id === item.id)) {
|
||||
toast.error("같은 레벨에 이미 동일 품목이 존재합니다");
|
||||
return;
|
||||
}
|
||||
// 같은 레벨 중복 허용 — 소요량/공정 등이 다른 동일 품목을 별도 row로 등록할 수 있음
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
const parentNode = addTargetParentId ? findNodeById(editingTree, addTargetParentId) : null;
|
||||
|
||||
@@ -834,14 +834,7 @@ export default function BomManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 레벨(같은 부모) 중복 품목 체크
|
||||
const siblings = addTargetParentId
|
||||
? (findNodeById(editingTree, addTargetParentId)?.children || [])
|
||||
: editingTree;
|
||||
if (siblings.some((n) => n.child_item_id === item.id)) {
|
||||
toast.error("같은 레벨에 이미 동일 품목이 존재합니다");
|
||||
return;
|
||||
}
|
||||
// 같은 레벨 중복 허용 — 소요량/공정 등이 다른 동일 품목을 별도 row로 등록할 수 있음
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
const parentNode = addTargetParentId ? findNodeById(editingTree, addTargetParentId) : null;
|
||||
|
||||
@@ -834,14 +834,7 @@ export default function BomManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 레벨(같은 부모) 중복 품목 체크
|
||||
const siblings = addTargetParentId
|
||||
? (findNodeById(editingTree, addTargetParentId)?.children || [])
|
||||
: editingTree;
|
||||
if (siblings.some((n) => n.child_item_id === item.id)) {
|
||||
toast.error("같은 레벨에 이미 동일 품목이 존재합니다");
|
||||
return;
|
||||
}
|
||||
// 같은 레벨 중복 허용 — 소요량/공정 등이 다른 동일 품목을 별도 row로 등록할 수 있음
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
const parentNode = addTargetParentId ? findNodeById(editingTree, addTargetParentId) : null;
|
||||
|
||||
@@ -841,14 +841,7 @@ export default function BomManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 레벨(같은 부모) 중복 품목 체크
|
||||
const siblings = addTargetParentId
|
||||
? (findNodeById(editingTree, addTargetParentId)?.children || [])
|
||||
: editingTree;
|
||||
if (siblings.some((n) => n.child_item_id === item.id)) {
|
||||
toast.error("같은 레벨에 이미 동일 품목이 존재합니다");
|
||||
return;
|
||||
}
|
||||
// 같은 레벨 중복 허용 — 소요량/공정 등이 다른 동일 품목을 별도 row로 등록할 수 있음
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
const parentNode = addTargetParentId ? findNodeById(editingTree, addTargetParentId) : null;
|
||||
|
||||
@@ -834,14 +834,7 @@ export default function BomManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 레벨(같은 부모) 중복 품목 체크
|
||||
const siblings = addTargetParentId
|
||||
? (findNodeById(editingTree, addTargetParentId)?.children || [])
|
||||
: editingTree;
|
||||
if (siblings.some((n) => n.child_item_id === item.id)) {
|
||||
toast.error("같은 레벨에 이미 동일 품목이 존재합니다");
|
||||
return;
|
||||
}
|
||||
// 같은 레벨 중복 허용 — 소요량/공정 등이 다른 동일 품목을 별도 row로 등록할 수 있음
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
const parentNode = addTargetParentId ? findNodeById(editingTree, addTargetParentId) : null;
|
||||
|
||||
@@ -834,14 +834,7 @@ export default function BomManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 레벨(같은 부모) 중복 품목 체크
|
||||
const siblings = addTargetParentId
|
||||
? (findNodeById(editingTree, addTargetParentId)?.children || [])
|
||||
: editingTree;
|
||||
if (siblings.some((n) => n.child_item_id === item.id)) {
|
||||
toast.error("같은 레벨에 이미 동일 품목이 존재합니다");
|
||||
return;
|
||||
}
|
||||
// 같은 레벨 중복 허용 — 소요량/공정 등이 다른 동일 품목을 별도 row로 등록할 수 있음
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
const parentNode = addTargetParentId ? findNodeById(editingTree, addTargetParentId) : null;
|
||||
|
||||
@@ -841,14 +841,7 @@ export default function BomManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 레벨(같은 부모) 중복 품목 체크
|
||||
const siblings = addTargetParentId
|
||||
? (findNodeById(editingTree, addTargetParentId)?.children || [])
|
||||
: editingTree;
|
||||
if (siblings.some((n) => n.child_item_id === item.id)) {
|
||||
toast.error("같은 레벨에 이미 동일 품목이 존재합니다");
|
||||
return;
|
||||
}
|
||||
// 같은 레벨 중복 허용 — 소요량/공정 등이 다른 동일 품목을 별도 row로 등록할 수 있음
|
||||
|
||||
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
const parentNode = addTargetParentId ? findNodeById(editingTree, addTargetParentId) : null;
|
||||
|
||||
@@ -131,13 +131,33 @@ export function DetailFormModal({
|
||||
}, [selectedItemCode]);
|
||||
|
||||
// BOM 자재 로드 완료 후 체크 상태 초기화
|
||||
// bomChecked 키는 BOM detail 고유 id(mat.id) 기준 — 동일 child_item_id가 여러 row로
|
||||
// 있어도 독립적으로 체크되도록 함. legacy 저장값은 child_item_id로 저장되어 있을 수
|
||||
// 있으므로 폴백 매핑으로 복원.
|
||||
useEffect(() => {
|
||||
if (!open || bomMaterials.length === 0) return;
|
||||
if (mode === "edit" && editData?.selected_bom_items) {
|
||||
const savedBom = editData.selected_bom_items;
|
||||
const parsedBom = typeof savedBom === "string" ? JSON.parse(savedBom) : savedBom;
|
||||
if (Array.isArray(parsedBom)) {
|
||||
setBomChecked(new Set(parsedBom));
|
||||
const matIds = new Set(bomMaterials.map((m) => m.id));
|
||||
const restored = new Set<string>();
|
||||
const seenChildForLegacy = new Set<string>();
|
||||
for (const saved of parsedBom) {
|
||||
if (matIds.has(saved)) {
|
||||
restored.add(saved); // 신규 포맷: bom_detail id 저장값
|
||||
} else {
|
||||
// legacy 폴백: child_item_id로 저장된 값 → 해당 품목의 첫 번째 행 1건 자동 체크
|
||||
const legacyKey = String(saved);
|
||||
if (seenChildForLegacy.has(legacyKey)) continue;
|
||||
const firstMat = bomMaterials.find((m) => m.child_item_id === legacyKey);
|
||||
if (firstMat) {
|
||||
restored.add(firstMat.id);
|
||||
seenChildForLegacy.add(legacyKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
setBomChecked(restored);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -310,8 +330,9 @@ export function DetailFormModal({
|
||||
submitData.content = submitData.content || "작업수량 / 불량수량 / 양품수량";
|
||||
}
|
||||
if (type === "material_input") {
|
||||
// 선택된 BOM 자재를 각각 개별 상세 항목으로 등록
|
||||
const checkedMats = bomMaterials.filter(m => bomChecked.has(m.child_item_id));
|
||||
// 선택된 BOM 자재를 각각 개별 상세 항목으로 등록 — 동일 품목 중복 row를 독립
|
||||
// 처리하기 위해 bom_detail 고유 id(m.id) 기준으로 매칭
|
||||
const checkedMats = bomMaterials.filter(m => bomChecked.has(m.id));
|
||||
if (checkedMats.length > 0) {
|
||||
const resolveType = (code: string) => itemTypeCatMap[code] || code || "";
|
||||
for (const mat of checkedMats) {
|
||||
@@ -955,7 +976,7 @@ export function DetailFormModal({
|
||||
}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
setBomChecked(new Set(bomMaterials.map((m) => m.child_item_id)));
|
||||
setBomChecked(new Set(bomMaterials.map((m) => m.id)));
|
||||
} else {
|
||||
setBomChecked(new Set());
|
||||
}
|
||||
@@ -994,12 +1015,12 @@ export function DetailFormModal({
|
||||
className="flex items-center gap-2.5 border-b px-3 py-2.5 last:border-b-0 hover:bg-sky-50/50"
|
||||
>
|
||||
<Checkbox
|
||||
checked={bomChecked.has(mat.child_item_id)}
|
||||
checked={bomChecked.has(mat.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
setBomChecked((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (checked) next.add(mat.child_item_id);
|
||||
else next.delete(mat.child_item_id);
|
||||
if (checked) next.add(mat.id);
|
||||
else next.delete(mat.id);
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user