// ChemiFactory MES - Inventory and Material Property Management Logic const API_INVENTORY = "/inventory"; const API_SUPPLIERS = "/suppliers"; let allInventories = []; let allMaterials = []; let allSuppliers = []; document.addEventListener("DOMContentLoaded", () => { initEvents(); fetchInventory(); fetchMaterials(); fetchSuppliers(); }); // Fetch API Data async function fetchInventory() { try { const res = await fetch(`${API_INVENTORY}/`); if (!res.ok) throw new Error("Failed to fetch inventory"); allInventories = await res.json(); renderInventoryList(allInventories); } catch (err) { console.error(err); document.getElementById("inventory-list-body").innerHTML = `재고 데이터를 불러오지 못했습니다.`; } } async function fetchMaterials() { try { const res = await fetch(`${API_INVENTORY}/materials`); if (!res.ok) throw new Error("Failed to fetch materials"); allMaterials = await res.json(); renderMaterialList(allMaterials); bindMaterialDropdown(allMaterials); } catch (err) { console.error(err); document.getElementById("material-list-body").innerHTML = `자재 데이터를 불러오지 못했습니다.`; } } async function fetchSuppliers() { try { const res = await fetch(`${API_SUPPLIERS}/`); if (!res.ok) throw new Error("Failed to fetch suppliers"); allSuppliers = await res.json(); bindSupplierDropdown(allSuppliers); } catch (err) { console.error(err); } } // Renderers function renderInventoryList(list) { const tbody = document.getElementById("inventory-list-body"); tbody.innerHTML = ""; if (list.length === 0) { tbody.innerHTML = `재고 입고 원장이 비어있습니다.`; return; } list.forEach(inv => { const tr = document.createElement("tr"); const statusBadge = inv.is_depleted ? `소진 완료` : `재고 있음`; tr.innerHTML = ` ${inv.receipt_date} ${inv.item_name || "-"}
${inv.item_number || "-"} ${inv.material_name || "-"} (${inv.material_code || "-"}) ${inv.receipt_quantity.toLocaleString()} ${(inv.usage_quantity || 0).toLocaleString()} ${(inv.stock || 0).toLocaleString()} ${inv.unit_cost.toLocaleString()} 원 ${inv.supplier || "-"} ${statusBadge} `; tbody.appendChild(tr); }); } function renderMaterialList(list) { const tbody = document.getElementById("material-list-body"); tbody.innerHTML = ""; if (list.length === 0) { tbody.innerHTML = `등록된 자재 기본 물성이 없습니다.`; return; } list.forEach(mat => { const tr = document.createElement("tr"); tr.innerHTML = ` ${mat.material_code} ${mat.material_name} ${mat.material_type === 'Yarn' ? '원사 (Yarn)' : mat.material_type === 'Bond' ? '본드 (Bond)' : '기타'} ${mat.density} g/cm³ ${mat.remarks || "-"} `; tbody.appendChild(tr); }); } function bindMaterialDropdown(materials) { const select = document.getElementById("inv-material"); select.innerHTML = ''; materials.forEach(m => { select.innerHTML += ``; }); } function bindSupplierDropdown(suppliers) { const select = document.getElementById("inv-supplier"); select.innerHTML = ''; suppliers.forEach(s => { select.innerHTML += ``; }); } // Events function initEvents() { // Tab switching document.querySelectorAll(".tab-btn").forEach(btn => { btn.addEventListener("click", () => { document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active")); document.querySelectorAll(".tab-content").forEach(tc => tc.classList.remove("active")); btn.classList.add("active"); document.getElementById(btn.dataset.tab).classList.add("active"); }); }); // Modal windows cancel const modalIds = ["material-modal", "inventory-modal"]; modalIds.forEach(id => { const modal = document.getElementById(id); modal.querySelectorAll(".close-btn, .close-modal-btn").forEach(btn => { btn.addEventListener("click", () => modal.style.display = "none"); }); }); // Material logic trigger document.getElementById("btn-add-material").addEventListener("click", () => { document.getElementById("material-form").reset(); document.getElementById("material-id").value = ""; document.getElementById("material-modal-title").innerText = "자재 물성 등록"; document.getElementById("material-modal").style.display = "flex"; }); document.getElementById("material-form").addEventListener("submit", handleMaterialSubmit); // Inventory logic trigger document.getElementById("btn-add-inventory").addEventListener("click", () => { document.getElementById("inventory-form").reset(); document.getElementById("inventory-id").value = ""; document.getElementById("usage-group").style.display = "none"; document.getElementById("inv-date").value = new Date().toISOString().substring(0, 10); document.getElementById("inventory-modal-title").innerText = "자재 신규 입고 등록"; document.getElementById("inventory-modal").style.display = "flex"; }); document.getElementById("inventory-form").addEventListener("submit", handleInventorySubmit); // Search filters document.getElementById("search-inventory").addEventListener("input", (e) => { const query = e.target.value.toLowerCase(); const filtered = allInventories.filter(i => (i.item_name && i.item_name.toLowerCase().includes(query)) || (i.item_number && i.item_number.toLowerCase().includes(query)) || (i.material_name && i.material_name.toLowerCase().includes(query)) || (i.material_code && i.material_code.toLowerCase().includes(query)) ); renderInventoryList(filtered); }); document.getElementById("search-material").addEventListener("input", (e) => { const query = e.target.value.toLowerCase(); const filtered = allMaterials.filter(m => m.material_name.toLowerCase().includes(query) || m.material_code.toLowerCase().includes(query) ); renderMaterialList(filtered); }); } // Submits Handlers async function handleMaterialSubmit(e) { e.preventDefault(); const id = document.getElementById("material-id").value; const payload = { material_code: document.getElementById("mat-code").value, material_name: document.getElementById("mat-name").value, material_type: document.getElementById("mat-type").value, density: parseFloat(document.getElementById("mat-density").value), remarks: document.getElementById("mat-remarks").value || null }; try { let res; if (id) { res = await fetch(`${API_INVENTORY}/materials/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); } else { res = await fetch(`${API_INVENTORY}/materials`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); } if (!res.ok) throw new Error("API failed"); document.getElementById("material-modal").style.display = "none"; fetchMaterials(); } catch (err) { console.error(err); alert("자재 규격 저장에 실패했습니다."); } } window.openEditMaterialModal = function(mat) { document.getElementById("material-id").value = mat.id; document.getElementById("mat-code").value = mat.material_code; document.getElementById("mat-name").value = mat.material_name; document.getElementById("mat-type").value = mat.material_type; document.getElementById("mat-density").value = mat.density; document.getElementById("mat-remarks").value = mat.remarks || ""; document.getElementById("material-modal-title").innerText = "자재 물성 수정"; document.getElementById("material-modal").style.display = "flex"; }; window.deleteMaterial = async function(id) { if (!confirm("이 자재 물성 규격을 정말 삭제하시겠습니까?\n이미 입고된 재고 이력이 존재할 경우 삭제가 거부될 수 있습니다.")) return; try { const res = await fetch(`${API_INVENTORY}/materials/${id}`, { method: "DELETE" }); if (!res.ok) { const data = await res.json(); throw new Error(data.detail || "Check references"); } fetchMaterials(); } catch (err) { console.error(err); alert(`자재 삭제 실패: ${err.message}`); } }; // Inventory Submits Handlers async function handleInventorySubmit(e) { e.preventDefault(); const id = document.getElementById("inventory-id").value; const payload = { material_id: parseInt(document.getElementById("inv-material").value), supplier_id: parseInt(document.getElementById("inv-supplier").value), receipt_date: document.getElementById("inv-date").value, item_name: document.getElementById("inv-item-name").value || null, item_number: document.getElementById("inv-item-num").value || null, receipt_quantity: parseFloat(document.getElementById("inv-qty").value), usage_quantity: id ? parseFloat(document.getElementById("inv-usage").value || 0) : 0.0, unit_cost: parseFloat(document.getElementById("inv-cost").value), remarks: document.getElementById("inv-remarks").value || null }; try { let res; if (id) { res = await fetch(`${API_INVENTORY}/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); } else { res = await fetch(`${API_INVENTORY}/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); } if (!res.ok) throw new Error("API failed"); document.getElementById("inventory-modal").style.display = "none"; fetchInventory(); } catch (err) { console.error(err); alert("재고 정보를 저장하는 데 실패했습니다."); } } window.openEditInventoryModal = function(inv) { document.getElementById("inventory-id").value = inv.id; document.getElementById("inv-material").value = inv.material_id; document.getElementById("inv-supplier").value = inv.supplier_id; document.getElementById("inv-date").value = inv.receipt_date; document.getElementById("inv-item-name").value = inv.item_name || ""; document.getElementById("inv-item-num").value = inv.item_number || ""; document.getElementById("inv-qty").value = inv.receipt_quantity; document.getElementById("inv-cost").value = inv.unit_cost; document.getElementById("inv-remarks").value = inv.remarks || ""; // Show usage input when modifying document.getElementById("usage-group").style.display = "block"; document.getElementById("inv-usage").value = inv.usage_quantity || 0; document.getElementById("inventory-modal-title").innerText = "재고 정보 수정"; document.getElementById("inventory-modal").style.display = "flex"; }; window.depleteInventory = async function(id) { if (!confirm("이 입고 건의 잔량 재고를 모두 소진 처리하시겠습니까?")) return; try { const res = await fetch(`${API_INVENTORY}/${id}/deplete`, { method: "PUT" }); if (!res.ok) throw new Error("Failed to deplete"); fetchInventory(); } catch (err) { console.error(err); alert("재고 소진 처리에 실패했습니다."); } };