From 2859dc43cc827b52654095d1246340faa2fa08a6 Mon Sep 17 00:00:00 2001 From: thug0bin Date: Wed, 4 Mar 2026 14:02:47 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=8F=99=EB=AC=BC=EC=95=BD=EB=A7=8C=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=EC=8B=9C=20=EA=B2=80=EC=83=89=EC=96=B4=20?= =?UTF-8?q?=EC=97=86=EC=9D=B4=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 동물약은 39건뿐이라 전체 조회해도 빠름 - 동물약만 체크 + 검색어 없음 → 전체 동물약 리스트 - 쿼리 조건 동적 생성 (animal_condition, search_condition) --- backend/app.py | 123 ++++++++++++++++---------- backend/templates/admin_products.html | 20 +++-- 2 files changed, 86 insertions(+), 57 deletions(-) diff --git a/backend/app.py b/backend/app.py index fa9de43..4e426b1 100644 --- a/backend/app.py +++ b/backend/app.py @@ -3313,15 +3313,26 @@ def api_products(): animal_only = request.args.get('animal_only', '0') == '1' in_stock_only = request.args.get('in_stock_only', '0') == '1' - if not search or len(search) < 2: + # 동물약만 체크시 검색어 없어도 전체 조회 가능 + if not animal_only and (not search or len(search) < 2): return jsonify({'success': False, 'error': '검색어는 2글자 이상 입력하세요'}) try: drug_session = db_manager.get_session('PM_DRUG') + # WHERE 조건 생성 (동물약 전체 조회 시 검색 조건 없음) + search_condition = "" + if search: + search_condition = """ + AND (G.GoodsName LIKE :search_like + OR G.DrugCode LIKE :search_like + OR G.BARCODE LIKE :search_like) + """ + + # 동물약만 필터 (쿼리에서 직접 처리) + animal_condition = "AND G.POS_BOON = '010103'" if animal_only else "" + # 제품 검색 쿼리 - 사용약품만 옵션에 따라 JOIN 방식 변경 - # in_stock_only: INNER JOIN으로 재고 있는 제품만 (빠름) - # 그렇지 않으면: LEFT JOIN으로 모든 제품 (느림) if in_stock_only: # 최적화된 쿼리: 재고 있는 제품만 (IM_total INNER JOIN) products_query = text(f""" @@ -3337,62 +3348,76 @@ def api_products(): IT.IM_QT_sale_debit as stock FROM CD_GOODS G INNER JOIN IM_total IT ON G.DrugCode = IT.DrugCode AND IT.IM_QT_sale_debit > 0 - WHERE - G.GoodsName LIKE :search_like - OR G.DrugCode LIKE :search_like - OR G.BARCODE LIKE :search_like + WHERE 1=1 + {animal_condition} + {search_condition} ORDER BY G.GoodsName """) else: # 전체 쿼리 (OUTER APPLY 포함, 느림) - products_query = text(f""" - SELECT TOP {limit} - G.DrugCode as drug_code, - G.GoodsName as product_name, - COALESCE(NULLIF(G.BARCODE, ''), U.CD_CD_BARCODE, '') as barcode, - G.Saleprice as sale_price, - G.Price as cost_price, - CASE - WHEN G.SplName IS NOT NULL AND G.SplName != '' THEN G.SplName - WHEN SET_CHK.is_set = 1 THEN N'세트상품' - ELSE '' - END as supplier, - CASE WHEN SET_CHK.is_set = 1 THEN 1 ELSE 0 END as is_set, - G.POS_BOON as pos_boon, - ISNULL(IT.IM_QT_sale_debit, 0) as stock - FROM CD_GOODS G - LEFT JOIN IM_total IT ON G.DrugCode = IT.DrugCode - OUTER APPLY ( - SELECT TOP 1 CD_CD_BARCODE - FROM CD_ITEM_UNIT_MEMBER - WHERE DRUGCODE = G.DrugCode AND CD_CD_BARCODE IS NOT NULL AND CD_CD_BARCODE != '' - ) U - OUTER APPLY ( - SELECT TOP 1 1 as is_set - FROM CD_item_set - WHERE SetCode = G.DrugCode AND DrugCode = 'SET0000' - ) SET_CHK - WHERE - G.GoodsName LIKE :search_like - OR G.DrugCode LIKE :search_like - OR G.BARCODE LIKE :search_like - OR U.CD_CD_BARCODE LIKE :search_like - ORDER BY G.GoodsName - """) + # 동물약만 조회 시 OUTER APPLY 생략 가능 + if animal_only: + products_query = text(f""" + SELECT TOP {limit} + G.DrugCode as drug_code, + G.GoodsName as product_name, + COALESCE(NULLIF(G.BARCODE, ''), '') as barcode, + G.Saleprice as sale_price, + G.Price as cost_price, + ISNULL(G.SplName, '') as supplier, + 0 as is_set, + G.POS_BOON as pos_boon, + ISNULL(IT.IM_QT_sale_debit, 0) as stock + FROM CD_GOODS G + LEFT JOIN IM_total IT ON G.DrugCode = IT.DrugCode + WHERE G.POS_BOON = '010103' + {search_condition} + ORDER BY G.GoodsName + """) + else: + products_query = text(f""" + SELECT TOP {limit} + G.DrugCode as drug_code, + G.GoodsName as product_name, + COALESCE(NULLIF(G.BARCODE, ''), U.CD_CD_BARCODE, '') as barcode, + G.Saleprice as sale_price, + G.Price as cost_price, + CASE + WHEN G.SplName IS NOT NULL AND G.SplName != '' THEN G.SplName + WHEN SET_CHK.is_set = 1 THEN N'세트상품' + ELSE '' + END as supplier, + CASE WHEN SET_CHK.is_set = 1 THEN 1 ELSE 0 END as is_set, + G.POS_BOON as pos_boon, + ISNULL(IT.IM_QT_sale_debit, 0) as stock + FROM CD_GOODS G + LEFT JOIN IM_total IT ON G.DrugCode = IT.DrugCode + OUTER APPLY ( + SELECT TOP 1 CD_CD_BARCODE + FROM CD_ITEM_UNIT_MEMBER + WHERE DRUGCODE = G.DrugCode AND CD_CD_BARCODE IS NOT NULL AND CD_CD_BARCODE != '' + ) U + OUTER APPLY ( + SELECT TOP 1 1 as is_set + FROM CD_item_set + WHERE SetCode = G.DrugCode AND DrugCode = 'SET0000' + ) SET_CHK + WHERE 1=1 + {search_condition} + ORDER BY G.GoodsName + """) - search_like = f'%{search}%' - rows = drug_session.execute(products_query, { - 'search_like': search_like - }).fetchall() + # 파라미터 설정 (검색어가 있을 때만) + params = {} + if search: + params['search_like'] = f'%{search}%' + + rows = drug_session.execute(products_query, params).fetchall() items = [] for row in rows: is_animal = row.pos_boon == '010103' - # 동물약만 필터링 - if animal_only and not is_animal: - continue - # APC 조회 (동물약인 경우) apc = None if is_animal: diff --git a/backend/templates/admin_products.html b/backend/templates/admin_products.html index 12e6b6f..bf1ec0e 100644 --- a/backend/templates/admin_products.html +++ b/backend/templates/admin_products.html @@ -695,19 +695,23 @@ function searchProducts() { const search = document.getElementById('searchInput').value.trim(); - if (!search) { - alert('검색어를 입력하세요'); - return; - } - if (search.length < 2) { - alert('2글자 이상 입력하세요'); - return; + const animalOnly = document.getElementById('animalOnly').checked; + + // 동물약만 체크시 검색어 없어도 전체 조회 가능 + if (!animalOnly) { + if (!search) { + alert('검색어를 입력하세요'); + return; + } + if (search.length < 2) { + alert('2글자 이상 입력하세요'); + return; + } } const tbody = document.getElementById('productsTableBody'); tbody.innerHTML = '

검색 중...

'; - const animalOnly = document.getElementById('animalOnly').checked; const inStockOnly = document.getElementById('inStockOnly').checked; let url = `/api/products?search=${encodeURIComponent(search)}`; if (animalOnly) url += '&animal_only=1';