diff --git a/static/app.js b/static/app.js index 9d2c492..154d8cf 100644 --- a/static/app.js +++ b/static/app.js @@ -1467,6 +1467,366 @@ $(document).ready(function() { applyLedgerFilters(); }); + // ==================== 재고 보정 ==================== + + // 재고 보정 모달 열기 + $('#showStockAdjustmentBtn').on('click', function() { + // 현재 날짜 설정 + $('#adjustmentDate').val(new Date().toISOString().split('T')[0]); + $('#adjustmentItemsList').empty(); + $('#stockAdjustmentForm')[0].reset(); + $('#stockAdjustmentModal').modal('show'); + }); + + // 재고 보정 내역 모달 열기 + $('#showAdjustmentHistoryBtn').on('click', function() { + loadAdjustmentHistory(); + }); + + // 재고 보정 내역 로드 + function loadAdjustmentHistory() { + $.get('/api/stock-adjustments', function(response) { + if (response.success) { + const tbody = $('#adjustmentHistoryList'); + tbody.empty(); + + if (response.data.length === 0) { + tbody.append(` + + 보정 내역이 없습니다. + + `); + } else { + response.data.forEach(adj => { + // 보정 유형 한글 변환 + let typeLabel = ''; + switch(adj.adjustment_type) { + case 'LOSS': typeLabel = '감모/손실'; break; + case 'FOUND': typeLabel = '발견'; break; + case 'RECOUNT': typeLabel = '재고조사'; break; + case 'DAMAGE': typeLabel = '파손'; break; + case 'EXPIRE': typeLabel = '유통기한 경과'; break; + default: typeLabel = adj.adjustment_type; + } + + tbody.append(` + + ${adj.adjustment_date} + ${adj.adjustment_no} + ${typeLabel} + ${adj.detail_count || 0}개 + ${adj.created_by || '-'} + ${adj.notes || '-'} + + + + + `); + }); + + // 상세보기 버튼 이벤트 + $('.view-adjustment-detail').on('click', function() { + const adjustmentId = $(this).data('id'); + viewAdjustmentDetail(adjustmentId); + }); + } + + $('#adjustmentHistoryModal').modal('show'); + } + }).fail(function() { + alert('보정 내역을 불러오는데 실패했습니다.'); + }); + } + + // 재고 보정 상세 조회 + function viewAdjustmentDetail(adjustmentId) { + $.get(`/api/stock-adjustments/${adjustmentId}`, function(response) { + if (response.success) { + const data = response.data; + + // 보정 정보 표시 + $('#detailAdjustmentNo').text(data.adjustment_no); + $('#detailAdjustmentDate').text(data.adjustment_date); + + // 보정 유형 한글 변환 + let typeLabel = ''; + switch(data.adjustment_type) { + case 'LOSS': typeLabel = '감모/손실'; break; + case 'FOUND': typeLabel = '발견'; break; + case 'RECOUNT': typeLabel = '재고조사'; break; + case 'DAMAGE': typeLabel = '파손'; break; + case 'EXPIRE': typeLabel = '유통기한 경과'; break; + default: typeLabel = data.adjustment_type; + } + $('#detailAdjustmentType').html(`${typeLabel}`); + $('#detailAdjustmentCreatedBy').text(data.created_by || '-'); + $('#detailAdjustmentNotes').text(data.notes || '-'); + + // 보정 상세 항목 표시 + const itemsBody = $('#detailAdjustmentItems'); + itemsBody.empty(); + + if (data.details && data.details.length > 0) { + data.details.forEach(item => { + const delta = item.quantity_delta; + let deltaHtml = ''; + if (delta > 0) { + deltaHtml = `+${delta.toFixed(1)}g`; + } else if (delta < 0) { + deltaHtml = `${delta.toFixed(1)}g`; + } else { + deltaHtml = '0g'; + } + + itemsBody.append(` + + ${item.herb_name} + ${item.insurance_code || '-'} + ${item.origin_country || '-'} + #${item.lot_id} + ${item.quantity_before.toFixed(1)}g + ${item.quantity_after.toFixed(1)}g + ${deltaHtml} + ${item.reason || '-'} + + `); + }); + } + + // 보정 상세 모달 표시 + $('#adjustmentDetailModal').modal('show'); + } + }).fail(function() { + alert('보정 상세 정보를 불러오는데 실패했습니다.'); + }); + } + + // 보정 대상 약재 추가 + let adjustmentItemCount = 0; + $('#addAdjustmentItemBtn').on('click', function() { + addAdjustmentItemRow(); + }); + + function addAdjustmentItemRow() { + adjustmentItemCount++; + const rowId = `adj-item-${adjustmentItemCount}`; + + const newRow = $(` + + + + + + + + - + + + + - + + + + + + + + `); + + $('#adjustmentItemsList').append(newRow); + + // 약재 목록 로드 + loadHerbsForSelect(newRow.find('.adj-herb-select')); + + // 약재 선택 이벤트 + newRow.find('.adj-herb-select').on('change', function() { + const herbId = $(this).val(); + const row = $(this).closest('tr'); + + if (herbId) { + loadLotsForAdjustment(herbId, row); + } else { + row.find('.adj-lot-select').empty().append('').prop('disabled', true); + row.find('.before-qty').text('-'); + row.find('.after-qty-input').val(''); + row.find('.delta-qty').text('-'); + } + }); + + // 로트 선택 이벤트 + newRow.find('.adj-lot-select').on('change', function() { + const selectedOption = $(this).find('option:selected'); + const row = $(this).closest('tr'); + + if (selectedOption.val()) { + const beforeQty = parseFloat(selectedOption.data('qty')) || 0; + row.find('.before-qty').text(beforeQty.toFixed(1) + 'g'); + row.data('before-qty', beforeQty); + + // 기존 변경후 값이 있으면 델타 재계산 + const afterQty = parseFloat(row.find('.after-qty-input').val()); + if (!isNaN(afterQty)) { + updateDelta(row, beforeQty, afterQty); + } + } else { + row.find('.before-qty').text('-'); + row.find('.after-qty-input').val(''); + row.find('.delta-qty').text('-'); + } + }); + + // 변경후 수량 입력 이벤트 + newRow.find('.after-qty-input').on('input', function() { + const row = $(this).closest('tr'); + const beforeQty = row.data('before-qty') || 0; + const afterQty = parseFloat($(this).val()) || 0; + + updateDelta(row, beforeQty, afterQty); + }); + + // 삭제 버튼 + newRow.find('.remove-adj-item').on('click', function() { + $(this).closest('tr').remove(); + }); + } + + // 약재별 로트 목록 로드 + function loadLotsForAdjustment(herbId, row) { + $.get(`/api/inventory/detail/${herbId}`, function(response) { + if (response.success) { + const lotSelect = row.find('.adj-lot-select'); + lotSelect.empty(); + lotSelect.append(''); + + const data = response.data; + + // 원산지별로 로트 표시 + data.origins.forEach(origin => { + const optgroup = $(``); + + origin.lots.forEach(lot => { + optgroup.append(` + + `); + }); + + lotSelect.append(optgroup); + }); + + lotSelect.prop('disabled', false); + } + }).fail(function() { + alert('재고 정보를 불러오는데 실패했습니다.'); + }); + } + + // 델타 계산 및 표시 + function updateDelta(row, beforeQty, afterQty) { + const delta = afterQty - beforeQty; + const deltaElement = row.find('.delta-qty'); + + if (delta > 0) { + deltaElement.html(`+${delta.toFixed(1)}g`); + } else if (delta < 0) { + deltaElement.html(`${delta.toFixed(1)}g`); + } else { + deltaElement.html('0g'); + } + + row.data('delta', delta); + } + + // 재고 보정 저장 버튼 + $('#saveAdjustmentBtn').on('click', function() { + saveStockAdjustment(); + }); + + // 재고 보정 저장 + $('#stockAdjustmentForm').on('submit', function(e) { + e.preventDefault(); + saveStockAdjustment(); + }); + + function saveStockAdjustment() { + const items = []; + let hasError = false; + + $('#adjustmentItemsList tr').each(function() { + const herbId = $(this).find('.adj-herb-select').val(); + const lotId = $(this).find('.adj-lot-select').val(); + const beforeQty = $(this).data('before-qty'); + const afterQty = parseFloat($(this).find('.after-qty-input').val()); + const delta = $(this).data('delta'); + const reason = $(this).find('.reason-input').val(); + + if (!herbId || !lotId) { + hasError = true; + return false; + } + + items.push({ + herb_item_id: parseInt(herbId), + lot_id: parseInt(lotId), + quantity_before: beforeQty, + quantity_after: afterQty, + quantity_delta: delta, + reason: reason + }); + }); + + if (hasError) { + alert('모든 항목의 약재와 로트를 선택해주세요.'); + return; + } + + if (items.length === 0) { + alert('보정할 항목을 추가해주세요.'); + return; + } + + const adjustmentData = { + adjustment_date: $('#adjustmentDate').val(), + adjustment_type: $('#adjustmentType').val(), + created_by: $('#adjustmentCreatedBy').val() || 'SYSTEM', + notes: $('#adjustmentNotes').val(), + details: items // API expects 'details', not 'items' + }; + + $.ajax({ + url: '/api/stock-adjustments', + method: 'POST', + contentType: 'application/json', + data: JSON.stringify(adjustmentData), + success: function(response) { + if (response.success) { + alert(`재고 보정이 완료되었습니다.\n보정번호: ${response.adjustment_no}\n항목 수: ${items.length}개`); + $('#stockAdjustmentModal').modal('hide'); + + // 재고 목록 새로고침 + loadInventory(); + } + }, + error: function(xhr) { + alert('오류: ' + (xhr.responseJSON?.error || '재고 보정 실패')); + } + }); + } + function formatCurrency(amount) { if (amount === null || amount === undefined) return '0원'; return new Intl.NumberFormat('ko-KR', { diff --git a/templates/index.html b/templates/index.html index 8afb8b3..092b2cb 100644 --- a/templates/index.html +++ b/templates/index.html @@ -589,6 +589,9 @@ + @@ -698,6 +701,200 @@ + + + + + + + + +