Commit Graph

70 Commits

Author SHA1 Message Date
0d62440ee1 [feat] negodata/front: 온보딩 페이지 추가 + 이용안내 모달 재작성 2026-07-09 16:08:54 +09:00
18020a9e07 [feat] 카드 카탈로그 DB 정본화 — config 결합 제거 + 학습 보존 자동반영
카드 카탈로그(negodata)가 Q-table action space 를 정의하는 정본이 되고, 카드 변경이
config 수정·학습 손실 없이 agent 에 자동 반영되는 고리를 완성.

- action space 정리: 카탈로그 전체(NGC-001~011, 11장) 고정, 견적별 선택은 축소가 아니라
  available_mask(_selection_mask) 로 처리 — action_id↔카드 대응을 견적마다 일정하게 유지해
  Q-table 학습 일관성 보장. 구 인덱스 방식(selected[action_id]) 폐기.
- ① 카탈로그 DB 정본화: action_mapping.type=db 면 registry 가 card.nego_cards(user_id NULL,
  number 순) 조회로 action_to_card 동적 구성(파일은 폴백). port/adapter(card_catalog_*).
  _base=type:db. → negodata 카드 추가/삭제 시 config 수정 불필요.
- ② 차원 변경 학습 보존 마이그레이션: migrate_active_version_dim — 겹치는 셀 복사
  (append/truncate 안전) + 새 카드 fresh. model_store.load 가 차원 불일치 시 호출.
- ③ reload 엔드포인트: /v1/catalog-refresh(테넌트) · /v1/catalog-refresh-all(전역, 화이트리스트).
- ④ 브랜드: company_profile_repo — 자동 온보딩 고객사(company_id UUID)는
  company.companies.name 으로 {company_name} 채움. 데모 테넌트는 파일 유지.
- 크로스서비스: negodata card_service 가 공용 nego 카드 변경 시 agent_notify 로 전역 리로드 알림
  (best-effort, is_test skip). config 에 agent_base_url.
- 하니스 episodes 400→600(action 11 수렴). 테스트 갱신·추가로 agent 98/98.

알려진 갭(후속): per-company 카탈로그 스코프(회사 카드도 action space 포함), 카탈로그 중간
삭제 시 카드번호 기반 마이그레이션.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:56:28 +09:00
a2731d97f5 conflict 수정. 2026-07-07 17:18:54 +09:00
15f3d10eb6 와일들카드 선택 수정 . 2026-07-07 17:16:19 +09:00
d21e7c10b0 [refactor] 공급유형 기준을 상품-협력사 매핑으로 전환 2026-07-07 17:05:07 +09:00
9ac8fe6363 Merge commit '8c700b1e080a29a02375664e856bc6e5b757c7c3' into feature/negodata 2026-07-07 16:19:52 +09:00
c62b66e35b [feat] negodata: 소유권 게이팅 + 견적 낙찰 + 작성자명 표시 + 전화입력 + 카드 엑셀
- 소유권 게이팅(common/authz): 변경 액션 본인∪OWNER, 협력사 삭제 OWNER 전용
- 견적 수동 낙찰(award) + 작성자명(creatorName) 표시 + 전화번호 입력 컴포넌트 + 카드 엑셀 업로드
- supplier_type 은 이번 커밋 미변경(다음 커밋에서 코드부터 정리 예정)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:18:33 +09:00
c32ee300e6 [feat] negodata: 통계 페이지(성과 분석) 추가 — 파생 집계 API + recharts 차트
- 백엔드 GET /v1/statistics/summary: 회사/내견적 스코프, 최근 6개월 파생 집계(저장 X, DDL 0)
- 지표: 총절감/절감률/낙찰률/앵커도달률/마감수/재견적라운드 + 월별추이·마감결과분해·참여율·유형별(협상/견적)·카테고리·카드빈도
- 프론트 recharts+shadcn Chart, 사이드바 통계 nav, /statistics

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:18:33 +09:00
1c8c4254a6 [feat] 협상 멘트 스타일 파이프라인(마커) + agent 카드 DB 소스 + supplier_items 연동
표현 아키텍처: agent 스크립트는 의미(텍스트)만 소유, 표현(굵기·색)은 프론트 소유.
Slate 는 negodata 에디터 내부에만 두고, 전송/저장은 마커 문자열 한 벌(구버전의 리치텍스트 이중관리 폐기).

