from abc import ABC, abstractmethod from typing import Tuple from sqlalchemy import asc, desc, func, select, update from sqlalchemy.ext.asyncio import AsyncSession from common.database.db_session_manager import DB_SESSION_MNG from common.database.model.models import items, quotations, sessions from common.enums import ErrorType from common.logger import LOG # 협상 세션 CRUD. 목록은 세션(negotiation) ⨝ 상품(partner) ⨝ 견적(quotation) 조인으로 만든다. # 마감일(qt_end_time)은 견적(quotation.end_time)이 진실값이다(session.end_time 은 협상 종료 시점 기록용). class ISessionCRUD(ABC): @abstractmethod async def list_by_supplier(self, cdb: AsyncSession, supplier_id, status, qt_type, order, offset, limit) -> Tuple[ErrorType, list]: pass @abstractmethod async def count_by_supplier(self, cdb: AsyncSession, supplier_id, status, qt_type) -> Tuple[ErrorType, int]: pass @abstractmethod async def get_session_by_id(self, cdb: AsyncSession, session_id) -> Tuple[ErrorType, sessions]: pass @abstractmethod async def get_quotation_by_id(self, cdb: AsyncSession, quotation_id) -> Tuple[ErrorType, quotations]: pass @abstractmethod async def update_session_status(self, cdb: AsyncSession, session_id, status: int) -> ErrorType: pass @abstractmethod async def update_quotation_status(self, cdb: AsyncSession, quotation_id, status: int) -> ErrorType: pass @abstractmethod async def update_session_reject(self, cdb: AsyncSession, session_id, status: int, reject_reason: str) -> ErrorType: pass class SessionCRUD(ISessionCRUD): @staticmethod def __filters(supplier_id, status, qt_type): conds = [sessions.supplier_id == supplier_id, sessions.deleted == False] # noqa: E712 if status is not None: conds.append(sessions.status == status) if qt_type is not None: conds.append(sessions.qt_type == qt_type) return conds async def list_by_supplier(self, cdb: AsyncSession, supplier_id, status, qt_type, order, offset, limit) -> Tuple[ErrorType, list]: try: conds = self.__filters(supplier_id, status, qt_type) order_col = desc(quotations.end_time) if order == "desc" else asc(quotations.end_time) query = ( select( sessions.session_id, sessions.status, sessions.qt_type, sessions.qt_number, quotations.end_time, # qt_end_time = 견적 마감 시각 items.code, items.name, items.model_name, items.manufacturer, ) .join(items, items.item_id == sessions.item_id) .join(quotations, quotations.qt_id == sessions.quotation_id) .where(*conds, items.deleted == False, quotations.deleted == False) # noqa: E712 .order_by(order_col) .offset(offset) .limit(limit) ) err_type, rows = await DB_SESSION_MNG.execute(cdb, query, "list_by_supplier failed.") if err_type != ErrorType.SUCCESS: return err_type, [] return ErrorType.SUCCESS, rows except Exception as ex: LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED, [] async def count_by_supplier(self, cdb: AsyncSession, supplier_id, status, qt_type) -> Tuple[ErrorType, int]: try: conds = self.__filters(supplier_id, status, qt_type) query = ( select(func.count()) .select_from(sessions) .join(items, items.item_id == sessions.item_id) .join(quotations, quotations.qt_id == sessions.quotation_id) .where(*conds, items.deleted == False, quotations.deleted == False) # noqa: E712 ) err_type, rows = await DB_SESSION_MNG.execute(cdb, query, "count_by_supplier failed.") if err_type != ErrorType.SUCCESS: return err_type, 0 return ErrorType.SUCCESS, (rows[0] if rows else 0) except Exception as ex: LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED, 0 async def get_session_by_id(self, cdb: AsyncSession, session_id) -> Tuple[ErrorType, sessions]: try: query = select(sessions).where(sessions.session_id == session_id, sessions.deleted == False).limit(1) # noqa: E712 err_type, row_list = await DB_SESSION_MNG.execute(cdb, query, f"get_session_by_id({session_id}) failed.") if err_type != ErrorType.SUCCESS: return err_type, None if len(row_list) != 1: return ErrorType.DB_INVALID_KEY, None return ErrorType.SUCCESS, row_list[0] except Exception as ex: LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED, None async def get_quotation_by_id(self, cdb: AsyncSession, quotation_id) -> Tuple[ErrorType, quotations]: try: query = select(quotations).where(quotations.qt_id == quotation_id, quotations.deleted == False).limit(1) # noqa: E712 err_type, row_list = await DB_SESSION_MNG.execute(cdb, query, f"get_quotation_by_id({quotation_id}) failed.") if err_type != ErrorType.SUCCESS: return err_type, None if len(row_list) != 1: return ErrorType.DB_INVALID_KEY, None return ErrorType.SUCCESS, row_list[0] except Exception as ex: LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED, None async def update_session_status(self, cdb: AsyncSession, session_id, status: int) -> ErrorType: try: query = update(sessions).where(sessions.session_id == session_id).values(status=status) return await DB_SESSION_MNG.add(cdb, query) except Exception as ex: LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED async def update_quotation_status(self, cdb: AsyncSession, quotation_id, status: int) -> ErrorType: try: query = update(quotations).where(quotations.qt_id == quotation_id).values(status=status) return await DB_SESSION_MNG.add(cdb, query) except Exception as ex: LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED async def update_session_reject(self, cdb: AsyncSession, session_id, status: int, reject_reason: str) -> ErrorType: try: query = ( update(sessions) .where(sessions.session_id == session_id) .values(status=status, reject_reason=reject_reason) ) return await DB_SESSION_MNG.add(cdb, query) except Exception as ex: LOG.e_no_callstack(ex) return ErrorType.DB_RUN_FAILED