o2o-negosium-original/negodata/backend/common/enums.py
Mina Choi 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

216 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from enum import Enum, auto
from fastapi import HTTPException
class CodeEnum(Enum):
"""OpenAPI 스키마에 x-enum-varnames(멤버 이름)을 실어 orval 이 이름 있는 enum 을 생성하게 하는 베이스."""
@classmethod
def __get_pydantic_json_schema__(cls, core_schema, handler):
json_schema = handler(core_schema)
json_schema = handler.resolve_ref_schema(json_schema)
json_schema["x-enum-varnames"] = [m.name for m in cls]
return json_schema
class ErrorType(Enum):
"""서버 전역 결과 코드. Res_WebPacketProtocol.result 에 담겨 클라이언트로 전달된다.
HTTP status 와 겹치지 않도록 구간을 분리해서 관리한다.
"""
SUCCESS = 0
FAIL = 1
# DB / Redis 에러
DB_RUN_FAILED = 10
DB_ALREADY_SAME_KEY = auto()
DB_INVALID_KEY = auto()
DB_EMPTY_DATA = auto()
DB_INVALID_TYPE = auto()
# 요청/직렬화 에러
JSON_PARSE_ERROR = 100
INVALID_REQUEST_DATA = auto()
INTERNAL_EXCEPTION = auto()
# http 에러 코드와 겹치지 않게 설정 - router 전용 예외 발생 옵션
HTTP_FORBIDDEN = 403
HTTP_INVALID_CLIENT_REQUEST = 419
HTTP_TO_MANY_REQUEST = 429
HTTP_INVALID_CLIENT_ACCESS = 433
HTTP_ACCESS_TOKEN_EXPIRED = 434
HTTP_REFRESH_TOKEN_EXPIRED = 435
HTTP_INVALID_TOKEN_ACCESS = 436
# 계정 관련 에러
ACCOUNT_INVALID_INFO = 1200
ACCOUNT_ALREADY_EXIST = auto()
ACCOUNT_BLOCKED_USER = auto()
ACCOUNT_NOT_FOUND = auto()
ACCOUNT_FORBIDDEN = auto() # 최고관리자 외 접근 / 다른 회사·최고관리자 대상 변경 시도
# 상품 관련 에러
ITEM_NOT_FOUND = 1300
ITEM_CODE_DUPLICATE = auto()
# 협력사 관련 에러
SUPPLIER_NOT_FOUND = 1400
SUPPLIER_CODE_DUPLICATE = auto()
# 견적 관련 에러
QUOTATION_NOT_FOUND = 1500
QUOTATION_NOT_LATEST_ROUND = auto() # 마지막 차수가 아닌 견적을 재생성하려 함
QUOTATION_TARGET_PRICE_UNAVAILABLE = auto() # md_price·인터넷최저가 둘 다 없어 목표가 산정 불가
# 견적 설정 관련 에러
QUOTATION_SETTING_NOT_FOUND = 1600
# 협상카드 관련 에러
CARD_NOT_FOUND = 1700
# 이미지 업로드 관련 에러
IMAGE_INVALID_TYPE = 1800
IMAGE_TOO_LARGE = auto()
IMAGE_UPLOAD_FAILED = auto()
# 초청 메일 발송 관련 에러
EMAIL_NOT_CONFIGURED = 1900 # ACS/SMTP 둘 다 미설정 — 발송 불가(설정 필요)
EMAIL_SEND_FAILED = auto() # 발송 시도했으나 전부 실패(수신자 0 성공)
# ErrorType 의 HTTP_* 값과 status_code 를 맞춰 router 단에서 raise 한다.
EXCEPTION_FORBIDDEN = HTTPException(status_code=ErrorType.HTTP_FORBIDDEN.value, detail=ErrorType.HTTP_FORBIDDEN.name)
EXCEPTION_INVALID_CLIENT_REQUEST = HTTPException(status_code=ErrorType.HTTP_INVALID_CLIENT_REQUEST.value, detail=ErrorType.HTTP_INVALID_CLIENT_REQUEST.name)
EXCEPTION_TO_MANY_REQUEST = HTTPException(status_code=ErrorType.HTTP_TO_MANY_REQUEST.value, detail=ErrorType.HTTP_TO_MANY_REQUEST.name)
EXCEPTION_INVALID_CLIENT_ACCESS = HTTPException(status_code=ErrorType.HTTP_INVALID_CLIENT_ACCESS.value, detail=ErrorType.HTTP_INVALID_CLIENT_ACCESS.name)
EXCEPTION_ACCESS_TOKEN_EXPIRED = HTTPException(status_code=ErrorType.HTTP_ACCESS_TOKEN_EXPIRED.value, detail=ErrorType.HTTP_ACCESS_TOKEN_EXPIRED.name)
EXCEPTION_REFRESH_TOKEN_EXPIRED = HTTPException(status_code=ErrorType.HTTP_REFRESH_TOKEN_EXPIRED.value, detail=ErrorType.HTTP_REFRESH_TOKEN_EXPIRED.name)
EXCEPTION_HTTP_INVALID_TOKEN_ACCESS = HTTPException(status_code=ErrorType.HTTP_INVALID_TOKEN_ACCESS.value, detail=ErrorType.HTTP_INVALID_TOKEN_ACCESS.name)
class DBType(Enum):
"""논리 DB 구분. 모델마다 DBType() 으로 자신이 속한 DB 를 반환한다.
DB 가 늘어나면 여기에 추가하고 db_session_manager 의 맵에 등록만 하면 된다.
"""
MAIN = 1
class DBWRType(Enum):
"""Read / Write 접속 구분. 조회는 DB_READ, 변경은 DB_WRITE 엔진을 사용한다."""
DB_READ = 1
DB_WRITE = 2
# 도메인 코드값
class UserStatus(CodeEnum):
"""users.status 코드값."""
ACTIVE = 1
INACTIVE = 2
class UserRole(CodeEnum):
"""users.role 코드값. negodata 유저는 전부 회사 직원(관리자측) —
의미 있는 구분은 '직원 계정 관리 권한 유무' 하나뿐이라 2단계로 둔다.
1=일반, 2=최고관리자(직원 계정 생성·관리)."""
USER = 1
OWNER = 2 # 최고관리자: 자기 회사 유저(직원 계정)를 생성·관리
class CompanyStatus(CodeEnum):
"""companies.status 코드값."""
ACTIVE = 1
INACTIVE = 2
class QuotationType(CodeEnum):
"""quotations.type 코드값. 신규/재 × 협상(1:1)/견적(1:N).
1=재협상(1:1), 2=재견적(1:N), 3=신규협상(1:1), 4=신규견적(1:N).
기존 데이터 보존 위해 재협상/재견적 코드(1·2)는 고정, 신규는 3·4로 추가."""
RENEGO = 1 # 재협상(1:1)
REQUOTE = 2 # 재견적(1:N)
NEW_NEGO = 3 # 신규협상(1:1)
NEW_QUOTE = 4 # 신규견적(1:N)
class QuotationStatus(CodeEnum):
"""quotations.status 코드값(SMALLINT). 프론트 견적상태 뱃지와 매핑된다."""
CREATED = 1
ACTIVE = 2
CLOSED = 3
ON_HOLD = 4
class SessionStatus(CodeEnum):
"""negotiation.sessions.status 코드값. 협력사별 협상 세션 진행 상태."""
CREATED = 1
IN_PROGRESS = 2
DONE = 3
NOT_PARTICIPATED = 4
REJECTED = 5
class CloseOutcome(Enum):
"""견적 마감 판정 결과(close_and_decide 반환값). 내부 제어·로그용 — DB 저장/프론트 노출 안 함."""
AWARDED = "awarded" # 단독 낙찰 확정
REGENERATED = "regenerated" # 다음 라운드 재생성
CLOSED = "closed" # 그냥 마감 (선점 실패로 이미 닫혀 있던 경우 포함)
REGEN_FAILED = "regen_failed" # 재생성 시도했으나 실패 — 원본은 CLOSED 인데 다음 라운드가 없음(체인 끊김, 모니터링 필요)
class ChatSender(CodeEnum):
"""negotiation.chats.sender 코드값. 채팅 발신 주체."""
BOT = 1
USER = 2
class DeliveryType(CodeEnum):
"""items.delivery_type 코드값. 협상 채팅의 배송형태 선택지와 동일 집합."""
PARTNER = 1 # 협력사배송
COURIER = 2 # 지정택배배송
PICKUP = 3 # 픽업배송
class CardStatus(CodeEnum):
"""nego_cards.status 코드값. 와일드카드의 협상 적용 여부(수동 승인). 일반 협상카드는 상시 ACTIVE."""
ACTIVE = 1
INACTIVE = 2
class CardType(CodeEnum):
"""negotiation.chats.card_type / quotation_cards.type 코드값. 1=nego_card, 2=wild_card."""
NEGO = 1
WILD = 2
class SupplierType(CodeEnum):
"""quotations.supplier_type 코드값. 없음(0,미지정)/유통(1)/제조(2)/총판(3).
없음은 프론트 폼에 '없음'으로 노출. KTC 앵커링 코드(기타=0)와 매핑 시 0↔없음 대응."""
NONE = 0 # 없음(미지정)
DISTRIBUTION = 1 # 유통
MANUFACTURE = 2 # 제조
SOLE_AGENCY = 3 # 총판
class CardUsageType(CodeEnum):
"""nego_cards/wild_cards.usage_type 코드값. 협상카드 사용 범위(신규/재 견적·협상 양쪽 적용).
공통=모두 적용(기본), 신규견적전용, 재견적전용."""
COMMON = 1 # 공통(모두) — 기본
NEW = 2 # 신규견적전용
REUSE = 3 # 재견적전용