- 트랙 A (negodata): serializeToMarker 추가 — Slate 마크(bold/underline/color)를 **/__/{{토큰}} 로
  인코딩해 nego_cards.script 저장. edit_script(Slate 원본)는 재편집 전용. 고정 3색 → 시맨틱 토큰(강조/안내).
- 트랙 B (agent): 카드 멘트 DB 소스 — ICardScriptRepository/CardScriptDbRepository(port+adapter),
  ScriptRepository.resolve_card_script 가 cards.source_type=backoffice_db 면 card.nego_cards.script 우선,
  없으면 파일 폴백. action_id→card_id→nego_cards.number 매칭.
- 트랙 C (양 프론트): renderEmphasis 재귀 파서 — **굵게**·__밑줄__·{{강조|빨강}}·{{안내|파랑}} 중첩 렌더.
  색은 시맨틱 토큰→디자인 토큰 클래스(다크모드 안전). negodata tokens.css 에 --info 신설. CardTable 미리보기 적용.
- supplier_items 연동: 유통코드=supplier_items.supply_type(→quotations.supplier_type 폴백),
  파트너유형=상품별 매핑 협력사 수(→세션 이력 폴백).
- 가격 수용률: 기존 공급가(item_price) 기준 양보율로 정정 — 첫 라운드부터 실값(첫 제시가 기준 0 아님).

테스트: agent 86/86, 공급사 frontend·negodata front tsc 통과.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:19:45 +09:00
43dbcbcd96 [feat] negodata: 협력사↔상품 매핑(취급상품·공급유형) + 견적 픽리스트 서버검색 콤보박스
- partner.supplier_items 매핑테이블 신설: supply_type(0없음/1유통/2제조/3총판), 부분 유니크 인덱스(soft-delete 인지). quotations.supplier_type와 의미단위 달라 컬럼명 supply_type.
- 백엔드: supplier_item CRUD/service/router(/v1/supplier-item, by-supplier·by-item·create·bulk·update·delete). 소유권 company 스코프. ErrorType.SUPPLIER_ITEM_NOT_FOUND 추가.
- DDL: 01-schema/04-alter(날짜접미 리네임) + dev DB 반영.
- 프론트(feature): 협력사 엑셀 '취급상품' 컬럼(콤마 상품명→일괄매핑, 이름 미매칭 스킵+리포트, 유형 안받고 없음), 협력사 상세 취급상품 관리(추가/삭제/유형변경 즉시반영), 견적모달 협력사 리스트에 선택상품 공급유형 배지.
- 재사용 서버검색 Combobox 신설 → 취급상품·견적상품·협력사초청·협상카드 픽리스트에 적용(size 100 캡 해소, 검색은 서버 ILIKE). 견적 선택상품 파생값은 useGetItem 단건조회로 안정화.
- orval 재생성(supplier-item 클라이언트/모델).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:59:13 +09:00
1ac42ddc9c [feat] negodata: 협력사 총매출액(total_revenue) 추가 + 우선선정(priority) 제거
Helped-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 17:27:59 +09:00
3176ec4262 Merge branch 'main' into feature/negodata
Helped-By: Claude Opus 4.8뀨 <noreply@anthropic.com>
2026-07-06 16:58:04 +09:00
f1a29b8bde [feat] negodata/front: 견적생성 UX 재개편(협상/견적 2축·낙찰기준 스펙트럼·카드 스텝) + 개찰 표기 + 세팅 정리
Co-Authored-By: Claude Opus 4.8뀨 <noreply@anthropic.com>
2026-07-06 16:36:33 +09:00
38709b3098 [feat] negodata: 결렬 폐지→개찰 마감 개편 + 낙찰기준 견적 단위 이관 + 세팅 컬럼 정리 (백엔드+DB)
Co-Authored-By: Claude Opus 4.8뀨 <noreply@anthropic.com>
2026-07-06 16:36:00 +09:00
민헌
5f9dec66cc refactor(anchoring): DDL·compose 루트 통합 — postgres-init/05 신설, 모듈 schema.sql·docker-compose 제거
- postgres-init/05-anchoring-schema.sql 신설(02-learning-schema 스타일) — anchoring 스키마·
  adjustments·인덱스 2종(부분 인덱스 포함)·뷰 2종. sessions 컬럼은 01/04 소관으로 명시
