- 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>
121 lines
9.6 KiB
SQL
121 lines
9.6 KiB
SQL
-- 기존 DB ALTER 누적 파일. 새 컬럼/변경은 이 파일에 계속 append 한다.
|
|
-- 전부 IF NOT EXISTS 라 몇 번을 재실행해도 안전(돌리면 최신 상태로 맞춰짐).
|
|
-- 신규/리셋 DB 는 01-schema*.sql 에 이미 반영돼 있어 이 파일이 필요 없다.
|
|
|
|
-- ───────────────────────────────────────────────────────────
|
|
-- [2026-06-26] 견적 개편: 가격(매입/판매)·수수료율·앵커링가 + 견적/카드/협력사 분류 컬럼
|
|
-- ───────────────────────────────────────────────────────────
|
|
|
|
-- 상품: 인터넷최저가 실값 + 매입가 + 판매가
|
|
ALTER TABLE partner.items
|
|
ADD COLUMN IF NOT EXISTS internet_lowest_price BIGINT,
|
|
ADD COLUMN IF NOT EXISTS purchase_price BIGINT,
|
|
ADD COLUMN IF NOT EXISTS selling_price BIGINT;
|
|
|
|
-- 세션: 앵커링가
|
|
ALTER TABLE negotiation.sessions
|
|
ADD COLUMN IF NOT EXISTS anchoring_price BIGINT;
|
|
|
|
-- 견적: MD 제시가 + 협력사(공급채널) 유형
|
|
ALTER TABLE quotation.quotations
|
|
ADD COLUMN IF NOT EXISTS md_price BIGINT,
|
|
ADD COLUMN IF NOT EXISTS supplier_type SMALLINT;
|
|
|
|
-- 카드: 사용 범위 구분(CardUsageType): 1=공통 2=신규견적전용 3=재견적전용
|
|
ALTER TABLE card.nego_cards
|
|
ADD COLUMN IF NOT EXISTS usage_type SMALLINT NOT NULL DEFAULT 1;
|
|
ALTER TABLE card.wild_cards
|
|
ADD COLUMN IF NOT EXISTS usage_type SMALLINT NOT NULL DEFAULT 1;
|
|
|
|
-- ───────────────────────────────────────────────────────────
|
|
-- [2026-06-29] 협상 초청 메일: 세션별 발송 시각(수동 발송 버튼이 채움)
|
|
-- ───────────────────────────────────────────────────────────
|
|
ALTER TABLE negotiation.sessions
|
|
ADD COLUMN IF NOT EXISTS email_sent_at TIMESTAMPTZ;
|
|
|
|
-- ───────────────────────────────────────────────────────────
|
|
-- [2026-06-30] 알림(인박스): 협상 이벤트를 견적 작성자에게 통지
|
|
-- ───────────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS company.notifications (
|
|
notification_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id uuid NOT NULL, -- 수신자(company.users.user_id) = 견적 작성자
|
|
type SMALLINT NOT NULL, -- 알림 유형(NotificationType): 1=success(낙찰), 2=regenerated(재생성), 3=failure(결렬)
|
|
ref_qt_id uuid NULL, -- 관련 견적(quotation.quotations.qt_id)
|
|
ref_session_id uuid NULL, -- 관련 세션(negotiation.sessions.session_id)
|
|
data JSONB NULL, -- 렌더 스냅샷(유형별)
|
|
read_at TIMESTAMPTZ NULL, -- 읽은 시각(NULL=안읽음)
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
deleted BOOLEAN NOT NULL DEFAULT FALSE
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON company.notifications (user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_notifications_user_unread ON company.notifications (user_id, created_at) WHERE deleted = FALSE AND read_at IS NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_notifications_ref_qt_id ON company.notifications (ref_qt_id);
|
|
|
|
-- ─────────────────────────────────────────────────────────────
|
|
-- [2026-07-02] 앵커링 v1.2 — sessions 판정·마킹 컬럼 3종
|
|
-- (신규 DB 는 01-schema*.sql 에 반영됨. anchoring 스키마 자체(adjustments·뷰)는
|
|
-- 05-anchoring-schema.sql 로 적용 — 여기엔 두지 않는다.)
|
|
ALTER TABLE negotiation.sessions
|
|
ADD COLUMN IF NOT EXISTS anchoring_value SMALLINT NULL, -- 제안 당시 앵커링 값(천분율‰) 박제
|
|
ADD COLUMN IF NOT EXISTS last_offer_price BIGINT NULL, -- 협력사 마지막 제시가(가격 흔적)
|
|
ADD COLUMN IF NOT EXISTS used_by_adjustment_id BIGINT NULL; -- 앵커링 배치 소비 마킹
|
|
|
|
-- ─────────────────────────────────────────────────────────────
|
|
-- [2026-07-02] 마감 close_reason 개편 — 견적 마감사유 + 가격정책 3구간
|
|
-- (신규 DB 는 01-schema*.sql 에 반영됨.)
|
|
ALTER TABLE quotation.quotations
|
|
ADD COLUMN IF NOT EXISTS close_reason SMALLINT NULL; -- 마감 사유(CloseReason 1~8), 미마감이면 NULL
|
|
ALTER TABLE quotation.quotation_settings
|
|
ADD COLUMN IF NOT EXISTS mid_action SMALLINT NOT NULL DEFAULT 1, -- 가격정책(PriceGateAction): 앵커링가<투찰가≤목표가 처리
|
|
ADD COLUMN IF NOT EXISTS over_action SMALLINT NOT NULL DEFAULT 1, -- 가격정책(PriceGateAction): 목표가<투찰가 처리
|
|
ADD COLUMN IF NOT EXISTS regen_limit SMALLINT NOT NULL DEFAULT 1; -- 재생성 최대 횟수(체인 전체 총합, 사유 무관)
|
|
|
|
-- ─────────────────────────────────────────────────────────────
|
|
-- [2026-07-06] 낙찰 기준 견적 단위 이관 + 마감 개편(개찰 모델) — quotations(견적 행)에 낙찰 기준 mid/over 미러.
|
|
-- 마감 판정이 견적 행에서 읽는다. 기준 미달/동가/거부/미응찰은 결렬(유찰)이 아니라 개찰(낙찰자 미정 마감).
|
|
-- 자동 재협상/재생성 폐지 → regen_limit 제거(설정 템플릿 quotation_settings.regen_limit 은 미사용 잔존).
|
|
-- (신규 DB 는 01-schema*.sql 에 반영됨. 기존 행은 DEFAULT 1=AWARD 로 백필.)
|
|
ALTER TABLE quotation.quotations
|
|
ADD COLUMN IF NOT EXISTS mid_action SMALLINT NOT NULL DEFAULT 1, -- 낙찰 기준(PriceGateAction 1=낙찰/2=개찰): 앵커링가<투찰가≤목표가 처리
|
|
ADD COLUMN IF NOT EXISTS over_action SMALLINT NOT NULL DEFAULT 1; -- 낙찰 기준(PriceGateAction 1=낙찰/2=개찰): 목표가<투찰가 처리(1:1 협상은 항상 개찰)
|
|
ALTER TABLE quotation.quotations
|
|
DROP COLUMN IF EXISTS regen_limit; -- 자동 재생성 폐지로 제거(먼저 추가됐던 dev DB 대비 멱등 DROP)
|
|
|
|
-- 폐기된 close_reason 코드(구 REGEN_* 2~4) → 개찰(OPEN_*) 로 이관. 미이관 시 QuotationData(CloseReason enum) 검증 실패로 견적 목록 500.
|
|
UPDATE quotation.quotations SET close_reason = 5 WHERE close_reason = 2; -- REGEN_PRICE → OPEN_PRICE
|
|
UPDATE quotation.quotations SET close_reason = 6 WHERE close_reason = 3; -- REGEN_EQUAL → OPEN_EQUAL
|
|
UPDATE quotation.quotations SET close_reason = 7 WHERE close_reason = 4; -- REGEN_NOSHOW → OPEN_NOSHOW
|
|
|
|
-- quotation_settings 정리 — 낙찰 정책(mid/over/regen)은 견적 단위 이관, 앵커링은 칸 rate(v1.2)로 대체 → 세팅 컬럼 제거.
|
|
-- 미제거 시 QuotationSettingData(PriceGateAction enum, 값 3=옛 FAIL) 검증 실패로 견적 세팅 목록 500.
|
|
ALTER TABLE quotation.quotation_settings
|
|
DROP COLUMN IF EXISTS mid_action,
|
|
DROP COLUMN IF EXISTS over_action,
|
|
DROP COLUMN IF EXISTS regen_limit,
|
|
DROP COLUMN IF EXISTS anchoring_value;
|
|
|
|
-- ─────────────────────────────────────────────────────────────
|
|
-- [2026-07-06] 협력사: 총매출액 추가 + 우선선정(priority) 제거.
|
|
-- priority(우선순위 문자열)와 그 파생 등급(rank)은 폐지 — 협력사에서 완전 제거.
|
|
ALTER TABLE partner.suppliers
|
|
ADD COLUMN IF NOT EXISTS total_revenue BIGINT; -- 총매출액(원, KTC total_revenue 미러)
|
|
ALTER TABLE partner.suppliers
|
|
DROP COLUMN IF EXISTS priority;
|
|
|
|
-- ─────────────────────────────────────────────────────────────
|
|
-- [2026-07-07] 협력사 취급상품 매핑: (협력사, 상품) + 공급유형(SupplierType).
|
|
-- (신규 DB 는 01-schema*.sql 에 반영됨.)
|
|
CREATE TABLE IF NOT EXISTS partner.supplier_items (
|
|
supplier_item_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
supplier_id uuid NOT NULL, -- 협력사(partner.suppliers.supplier_id)
|
|
item_id uuid NOT NULL, -- 상품(partner.items.item_id)
|
|
supply_type SMALLINT NOT NULL DEFAULT 0, -- 공급 유형(SupplierType): 0=없음/1=유통/2=제조/3=총판
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
deleted BOOLEAN NOT NULL DEFAULT FALSE
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_supplier_items_supplier_id ON partner.supplier_items (supplier_id);
|
|
CREATE INDEX IF NOT EXISTS idx_supplier_items_item_id ON partner.supplier_items (item_id);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_supplier_items ON partner.supplier_items (supplier_id, item_id) WHERE deleted = FALSE;
|