## 문제
- ORDER BY를 INV_QUAN으로 변경 시 같은 약품이 중복 표시됨
- 예: 수인 30개 주문 → '수인 30, 수인 30' (60개로 잘못 표시)
## 근본 원인
- GROUP BY에 JOIN된 테이블 컬럼(IT.IM_QT_sale_debit, POS.CD_NM_sale) 포함
- IM_total, CD_item_position이 1:N 관계일 때 같은 DrugCode가 여러 행으로 팽창
- GROUP BY가 이 값들을 포함하여 중복 행 유지
## 해결
- GROUP BY를 P.DrugCode 만으로 축소 (진짜 그룹핑 기준만)
- 나머지 컬럼은 MAX() 집계함수 사용
- DrugCode당 정확히 1행 보장
## 변경 내용
- GROUP BY: 6개 컬럼 → 1개 (P.DrugCode)
- SELECT: ISNULL() → MAX(ISNULL()) 래핑
- ORDER BY: INV_QUAN 기준 정렬 (투약량순)
- get_conversion_factor()에서 storage_conditions 함께 반환
- PostgreSQL 조회 결과 없으면 '실온보관' 기본값
- create_label_image()에 storage_conditions 파라미터 추가
- 용법 박스 아래, 조제일 위 여백에 보관조건 표시
- 모든 약품에 보관조건 표시 (실온보관 포함)
- Brother QL 라벨 인쇄 API 추가 (POST /pmr/api/label/print)
- PMR 라벨 인쇄 버튼 동작 구현 (QL-810W)
- 환산계수 sung_code 프론트→백엔드 전달 추가
- 환산계수 모달 제품명 readonly 처리 (MSSQL 원본 보호)
- Pillow 10+ 호환성 패치 (ANTIALIAS → LANCZOS)
- API: 처방 조회 시 CD_PERSON.PHONE 반환
- API: PUT /api/members/{code}/phone - 전화번호 저장
- UI: 나이/성별 옆에 전화번호 뱃지 표시
- UI: 전화번호 없으면 '전화번호 추가' 클릭 가능
- UI: 클릭 시 모달에서 전화번호 입력/저장
## 배경
- 기존: QR 적립된 구매만 표시 (SQLite claim_tokens)
- 문제: pos-live에서 고객 매핑한 구매가 안 보임
## 변경 사항
- admin_user_detail API에 MSSQL SALE_MAIN 조회 추가
- 전화번호 → CD_PERSON.CUSCODE → SALE_MAIN.SL_CD_custom 매칭
- QR 적립 구매와 POS 직접 구매 통합 표시
- 중복 제거: 이미 QR 적립된 건은 스킵
- 최근 30일, 최대 20건 조회
## 구매 소스 구분
- QR 적립: points > 0
- POS 직접 매핑: points = 0, source = 'pos'
- GET /api/customers/search: CD_PERSON 검색 (최근 활동순)
- PUT /api/pos-live/{order}/customer: SALE_MAIN 고객 업데이트
- GET /api/customers/{code}/mileage: 비동기 마일리지 조회
- UI: 고객 뱃지 클릭 → 검색 모달 → 선택 → 업데이트
- 마일리지: 이름+전화뒤4자리 매칭, 비동기 표시
- 조제목록에 환자 뱃지 표시 (3명 이하: 전체, 3명 초과: 최근 3명 + 외 N명)
- API에 unique_patients, recent_patients 필드 추가
- limit 1000 → 5000 증가
- 현재고: 계산 방식 → IM_total.IM_QT_sale_debit (실제 DB 값)
- /admin/drug-usage 페이지 + API 3개 구현
- GET /api/drug-usage: 기간별 약품 통계 (조제건수, 입고건수)
- GET /api/drug-usage/<code>/imports: 약품별 입고 상세
- GET /api/drug-usage/<code>/prescriptions: 약품별 조제 상세
UX 개선:
- 약품 클릭 시 입고/조제 상세 펼침 패널
- table-layout:fixed + colgroup으로 컬럼 너비 고정
- white-space:nowrap으로 날짜/숫자 줄바꿈 방지
- 금액/거래처 사이 border로 구분선 추가
- 발행기관 OrderName으로 수정 (InsName 오류 수정)
QT_GUI 데이터와 100% 일치 검증 (살라겐정)
## APC 코드 체계 확장
- 기존: 023%만 검색 (~2023년 제품만)
- 변경: 02% OR 92% + 13자리 검증
- 02%: 2023년 이전 item_seq (9자리) 기반 APC
- 92%: 2024년 이후 item_seq (10자리) 기반 APC
- 999% 등 청구프로그램 임의코드는 제외
## 동물약 챗봇 피부약 추천 개선
- 피부약 2단계 추천 구조 추가
- 1차(치료): 의약품 (개시딘겔, 테르비덤 등)
- 2차(보조케어): 의약외품 (스킨카솔 - 회복기 피부보호)
- 스킨카솔은 의약외품임을 명시하여 치료제로 오인 방지
## 기타
- RAG 테스트 스크립트 추가
- 수인약품 API 문서화
autobegin 상태에서 begin() 재호출 에러 → engine.begin() 컨텍스트 매니저로 변경.
189건 PostgreSQL weight_min_kg/weight_max_kg 업데이트 완료.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
지마스터 캣 등에서 "4kg 초과-8kg 이하" 형태의 체중 구간이
(0, 8)로 잘못 파싱되던 문제 수정.
- 패턴8a: "Xkg 초과-Ykg" 전용 패턴 추가
- 패턴8c: "초과" 컨텍스트 중복 방지
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
제품명에 사이즈 라벨이 없지만 dosage 컬럼으로 구분 가능한 제품
(하트웜 솔루션, 지마스터, 넥스포인트 등) 처리 추가.
- 고유 dosage 수 == 체중구간 수 일 때 오름차순 매칭
- 작은 용량 = 작은 체중 원칙 적용
- 결과: 146건 → 189건으로 커버리지 증가 (+43건)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dosage_instructions에서 체중 구간을 파싱하여 weight 컬럼 업데이트.
- 제품명 사이즈 라벨(소형견/중형견 등)로 체중구간 매칭
- 단일 체중구간 제품은 전체 APC에 적용
- 통합 제품(SS,S,M,L)은 안전하게 SKIP
- 축산용(>60kg) 자동 제외
- dry-run 기본, --commit으로 실행
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. 프롬프트 개선:
- 상세 요청 감지 ('자세히', '설명해줘' 등)
- 상세 요청 시 10-15문장 응답
- RAG 검색 결과 적극 활용 지시
2. 벡터 검색 수정:
- L2 거리 → 유사도 변환: 1/(1+distance)
- 음수 유사도 문제 해결
- 임계값 0.3 적용 (30% 미만 제외)
3. 컨텍스트 주입 개선:
- 상세 질문 시 n_results=5로 증가
- RAG 활용 지시 추가
- API 3개 추가:
- POST /api/upload-session (세션 생성)
- GET /api/upload-session/{id} (상태 확인/폴링)
- POST /api/upload-session/{id}/image (이미지 업로드)
- 모바일 업로드 페이지 (/upload/{session_id})
- 이미지 등록 모달에 '📱 모바일' 탭 추가
- QR 스캔 → 모바일 촬영 → PC 실시간 반영
- 2초 폴링으로 업로드 완료 감지
- 세션 10분 만료, 메모리 기반 관리
- Edit 툴로 부분 수정하여 인코딩 유지
- 위치 뱃지 클릭 시 위치 수정 모달 표시
- '미지정' 뱃지 스타일 (점선 테두리, 클릭 유도)
- 기존 위치 선택 드롭다운 + 직접 입력 가능
- 위치 삭제 기능
- products 페이지와 동일한 API 재활용 (/api/locations, /api/drugs/.../location)
- 다크 테마에 맞는 모달 스타일
- Edit 툴로 부분 수정하여 인코딩 유지