- 루트 docker-compose.yml 에 anchoring + anchoring-redis 서비스 추가(기존 스타일),
  헤더 서비스 목록·DB 준비 절차 갱신, 모듈 compose 의 로그 로테이션(10MB×5) 이관
- 모듈 schema.sql·docker-compose.yml 삭제 — DDL 단일 원본은 postgres-init/05
- tests/conftest 부트스트랩 경로를 05 로 교체, 문서 4종·주석 포인터 갱신
  (인수인계의 낡은 'negodata 가 redis 참조' 문구도 무Redis 현실로 교정)
- 검증: 모듈 20·negodata 50 테스트 통과, docker compose config 정상

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:37:13 +09:00
민헌
a2c299aa14 refactor(anchoring): 도메인 이름 전면 개편 — adjustments·anchoring_value/price·price_range·sample 용어 통일
용어 체계: 값=anchoring_value(정수‰)·가격=anchoring_price·조정=adjustment·구간=price_range·표본=sample

- DB: rate_adjustments→anchoring.adjustments (id→adjustment_id, price_bracket_index→price_range_index,
  nego_count→sample_count, anchor_rate_before/after→anchoring_value_before/after,
  consumed_session_ids→used_session_ids)
- sessions: target_anchoring_price→anchoring_price, anchor_rate_permille→anchoring_value,
  last_offered_price→last_offer_price, anchoring_adjustment_id→used_by_adjustment_id
- 뷰: rate_history/current_rates→value_history/current_values, delta_permille→value_change
- 코드: calc_price_range_index·calc_anchoring_price·evaluate_samples·get_current_value·
  get_latest_adjusted_value·get_current_anchoring_value·fetch_current_values·get_base_anchoring_value·
  Adjustment(ORM)·update_last_offer_price, 상수 ANCHORING_VALUE_MIN/MAX·ADJUSTMENT_STEP·
  PRICE_RANGE_COUNT/INDEX_MAX, 배치 로그 키 bracket=→price_range=
- API: negodata protocol 필드 target_anchoring_price→anchoring_price (front 생성 모델·컴포넌트 동반)
- 기존 DB 마이그레이션 신설: schedules/anchoring/migrations/20260706_rename_anchoring.sql
  (멱등 DO 블록 — 테이블·컬럼·뷰·인덱스·PK 제약. 코드 배포와 동시 적용 필요)
- postgres-init 01·04, 문서 6종 동기화
- 실배포 전 수정 포함: main.py argparse 화(--dry-run 단독·오타 플래그 기동 전 차단),
  박제 정합식 calc_anchoring_price 재사용, clamped 지표가 실제 포화만 집계(경계값 유지 제외)

주의: sessions.anchoring_value(정수‰)와 quotation_settings.anchoring_value(구 float 비율)는
같은 이름·다른 단위 — 구 컬럼은 미변경.

검증: 모듈 20·negodata 50·backend 57 테스트 통과, front tsc·vite build 통과,
로컬 DB 마이그레이션 적용 후 배치 dry-run·상주 기동·양 서버 부팅 확인.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:17:01 +09:00
민헌
b362edf0d7 feat(negodata): 앵커링 v1.2 적용 — 칸 rate 조회·정수 박제·재생성 앵커 상속 폐지
- common/anchoring 이식 패키지 신설: 원본(schedules/anchoring)에서 읽기 경로 발췌
  (constants·base_table·service) + reader 이식판(Redis 미사용 — current_rates 뷰
  단일 쿼리, 실패·무이력 시 정적 테이블 폴백으로 견적 생성 무중단)
