- item/supplier/quotation/quotation_setting CRUD·service·router 추가 - item protocol delivery_type str→int (ERD/스키마 SMALLINT 일치) - DeliveryType enum + 한글 라벨, 공용 GET /v1/enums (도메인 코드 메타데이터) - CompanyBrief → CompanyData 로 *Data 네이밍 통일 - CORS: WebServerConfig.client_url(단일) 도입 (config_models/router) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
162 lines
7.5 KiB
Python
162 lines
7.5 KiB
Python
import uuid
|
|
|
|
from sqlalchemy.orm import declarative_base
|
|
from sqlalchemy import Column, Integer, SmallInteger, BigInteger, Numeric, Float, String, Boolean, DateTime
|
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
|
from sqlalchemy.sql import text
|
|
|
|
from common.enums import DBType, UserStatus, UserRole, CompanyStatus
|
|
|
|
# 모든 ORM 모델의 베이스. insert 시 isinstance 체크에도 사용된다.
|
|
MAIN_BASE = declarative_base()
|
|
|
|
|
|
# 공통 mixin
|
|
# DB 계약(_DBTypeMixin)과 ERD 공통 컬럼(MainTableMixin)을 분리해 둔다.
|
|
def _utc_now_sql():
|
|
return text("(now() AT TIME ZONE 'utc')")
|
|
|
|
|
|
class _DBTypeMixin:
|
|
"""모델이 자신이 속한 논리 DB 를 알려준다 (람다 실행 시 DBType 으로 세션 선택)."""
|
|
|
|
@staticmethod
|
|
def DBType():
|
|
return DBType.MAIN.value
|
|
|
|
|
|
# ERD 공통 컬럼(created/updated/deleted). negodata 도메인 테이블은 단일 MAIN DB 에서 soft-delete 를 쓴다.
|
|
class MainTableMixin(_DBTypeMixin):
|
|
created_at = Column(DateTime, nullable=False, server_default=_utc_now_sql())
|
|
updated_at = Column(DateTime, nullable=False, server_default=_utc_now_sql(), onupdate=_utc_now_sql())
|
|
deleted = Column(Boolean, nullable=False, server_default=text("false"), default=False)
|
|
|
|
|
|
# 스캐폴드 예시 계정 (negosium 골격 유산)
|
|
# negodata 인증은 ERD 의 users/companies 를 쓴다(아래). tbl_account 는 스캐폴드
|
|
# 테스트 호환을 위해 남겨둔다 (negodata_db 의 postgres-init 이 생성).
|
|
class tbl_account(_DBTypeMixin, MAIN_BASE):
|
|
__tablename__ = "tbl_account"
|
|
|
|
uid = Column(Integer, primary_key=True, autoincrement=True)
|
|
id = Column(String(45), nullable=False, unique=True)
|
|
pw = Column(String(255), nullable=False, default="")
|
|
nickname = Column(String(45), nullable=False, default="")
|
|
is_blocked = Column(Boolean, nullable=False, default=False)
|
|
last_login_at = Column(DateTime, nullable=False, server_default=_utc_now_sql())
|
|
create_at = Column(DateTime, server_default=_utc_now_sql())
|
|
|
|
|
|
# ERD 도메인 모델 (negosium.sql 기준, UUID PK)
|
|
class companies(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "companies"
|
|
|
|
company_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
name = Column(String(100), nullable=False)
|
|
business_number = Column(String(30), nullable=True)
|
|
code = Column(Integer, nullable=True) # 내부 인덱스용
|
|
representative_name = Column(String(50), nullable=True)
|
|
email = Column(String(255), nullable=True)
|
|
contact_number = Column(String(20), nullable=True)
|
|
website_url = Column(String(255), nullable=True)
|
|
industry = Column(SmallInteger, nullable=True) # 업종 코드 (스키마 SMALLINT)
|
|
status = Column(SmallInteger, nullable=False, default=CompanyStatus.ACTIVE.value) # CompanyStatus
|
|
|
|
|
|
class users(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "users"
|
|
|
|
user_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
company_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
id = Column(String(20), nullable=False, unique=True, index=True) # 로그인 아이디
|
|
password = Column(String(255), nullable=False) # bcrypt 해시 (ERD VARCHAR(30)→255 확장)
|
|
name = Column(String(50), nullable=True)
|
|
email = Column(String(255), nullable=True)
|
|
contact_number = Column(String(20), nullable=True)
|
|
last_accessed_at = Column(DateTime, nullable=False, server_default=_utc_now_sql())
|
|
status = Column(SmallInteger, nullable=False, default=UserStatus.ACTIVE.value) # UserStatus
|
|
role = Column(SmallInteger, nullable=False, default=UserRole.USER.value) # UserRole
|
|
|
|
|
|
class items(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "items"
|
|
|
|
item_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
company_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
user_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 등록한 유저
|
|
|
|
name = Column(String(100), nullable=False)
|
|
code = Column(String(30), nullable=True)
|
|
category = Column(String(255), nullable=True)
|
|
category_type = Column(Integer, nullable=False, default=1) # 카테고리 탐색용 자동 증가 숫자
|
|
|
|
image_url = Column(String(255), nullable=True)
|
|
model_name = Column(String(100), nullable=True)
|
|
spec = Column(String(255), nullable=True)
|
|
manufacturer = Column(String(50), nullable=True)
|
|
made_in = Column(String(100), nullable=True)
|
|
|
|
price = Column(BigInteger, nullable=True) # 금액(원), 스키마 BIGINT
|
|
internet_lowest_price_yn = Column(Boolean, nullable=False, default=False) # 최저가 솔루션 원자성 보존용
|
|
|
|
moq = Column(String(50), nullable=True) # 최소 주문 수량
|
|
lead_time = Column(SmallInteger, nullable=True) # 주문 후 배송 도착까지 시간
|
|
quantity_unit = Column(String(50), nullable=True) # 단위 라벨(자유입력): EA/BOX/SET/ROLL ...
|
|
delivery_type = Column(SmallInteger, nullable=True) # 배송 유형 코드
|
|
vat_yn = Column(Boolean, nullable=True)
|
|
delivery_fee_yn = Column(Boolean, nullable=True)
|
|
|
|
|
|
class suppliers(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "suppliers"
|
|
|
|
supplier_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
company_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
user_id = Column(UUID(as_uuid=True), nullable=False, index=True) # 등록한 유저
|
|
|
|
name = Column(String(100), nullable=False)
|
|
code = Column(String(20), nullable=True)
|
|
manager_name = Column(String(50), nullable=True)
|
|
manager_email = Column(String(255), nullable=True)
|
|
manager_contact_number = Column(String(20), nullable=True) # ERD 오타(manger) 교정
|
|
priority = Column(String(10), nullable=True) # True/False 가 아닌 string value 가능
|
|
|
|
|
|
class quotation_settings(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "quotation_settings"
|
|
|
|
qt_setting_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
user_id = Column(UUID(as_uuid=True), nullable=True, index=True) # 설정 소유 유저
|
|
target_margin_rate = Column(Numeric(8, 6), nullable=False)
|
|
anchoring_value = Column(Numeric(8, 6), nullable=False, default=0.01)
|
|
card_count = Column(Integer, nullable=False, default=3) # 한 협상 내 협상카드 사용 횟수
|
|
|
|
|
|
class quotations(MainTableMixin, MAIN_BASE):
|
|
__tablename__ = "quotations"
|
|
|
|
qt_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
qt_setting_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
version_id = Column(UUID(as_uuid=True), nullable=False, index=True)
|
|
|
|
name = Column(String(50), nullable=False)
|
|
number = Column(String(30), nullable=False)
|
|
type = Column(SmallInteger, nullable=False) # QuotationType: 1=renego(1:1) / 2=requote(1:N)
|
|
round = Column(Integer, nullable=False, default=1) # 재견적 진행 시 증가
|
|
status = Column(SmallInteger, nullable=False) # 진행 상태 코드
|
|
start_time = Column(DateTime, nullable=False)
|
|
end_time = Column(DateTime, nullable=False)
|
|
|
|
manager_name = Column(String(50), nullable=True)
|
|
manager_email = Column(String(255), nullable=True)
|
|
manager_contact_number = Column(String(20), nullable=True)
|
|
memo = Column(String(100), nullable=True)
|
|
|
|
iteration = Column(Integer, nullable=False, default=0)
|
|
preferred_sp_yn = Column(Boolean, nullable=True)
|
|
preferred_sp_id = Column(UUID(as_uuid=True), nullable=True)
|
|
preferred_sp_name = Column(String(20), nullable=True)
|
|
equal_bid_yn = Column(Boolean, nullable=True)
|
|
equal_bid_data = Column(JSONB, nullable=True)
|