fix: 조제 관리 약재 추가 시 마스터 약재명 표시 및 2단계 선택 구조 개선

- 약재 추가 드롭다운에서 제품명 대신 마스터 약재명 표시
- /api/herbs/masters 엔드포인트 사용하여 ingredient_code 기반 약재 목록 로드
- /api/herbs/by-ingredient/<code> 엔드포인트 추가 (제품 목록 조회)
- 2단계 선택 구조: 약재(마스터) → 제품 → 원산지/롯트
- 기존 처방 약재와 새로 추가하는 약재의 테이블 구조 통일 (6칼럼)
- 원산지 선택 칼럼에 제품/원산지 드롭다운 함께 표시

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-02-15 18:31:15 +00:00
parent bfc5c992de
commit a4861dc1b8
4 changed files with 253 additions and 67 deletions

View File

@@ -617,15 +617,15 @@ $(document).ready(function() {
value="${ing.grams_per_cheop}" min="0.1" step="0.1">
</td>
<td class="total-grams">${totalGrams.toFixed(1)}</td>
<td class="product-select-cell">
<select class="form-control form-control-sm product-select" ${ing.available_products.length === 0 ? 'disabled' : ''}>
${productOptions}
</select>
</td>
<td class="origin-select-cell">
<select class="form-control form-control-sm origin-select" disabled>
<option value="">제품 먼저 선택</option>
</select>
<div class="d-flex gap-1">
<select class="form-control form-control-sm product-select" style="flex: 1;" ${ing.available_products.length === 0 ? 'disabled' : ''}>
${productOptions}
</select>
<select class="form-control form-control-sm origin-select" style="flex: 1;" disabled>
<option value="">제품 먼저 선택</option>
</select>
</div>
</td>
<td class="stock-status">대기중</td>
<td>
@@ -722,7 +722,7 @@ $(document).ready(function() {
// 빈 약재 행 추가 함수
function addEmptyIngredientRow() {
const newRow = $(`
<tr>
<tr data-ingredient-code="" data-herb-id="">
<td>
<select class="form-control form-control-sm herb-select-compound">
<option value="">약재 선택</option>
@@ -734,9 +734,14 @@ $(document).ready(function() {
</td>
<td class="total-grams">0.0</td>
<td class="origin-select-cell">
<select class="form-control form-control-sm origin-select" disabled>
<option value="">약재 선택 후 표시</option>
</select>
<div class="d-flex gap-1">
<select class="form-control form-control-sm product-select" style="flex: 1;" disabled>
<option value="">약재 선택 후 표시</option>
</select>
<select class="form-control form-control-sm origin-select" style="flex: 1;" disabled>
<option value="">제품 선택 후 표시</option>
</select>
</div>
</td>
<td class="stock-status">-</td>
<td>
@@ -750,18 +755,53 @@ $(document).ready(function() {
$('#compoundIngredients').append(newRow);
// 약재 목록 로드
loadHerbsForSelect(newRow.find('.herb-select-compound'));
const herbSelect = newRow.find('.herb-select-compound');
loadHerbsForSelect(herbSelect);
// 약재 선택 시 원산지 옵션 로드
// 약재(마스터) 선택 시 제품 옵션 로드
newRow.find('.herb-select-compound').on('change', function() {
const herbId = $(this).val();
if (herbId) {
const ingredientCode = $(this).val();
const herbName = $(this).find('option:selected').data('herb-name');
if (ingredientCode) {
const row = $(this).closest('tr');
row.attr('data-ingredient-code', ingredientCode);
// 제품 목록 로드
loadProductOptions(row, ingredientCode, herbName);
// 제품 선택 활성화
row.find('.product-select').prop('disabled', false);
// 원산지 선택 초기화 및 비활성화
row.find('.origin-select').empty().append('<option value="">제품 선택 후 표시</option>').prop('disabled', true);
} else {
const row = $(this).closest('tr');
row.attr('data-ingredient-code', '');
row.attr('data-herb-id', '');
row.find('.product-select').empty().append('<option value="">약재 선택 후 표시</option>').prop('disabled', true);
row.find('.origin-select').empty().append('<option value="">제품 선택 후 표시</option>').prop('disabled', true);
}
});
// 제품 선택 이벤트
newRow.find('.product-select').on('change', function() {
const herbId = $(this).val();
const row = $(this).closest('tr');
if (herbId) {
row.attr('data-herb-id', herbId);
// 원산지 선택 활성화
row.find('.origin-select').prop('disabled', false);
// 원산지 옵션 로드
const cheopTotal = parseFloat($('#cheopTotal').val()) || 0;
const gramsPerCheop = parseFloat(row.find('.grams-per-cheop').val()) || 0;
const totalGrams = gramsPerCheop * cheopTotal;
loadOriginOptions(herbId, totalGrams);
} else {
row.attr('data-herb-id', '');
row.find('.origin-select').empty().append('<option value="">제품 선택 후 표시</option>').prop('disabled', true);
}
});
@@ -1599,14 +1639,48 @@ $(document).ready(function() {
}
function loadHerbsForSelect(selectElement) {
$.get('/api/herbs', function(response) {
$.get('/api/herbs/masters', function(response) {
if (response.success) {
selectElement.empty().append('<option value="">약재 선택</option>');
response.data.forEach(herb => {
selectElement.append(`<option value="${herb.herb_item_id}">${herb.herb_name}</option>`);
// 재고가 있는 약재만 필터링하여 표시
const herbsWithStock = response.data.filter(herb => herb.has_stock === 1);
herbsWithStock.forEach(herb => {
// ingredient_code를 value로 사용하고, 한글명(한자명) 형식으로 표시
let displayName = herb.herb_name;
if (herb.herb_name_hanja) {
displayName += ` (${herb.herb_name_hanja})`;
}
selectElement.append(`<option value="${herb.ingredient_code}" data-herb-name="${herb.herb_name}">${displayName}</option>`);
});
}
}).fail(function(error) {
console.error('Failed to load herbs:', error);
});
}
// ingredient_code 기반으로 제품 옵션 로드
function loadProductOptions(row, ingredientCode, herbName) {
$.get(`/api/herbs/by-ingredient/${ingredientCode}`, function(response) {
if (response.success) {
const productSelect = row.find('.product-select');
productSelect.empty();
if (response.data.length === 0) {
productSelect.append('<option value="">재고 없음</option>');
productSelect.prop('disabled', true);
} else {
productSelect.append('<option value="">제품 선택</option>');
response.data.forEach(product => {
const stockInfo = product.stock_quantity > 0 ? `(재고: ${product.stock_quantity.toFixed(1)}g)` : '(재고 없음)';
productSelect.append(`<option value="${product.herb_item_id}" ${product.stock_quantity === 0 ? 'disabled' : ''}>${product.company_name} ${stockInfo}</option>`);
});
productSelect.prop('disabled', false);
}
}
}).fail(function() {
console.error(`Failed to load products for ingredient code: ${ingredientCode}`);
});
}