- _build_quotation: 목표가 산정과 앵커 산출 분리 — 칸(items.company_id ×
  quotations.supplier_type × 가격구간) rate 로 tp*(1000-rate)//1000 정수 박제,
  anchor_rate_permille 동시 기록. quotation_settings.anchoring_value 계산 사용 중단
- 재생성(regenerate_next_round): target_price 만 상속, 앵커는 생성 시점 rate 재계산
  (인수인계 규칙 1 — 상속 폐지)
- sessions 모델 anchor_rate_permille 매핑, crud get_item_companies 신설
- 테스트 6종 신설(스키마 부재 폴백·칸별 조정 반영·유형 미지정·재생성 재계산·
  bracket 경계 골든 벡터) — 전체 스위트 50 통과
- negodata 담당자 승인 하 직접 적용. 6개월 압축 시뮬레이션(격주 배치 13회,
  세션 522건)으로 박제→소비→재박제 루프·클램프·이월·이중소비 방지 검증 완료

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 16:37:27 +09:00
c98e5fa7bb [feat] negodata/front: 이용안내 마감판정 재작성 + 견적상세 목표가·앵커링 표시
- 이용안내(OnboardingGuideModal) '마감 판정' 탭 재구성: 가격게이트(견적세팅 mid/over_action 정책·기본 낙찰) + 완료양상 결정표(낙찰/재생성/결렬, 8행=close_reason 8종) + 재생성 한도(체인 전체 총합).
- 견적상세 헤더카드에 목표가(→산정내역 링크)·앵커링가 표기(대표 세션 공통값), 협상 리스트에 앵커링 컬럼 추가. onShowTarget 을 헤더카드로 이관.
- 목록 마감결과 배지: 낙찰 시 낙찰 협력사명 병기.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 14:41:52 +09:00
e23349c11e [feat] negodata: 마감 close_reason 개편 + 견적상세 단가조정 스펙트럼
- 마감사유 quotations.close_reason(SMALLINT, 8종) 신설 — 낙찰/재협상3종/유찰4종 구분, 재생성 한도 카운팅. quotation_settings 에 mid_action·over_action·regen_limit(3구간 가격정책) 추가. models.py·enums.py·01-schema.sql·crud·service·테스트 정합.
- 프론트 노출: CloseReason/PriceGateAction 라벨·배지, 목록 마감결과 컬럼, 견적세팅 모달 3구간. orval 생성모델 갱신.
- 견적상세 결과밴드: 목표가→낙찰가→절감 stat 트리오를 단가조정 흐름 스펙트럼(목표가·최저·최고 투찰가)으로 교체. lowestBid/highestBid 는 bid_price 파생(DB 컬럼 아님), 최초가 컬럼 없어 시작앵커=최고 투찰가로 대체. 라벨 방향 값순 번갈아 배치로 겹침 방지.
- 라벨 정정: "단독 낙찰"→"낙찰"(낙찰은 단수), "목표 대비"→"목표가 대비".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 08:37:40 +09:00
43ac308a5e [docs] negodata: README 갱신 — 테스트 실행법·API 도메인·프론트 포트
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:06:55 +09:00
82076e138e [test] negodata: 백엔드 테스트 스위트 구축 + 공통 픽스처(conftest) 정비
- test DB 세션마다 자동 create/drop (팀원은 Postgres만 있으면 pytest 한 방)
- auth_headers 시드 픽스처(무인증 /auth/create 제거 대응) + other_company_id
- 커버: 회사 스코프(견적·상품·협력사·대시보드·세팅), 견적 마감 재견적 O/X + 알림,
  견적 생성·목표가, 알림함 읽기, 회사유저 OWNER 게이팅, 기존 파일 검증/기대결과 주석 정비

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:06:55 +09:00
78bef0f5f9 [fix] negodata: /me 응답 role 을 UserRole enum 으로 직렬화 (Pydantic 경고 제거)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:41:39 +09:00
f19c6f57ea [chore] negodata: enum 주석 영문값 보강 + 견적상태 3종 정리 + 대시보드 정비
- enum DDL 주석에 영문값 보강(status/role/delivery/usage_type/qt_type/supplier_type)
- 견적상태 3종(생성/진행중/마감)으로 정리(ON_HOLD 제거), 배송 PARTNER→SUPPLIER
- 대시보드 손질, 관련 테스트·negosium 견적유형 주석 동반

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 17:25:38 +09:00
615167ad41 [feat] negodata/front: 견적 생성 UX 개선 + 온보딩 견적유형 안내
- 견적 생성: 협력사 유형 전 유형 입력(항상 노출), 명칭 협상견적→견적, 단계 인디케이터 확대·클릭 이동, 라벨 통일
- 온보딩 가이드: 견적 유형 차이 안내 탭(목표가 산정·협력사 수, MD 제시가 우선 강조)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 17:25:15 +09:00
1d111a031e [feat] negodata: 알림함 — 견적 마감 결과(낙찰/재생성/결렬) 자동 통지
- company.notifications 테이블 + NotificationType(SUCCESS/REGENERATED/FAILURE)
- 백엔드 알림 조회/읽음 API + close_and_decide 결과 분기마다 알림 생성
- 헤더 알림 벨(안읽음 배지) + 알림 페이지(읽음·딥링크)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 17:24:59 +09:00
b42cc41b5e [feat] negodata: 대시보드 — 회사/내 견적 요약 API + 프론트 페이지
백엔드: GET /v1/dashboard/summary — 회사 전체(company)+내 견적(mine) 스코프로 진행중·이번달생성·마감임박(72h)·메일미발송·동가·결렬 집계(users.company_id 조인, 읽기 전용). 프론트: /dashboard 페이지(KPI 카드 + 액션 위젯, 행 클릭→/quotation?detail= 딥링크), 사이드바·라우트 배선, 루트·로그인 리다이렉트를 /dashboard 로. orval 재생성(useGetDashboardSummary + Dashboard* 타입, QuotationData.creator_name 동기화 포함).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:01:38 +09:00
5f648965e2 [fix] negodata: 견적 회사 스코프 적용 (목록·단건·세션 IDOR 차단) + 거짓 주석 제거
quotations 에 company_id 컬럼이 없어 견적 조회가 회사 무관하게 전부 노출되던 멀티테넌트 누수를 차단. 작성자(user_id)→users.company_id 서브쿼리로 회사 스코프를 걸어 목록(/list)과 단건·세션·협력사 엔드포인트(_fetch 가드)에서 남의 회사 견적을 NOT_FOUND 처리. 스케줄러/내부 호출은 company_id 없이 우회. 라우터/서비스의 '회사 스코핑 안 함' 거짓 주석 정정.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:01:27 +09:00
7b7a37ee98 [feat] negodata: 견적 목록 작성자 컬럼·내 견적 필터 + 검색바 클리어(X) 버튼
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 10:07:46 +09:00
33ce82997a [refactor] negodata: 목표가 산정내역을 백엔드 /target-breakdown 엔드포인트로 이관
- 생성(_calc_target_price)과 표시(get_target_breakdown)가 _candidates 헬퍼를 공유
  → 프론트 재계산 제거, 저장된 목표가와 항상 일치
