어느 가격이 "공급사에 지불하는 단가"인지는 회사마다 달라, 컬럼을 합치는 대신 회사가 고르게 했다.
판정은 settings.features.nego_baseline_field 1순위, 미설정 회사는 price 만 숨겼으면 purchase_price 폴백.
판정식 정본은 negodata/backend/common/nego_baseline.py (agent·negosium backend 가 같은 규칙 미러).
- negodata front: 회사 설정에 협상 기준가 라디오(선택지마다 실제 나갈 문장 미리보기 + 학습 경고),
'공급사 포털 안내' 탭 신설(협상 유의사항·헬프데스크 연락처), 용어 카탈로그에 target_price·supplier 추가.
인터넷 최저가는 숨김 대상에서 제외(신규 견적의 유일한 목표가 후보).
상품 등록 기본값에서 개발용 더미 제거(price 1,000,000·PROD-BAT-###·800,000·대한민국·10 EA·14).
- agent: get_item_price → get_item_baseline (기준가·호칭·회사 용어사전을 한 쿼리로),
"기존 공급가 대비" 하드코딩을 회사 용어로 치환 + 받침 기준 조사 자동 보정,
협상 스크립트 4종의 협력사·목표가·배송형태 용어를 {label_*} 토큰화, input_options 도 변수 치환 적용.
- negosium: 협상 화면 기준 단가·배송형태 라벨을 회사 설정 기준으로, 유의사항 본문과 헬프데스크 연락처를
하드코딩에서 회사 설정으로(미등록 시 영역 숨김). 유의사항의 VAT·배송비 문구 삭제(IMK 0803 ⑥).
- negodata backend: LPS 검색 가격 힌트를 items.price 고정에서 기준가 규칙으로.
검증: 기준가 설정 3 × 숨김 4 = 12조합에서 협상 멘트·포털 표시·LPS 힌트가 전부 일치.
agent 테스트 176건 통과. 보고서 negodata/docs/nego-baseline-verification.md
6.4 KiB
IMK 0803 가격협상 요청 정리
원본: 0803_가격협상 우선 적용 및 논의 정리.xlsx (12건). 시트의 우선/논의 구분 대신
스펙이 확정돼 바로 착수 가능한 것 / 결정이 있어야 착수 가능한 것으로 다시 갈랐다.
시트와 다른 3건 — ⑪은 수정으로, ③은 논의로, ④는 코드가 아니라 회사설정으로.
수정 (스펙 확정)
④ 부가세 입력칸 — 코드 문제 아님
vat_yn 은 상품폼(ProductFormSheet.tsx)·업로드 양식(ExcelUploadModal.tsx) 둘 다 이미 있다.
IMK 회사설정이 hidden_fields = [made_in, delivery_fee_yn, selling_price, vat_yn] 라 화면·양식에서
같이 빠진 것. 설정에서 체크 해제하면 끝.
⑥ 유의사항 문구 / 헬프데스크
- 유의사항의 VAT·배송비 문구는
GuideContent.tsx에서 상품별 동적 표기로 이미 바뀐 상태 → 요청대로 삭제. - 헬프데스크 연락처 실사용처 3곳, 전부 placeholder였음:
frontend/src/features/chat/components/menu/Contact.tsx,frontend/src/features/chat/components/popup/GuideContent.tsx,frontend/src/pages/LoginPage.tsx. 초청 메일엔 없음. - 반영:
companies.settings.branding.helpdesk: string[]로 회사별 설정화. 아래 "헬프데스크 배선" 참조.
⑦ 공급가–매입가 일원화
목표가 후보엔 이미 공급가가 없다(internet·purchase·selling — pricing.py). 잔여 2건:
- 화면의 공급가(
item.price, IMK 라벨 "공급가") 노출 제거 - 신규 견적이 아직 인터넷최저가만 씀 → 매입가 후보 추가
⚠️ items.price 를 hidden 으로 감추면 안 된다. hidden_fields 는 목록·등록폼·엑셀양식 3곳을
동시에 감추므로 신규 상품의 price 가 NULL 로 쌓이고, 협상 멘트의 인하율이 통째로 빠진다.
items.price 소비처 → items-price-is-nego-baseline 메모 참조.
⑫ 시장가 산식 (상품가+배송비)/1.1
인터넷 최저가는 몰 판매가(VAT 포함·배송비 별도)로 수집되는데 우리 매입가·공급사 견적가는 VAT 별도라 축이 어긋난 채 비교 중. 배송비를 더해 실구매 총액을 만들고 1.1로 나눠 VAT를 벗긴다(약 9% 낮아짐).
미결 2개:
- 배송비를 대부분 모른다(네이버 쇼핑 API 미제공, 실측 39/39 null). 0으로 칠지 / 미상이면 환산 스킵할지
- 적용 범위 — 카드 멘트 인용값만인지, 목표가 후보(
internet × (1−수수료율))에도 거는지. 후자면 인터넷 기준 목표가가 9% 내려간다.
⑪ 인터넷최저가 ≥ 목표가 → 시장가 카드 차단 (절반 완료)
견적생성 시 선택 게이팅은 반영됨(useCardGating.tsx). 잔여 = 협상 진행 중 런타임 차단.
⑤ 상품 일괄등록 유효성 오류
재현 케이스(어느 필드가 오탐인지) 확보 후 수정. 엑셀 원본을 받는 게 빠름.
② 목표가 초과 낙찰 허용
상한 그릇은 들어감(sessions.done_ceiling_price, 커밋 91b8dc77).
협상 엔진이 아직 안 읽는다 — agent/·루트 backend/ 어디에도 done_ceiling 참조 없음.
타결 판정에 배선하면 EST-202607-973E 케이스 해소.
조건: 기존 단가 > 견적가 > 목표가 > 앵커링가면 타결. 여기서 "기존 단가" = items.price.
논의 (결정 필요)
③ 중간값 로직
②의 짝. "목표가 초과 제시 안 함" 상한을 푸는 건 맞는데 대신 무엇을 상한으로 쓸지 —
done_ceiling_price(목표가×(1+율))인지 기존 단가인지.
IMK 예시(목표 39,800 / 필요 40,200)는 +1.0% 수준이고 현 기본율은 +5%.
⑧ 세팅 횟수만큼 카드 소진
목표가에 이미 근접했는데도 남은 카드를 다 태울지. 라운드가 늘면 결렬 위험·시간도 는다. "최소 사용 횟수 보장" vs "조기 타결 우선" 중 택.
⑨ 앵커 제안가 단조성
후속 카드 제안가가 앞 카드보다 낮아지는 건(16,980 → 16,810) 카드마다 고정 인하율을 쓰기 때문. 제안가를 카드가 아니라 라운드에 종속시켜 단조 상향으로 바꾸는 구조 변경 → 이 중 유일하게 범위가 큼.
⑩ 와일드카드 + 최종제안 중복
종결 국면은 현재 필수 관문이다(chat_engine.py check_iteration_limit):
closing_playedfalse →force_closing→ 종결 전용 와일드카드 또는 폴백 최후통첩(목표가 제시)closing_playedtrue → 제시가 ≤ 목표가면 타결, 초과면 결렬
스킵하면 마지막 우리 카운터가 안 나가고 공급사 마지막 제시가로 즉시 판정 → 결렬률 상승.
구현 시 closing_played = True 는 반드시 세워야 무한루프를 피한다.
단 중복 방지는 이미 양쪽에 걸려 있다 — 진입 단계(chat_engine.py)도 종결 단계(chat_service.py)도
is_played 로 쓴 카드를 건너뛴다. 코드상 같은 카드가 2회 나올 수 없으므로 IMK가 본 것이
진짜 동일 카드인지 확인이 먼저(dev DB엔 EST-202607-5947 없음 — 운영 데이터).
⑬ SG별 앵커링
SG = items.category (IMK 라벨 "SG명"). 현재 SG는 앵커링에도 목표가 산정에도 안 들어간다.
앵커링 축은 3개 — (company_id, supplier_type, price_range_index):
| 축 | 값 |
|---|---|
company_id |
회사 |
supplier_type |
1 유통 / 2 제조 / 3 총판 — 개별 협력사가 아니라 유형 |
price_range_index |
목표가 기준 46개 자릿수 구간 |
SG 축을 추가하면 셀이 46×3×SG수로 쪼개져 셀당 표본 10건(SAMPLE_THRESHOLD)을 못 채우고
평가가 스킵·이월되어 학습이 사실상 멈춘다. 이게 실제 결정 포인트.
헬프데스크 배선 (⑥ 반영분)
companies.settings.branding.helpdesk: string[] — 한 줄 = 담당자 한 명, 자유 문자열.
| 경로 | 파일 |
|---|---|
| 편집 UI | negodata/front/src/features/settings/SettingsView.tsx (브랜딩 탭) |
| 타입 | negodata/front/src/features/settings/catalog.ts |
| 로그인 후 전달 | backend/services/auth_service.py me() — branding dict 통째로 내려 자동 포함 |
| 로그인 전 전달 | backend/services/auth_service.py session_branding() + protocol.py Res_SessionBranding |
| 표시 | frontend/ 의 Contact.tsx · GuideContent.tsx · LoginPage.tsx |
값이 비면 각 표시부는 연락처 줄을 렌더하지 않는다(placeholder 노출 금지).