o2o-negosium-original/negodata/backend/crud/supplier_item_crud.py

190 lines
7.7 KiB
Python

from abc import ABC, abstractmethod
from typing import Tuple
from sqlalchemy import select, func, update
from sqlalchemy.ext.asyncio import AsyncSession
from common.database.db_session_manager import DB_SESSION_MNG
from common.database.model.models import supplier_items, items, suppliers
from common.enums import ErrorType
from common.logger import LOG
from common.utils.gtime import GTime
# 협력사-상품 매핑 CRUD. 스코프는 상위(협력사/상품)가 company_id 로 이미 걸린다.
class ISupplierItemCRUD(ABC):
@abstractmethod
async def list_by_supplier(self, cdb: AsyncSession, supplier_id) -> Tuple[ErrorType, list]:
pass
@abstractmethod
async def list_by_item(self, cdb: AsyncSession, item_id) -> Tuple[ErrorType, list]:
pass
@abstractmethod
async def get_by_id(self, cdb: AsyncSession, supplier_item_id) -> Tuple[ErrorType, supplier_items]:
pass
@abstractmethod
async def existing_item_ids(self, cdb: AsyncSession, supplier_id, item_ids: list) -> Tuple[ErrorType, list]:
pass
@abstractmethod
async def find_items_by_names(self, cdb: AsyncSession, company_id, names: list) -> Tuple[ErrorType, list]:
pass
@abstractmethod
async def add_many(self, cdb: AsyncSession, mappings: list) -> ErrorType:
pass
@abstractmethod
async def update_type(self, cdb: AsyncSession, supplier_item_id, supply_type: int) -> ErrorType:
pass
@abstractmethod
async def soft_delete(self, cdb: AsyncSession, supplier_item_id) -> ErrorType:
pass
class SupplierItemCRUD(ISupplierItemCRUD):
async def list_by_supplier(self, cdb: AsyncSession, supplier_id) -> Tuple[ErrorType, list]:
# 협력사 상세용: 매핑 + 상품명/코드/카테고리/제조사 조인.
# Row(supplier_item_id, item_id, name, code, supply_type, category, manufacturer)
try:
query = (
select(
supplier_items.supplier_item_id,
supplier_items.item_id,
items.name,
items.code,
supplier_items.supply_type,
items.category,
items.manufacturer,
)
.join(items, items.item_id == supplier_items.item_id)
.where(
supplier_items.supplier_id == supplier_id,
supplier_items.deleted == False, # noqa: E712
items.deleted == False, # noqa: E712
)
.order_by(supplier_items.created_at.desc())
)
err_type, rows = await DB_SESSION_MNG.execute(cdb, query)
if err_type != ErrorType.SUCCESS:
return err_type, []
return ErrorType.SUCCESS, list(rows)
except Exception as ex:
LOG.e_no_callstack(ex)
return ErrorType.DB_RUN_FAILED, []
async def list_by_item(self, cdb: AsyncSession, item_id) -> Tuple[ErrorType, list]:
# 견적생성 모달·상품 상세 공용: 이 상품을 취급하는 협력사별 공급유형.
# Row(supplier_id, supply_type, name, supplier_item_id)
try:
query = (
select(
supplier_items.supplier_id,
supplier_items.supply_type,
suppliers.name,
supplier_items.supplier_item_id,
)
.join(suppliers, suppliers.supplier_id == supplier_items.supplier_id)
.where(
supplier_items.item_id == item_id,
supplier_items.deleted == False, # noqa: E712
suppliers.deleted == False, # noqa: E712
)
)
err_type, rows = await DB_SESSION_MNG.execute(cdb, query)
if err_type != ErrorType.SUCCESS:
return err_type, []
return ErrorType.SUCCESS, list(rows)
except Exception as ex:
LOG.e_no_callstack(ex)
return ErrorType.DB_RUN_FAILED, []
async def get_by_id(self, cdb: AsyncSession, supplier_item_id) -> Tuple[ErrorType, supplier_items]:
try:
query = select(supplier_items).where(
supplier_items.supplier_item_id == supplier_item_id,
supplier_items.deleted == False, # noqa: E712
).limit(1)
err_type, row_list = await DB_SESSION_MNG.execute(cdb, query)
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 existing_item_ids(self, cdb: AsyncSession, supplier_id, item_ids: list) -> Tuple[ErrorType, list]:
# 이 협력사에 이미 매핑된 item_id 들(중복 등록 스킵용).
try:
if not item_ids:
return ErrorType.SUCCESS, []
query = select(supplier_items.item_id).where(
supplier_items.supplier_id == supplier_id,
supplier_items.item_id.in_(item_ids),
supplier_items.deleted == False, # noqa: E712
)
err_type, rows = await DB_SESSION_MNG.execute(cdb, query)
if err_type != ErrorType.SUCCESS:
return err_type, []
return ErrorType.SUCCESS, [r for r in rows if r is not None]
except Exception as ex:
LOG.e_no_callstack(ex)
return ErrorType.DB_RUN_FAILED, []
async def find_items_by_names(self, cdb: AsyncSession, company_id, names: list) -> Tuple[ErrorType, list]:
# 상품명(정확 일치) → 상품. Row(item_id, name). 이름 중복 상품이 있으면 여럿 반환될 수 있다(서비스에서 첫 매칭 사용).
try:
if not names:
return ErrorType.SUCCESS, []
query = select(items.item_id, items.name).where(
items.company_id == company_id,
items.name.in_(names),
items.deleted == False, # noqa: E712
)
err_type, rows = await DB_SESSION_MNG.execute(cdb, query)
if err_type != ErrorType.SUCCESS:
return err_type, []
return ErrorType.SUCCESS, list(rows)
except Exception as ex:
LOG.e_no_callstack(ex)
return ErrorType.DB_RUN_FAILED, []
async def add_many(self, cdb: AsyncSession, mappings: list) -> ErrorType:
try:
if not mappings:
return ErrorType.SUCCESS
return await DB_SESSION_MNG.insert(cdb, mappings)
except Exception as ex:
LOG.e_no_callstack(ex)
return ErrorType.DB_RUN_FAILED
async def update_type(self, cdb: AsyncSession, supplier_item_id, supply_type: int) -> ErrorType:
try:
query = (
update(supplier_items)
.where(supplier_items.supplier_item_id == supplier_item_id)
.values(supply_type=supply_type, updated_at=GTime.UTC())
)
return await DB_SESSION_MNG.add(cdb, query)
except Exception as ex:
LOG.e_no_callstack(ex)
return ErrorType.DB_RUN_FAILED
async def soft_delete(self, cdb: AsyncSession, supplier_item_id) -> ErrorType:
try:
query = (
update(supplier_items)
.where(supplier_items.supplier_item_id == supplier_item_id)
.values(deleted=True, updated_at=GTime.UTC())
)
return await DB_SESSION_MNG.add(cdb, query)
except Exception as ex:
LOG.e_no_callstack(ex)
return ErrorType.DB_RUN_FAILED