- TargetPriceModal: useGetTargetBreakdown 으로 후보·채택·앵커링가 '표시만'
- QuotationType.is_new() 한 곳에서 신규유형 판단(생성·산정 공통)
- enum 라벨 단일출처(lib/enumLabels.ts): 협력사유형/카드 사용구분/유저상태
  members·CardFormSheet 는 거기서 re-export

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:40:30 +09:00
019f3dbeac [feat] negodata: 견적 목표가 산정 개편 + 협상 초청메일 + 카드 사용구분
[견적·목표가]
- 목표가 산정 KTC 기준 정비(MD 우선, 없으면 인터넷최저가·매입가·판매가 중 최저 / 신규는 인터넷최저가만)
- 인터넷 평균 수수료 0.078 상수화(견적설정 컬럼 제거)
- 앵커링가 세션 저장 + 목표가 클릭 시 산정내역 모달
- 재생성 시 목표가·앵커링가 재계산 없이 직전 값 상속(KTC)
- MD 제시가·협력사 유형(유통·제조·총판·기타) 입력

[협상카드]
- 사용 구분(공통/신규견적전용/재견적전용)

[협상 진행]
- 협력사 초청 메일 발송(일괄/개별·재발송, 발송상태 표시)

[기타]
- 견적상세 창 닫기 버그 수정, 불필요한 컬럼 주석 정리
- DB: 기존 DB는 postgres-init/04-alter.sql 적용 필요(자동 마이그레이션 없음)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 13:33:08 +09:00
b42d084063 [feat] negodata/front: 상품 UI 보강 — 매입가·판매가 입력 등
상품 등록/목록·엑셀 업로드 화면에 매입가·판매가 등 가격 필드 반영.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 13:32:13 +09:00
a40257ddc1 [feat] negodata: 회원관리 / 최고관리자(OWNER)
- OWNER 권한 신설, 최고관리자가 자기 회사 소속 직원(USER) 계정 생성·수정·삭제 관리
- 회사 사용자 API(/v1/company/user/*), 회원관리 페이지·폼
- 로그인/프로필 흐름 정비, 무인증 계정생성(/auth/create) 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 13:31:32 +09:00
103c80046e [feat] negodata: 신규 견적유형 + 목표가·앵커링 산정 + 협력사유형 + 협상카드 용도
- 견적유형 4종: QuotationType 에 신규협상(3)·신규견적(4) 추가(1·2 재 고정)
- 목표가 산정: items(매입가·판매가·인터넷최저가) + quotations.md_price +
  quotation_settings.internet_average_fee 로 _calc_target_price(KTC 이식, 신규/재 분리),
  세션 target_price·target_anchoring_price 저장
- 협력사유형: quotations.supplier_type(SupplierType) + 직전유형 조회 API(/supplier/{id}/last-type)
- 협상카드 용도: nego_cards/wild_cards.usage_type(CardUsageType: 공통/신규견적전용/재견적전용)
- 스키마(01-schema.sql)·ORM·protocol·service·crud·프론트(생성타입/폼) 일괄, /backend enums·models 미러

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 17:09:14 +09:00
21e9f71f53 [feat] negodata/front: 상품·협력사 엑셀 업로드 양식 정비
업로드 모달/툴바를 드롭다운(일괄 업로드 + 양식 다운로드)으로 정리,
UPLOAD_COLUMNS 단일 정의 공유, CSV 템플릿(헤더+예시행) 추가. dropdown-menu UI 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 17:08:43 +09:00
0c6a002e29 [fix] negodata/backend: 시각 컬럼 timestamptz 전환 + naive 변환 제거
- models.py: created_at/updated_at/last_accessed_at/start_time/end_time/bid_at
  DateTime → DateTime(timezone=True)
- quotation_service: tz-aware 입력을 naive 로 깎던 _naive_utc 제거(컬럼이 tz-aware라 불필요),
  start_time/end_time 직접 사용. negosium_db 는 UTC 고정.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:30:33 +09:00
aed8364b40 Merge remote-tracking branch 'origin/main' into feature/negodata 2026-06-26 14:30:29 +09:00
7e0f88ca03 [fix] 견적 자동마감 동시성·정합성 + 프론트 안정화 (코드리뷰 후속)
백엔드 close_and_decide 경로:
- 동시 이중 마감 가드: 마감 판정 전 원자적 CLOSED 선점(claim)으로 두 크론 잡·수동마감 경합 직렬화
- 재생성 실패 표면화: regenerate_next_round 결과 검사 → 실패 시 REGEN_FAILED 반환(체인 끊김 은폐 방지)
- 차수 충돌 방지: 다음 라운드 = 체인 최신 round+1(chain_max_round 기준)
- 재생성 사유 집계 정밀화: 미참여/동가를 양성 표식으로 구분(단독낙찰·거부 오집계 제거)
- 재생성 라운드 최소 협상기간 하한(즉시 재마감 캐스케이드 방지)
- 잡 루프 per-item 예외 격리(한 건 실패가 배치 전체를 멈추지 않음)

프론트:
- useChatController: 무권한 가드를 sessionId 별로 추적해 세션 변경 시 자연 해제
- useScrollLock: 마지막 해제를 rAF 로 지연해 재마운트 사이 일시적 잠금 해제 방지
- quotation 상세 쿼리 placeholderData 로 라운드 전환 중 시트 유지

테스트:
- 신규 test_close_and_decide_fixes.py(동시성·차수·집계·기간 하한 검증)
- conftest 결함 수정(존재하지 않는 tbl_account TRUNCATE 제거, companies.status 명시)
- stale 테스트 갱신(test_quotation_create 를 타입드 Req/신규 응답 형식에 맞게 재작성)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:22:21 +09:00
05fe0de5ce [feat] negodata/front: 협상 채팅·견적 리스트 개선 및 404 페이지 추가
- 협상 채팅 말풍선에서 협력사 제시가 마스킹 + 챗버블 컴포넌트 분리
- 견적 리스트에 재생성된 다음 차수 표시
- 매칭 없는 경로용 404 폴백 페이지 추가
- 견적 재생성 모달 너비 확대

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:50:36 +09:00
9f947e4823 [feat] negodata/front: 견적 재생성·차수 체인 UI + 담당자/메모 + 스크롤·목업 정비
- 견적 재생성(다음 견적) 엔드포인트 연동 모달: 연결 공급사+협상단계 표시, 마지막 차수에서만
- 차수 체인 타임라인/견적번호 클릭 필터, 생성 응답 슬림(qt_id/session_count) 반영
- 상세 시트 스크롤 수정(min-h-0)·배경 스크롤 락(useScrollLock)·헤더2:탭1 비율
- 담당자=로그인 유저(store) 자동 전송 + 생성 모달 메모 입력 추가
- 목업/가짜 폴백 제거(담당자·견적명·번호·메모 → '-'), 시드 회사 placeholder UUID→실 UUID
- 견적상태 CREATED 칩 pulse 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 16:57:29 +09:00
7f948075e5 [feat] negodata/backend: 견적 자동마감 스케줄러 + 다음 라운드 재생성(자동/수동)
- APScheduler 크론(KST 5분): 마감일 지난 견적 / 모든 세션 종결 견적 → close_and_decide
- 마감 판정: 단독 최저가 낙찰 확정 / 동가·전원미참여 다음 라운드 재생성 / 거부·한도 마감
- 재생성: 같은 견적번호 체인(round+1, 이름 '(N차)'), 공급사 수로 재협상·재견적, 사유별 한도(미참여1+동가1)
- 수동 재생성 POST /regenerate/{qt_id} (마지막 차수만 허용)
- CloseOutcome enum + 잡 결과별 로그

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 16:36:22 +09:00
d47476dc04 [refactor] negodata/backend: 서비스 계층 입력을 dict→타입드 Req 패킷으로 통일
라우터가 req.model_dump(exclude_unset=True)로 dict를 만들어 넘기던 것을
req(Req_*) 객체 그대로 전달하도록 변경. 서비스 시그니처를 전부 타입드로 통일.

- create 5개(quotation/card/item/supplier/quotation_setting): ORM은 명시 kwargs
  조립(item만 컬럼 16개라 model_dump 펼침). 경계 검증·타입 유지, **data 결합 제거.
- update 4개: 서비스가 req 받아 내부에서 model_dump(exclude_unset=True) 생성 후
  CRUD(dict)로 전달 — 공식 PATCH 메커니즘 유지.
- 서비스 계층 data: dict 시그니처 0개. CRUD는 dict 유지(의도).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 09:27:04 +09:00
5406094ea1 [wip] negodata/backend: 견적 마감 스케줄러
- scheduler/(__init__·jobs): 마감 처리·낙찰자 선정 잡
- quotation_crud·service: 스케줄러 연동 조회/마감 로직
- web_main·requirements·docker-compose·config: 스케줄러 기동 설정

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 17:38:02 +09:00
ae103fd0f1 [refactor] negodata/front: 전역 타입 정리 — 공유(2+) 타입만 글로벌, 중복 제거
- src/types.ts: DB 미러 중복 제거, generated DTO 기반 별칭만(Product/Partner/NegotiationCard/PageType)
- products/partners 의 로컬 Product/Partner 중복 → 글로벌 단일 정본 재수출

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 17:06:37 +09:00
eb6612af1c [refactor] negodata: 도메인 코드값 enum 정비 — 백엔드 정본 + 프론트 생성타입 사용
- 백엔드: 도메인 enum CodeEnum 상속(x-enum-varnames) → orval이 이름 있는 enum 생성. 응답 필드 enum 타입 지정(요청은 int 유지). ENUM_LABELS/DOMAIN_ENUMS·/v1/enums 제거
- 프론트: 생성 enum + 라벨맵으로 교체(매직넘버·발명 어휘 제거), 견적·세션 상태 코드화, role/delivery 라벨 프론트 소유
- 동반 정리: unwrap 캐스트·목업(buildSessions·데모)·死코드 제거, mapItem toMinPrice dedupe

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 17:06:26 +09:00
6fa024137f [feat] negodata: 견적·카드 검색/필터 서버사이드 전환 + 견적 상세 Sheet 리팩터
- 백엔드: quotation 목록 search 파라미터(name/number ILIKE) 추가; card 목록 is_wildcard 탭 필터 + total_nego/total_wild 탭 카운트 추가; status/type 코드 int 비교 보정
- 프론트: cards/quotation 검색·상태·유형 필터·페이지네이션을 useServerList 기반 서버사이드로 전환; 엔터 즉시검색 + 디바운스 500ms·최소 2글자; 죽은 client 필터 훅(useCardFilters/useQuotationFilters) 제거
- generated 파라미터/응답 타입 손보강(listCardsParams.is_wildcard, listQuotationsParams.search, resCardList.total_nego/total_wild) — orval 재생성 시 동일
- 견적 상세 Drawer→Sheet 분리, useOverlayParams→useOverlayRouter, CreateQuotationWizard→QuotationCreateModal

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 17:03:53 +09:00
b65de5a5f2 Merge branch 'main' of https://gitea.o2o.kr/Negosium/o2o-negosium 2026-06-18 18:59:08 +09:00
cb3adf6d2e 버그 수정중 . 2026-06-18 18:59:06 +09:00
b45889ec0a [feat] negodata: chats.meta 컬럼 추가 + 견적생성 에러 응답패킷 규약대로 표기
- negotiation.chats 에 meta(JSONB) 추가 — 말풍선 표현 데이터(script/step/input_mode 등)
- 견적생성 실패 토스트를 result.code/desc + msg 규약대로 표시(HTTP 200·success=false 포함)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:45:17 +09:00
06c2481fe7 [fix] negodata/front: 세션상태 라벨을 SHARED_ENUMS.md 5-state로 정렬
- SESSION_STATUS_LABEL 옛 3-state(1협상중·2협상종료·3협상거부) → 정본 5-state
  (1협상생성·2협상중·3협상완료·4미참여·5협상거부, 서버 /v1/enums·백엔드 SessionStatus 미러)
- 코드→라벨 단일 진입점 sessionStatusLabel() 추가, 드로어 인라인 매핑 제거
- '협상종료' 표기 → '협상완료'(DONE) 통일, ChatSession status 유니온 정렬

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:43:20 +09:00
088c57f415 Merge branch 'main' of https://gitea.o2o.kr/Negosium/o2o-negosium
# Conflicts:
#	postgres-init/03-seed-negodata.sql
2026-06-18 17:53:03 +09:00