- enum DDL 주석에 영문값 보강(status/role/delivery/usage_type/qt_type/supplier_type) - 견적상태 3종(생성/진행중/마감)으로 정리(ON_HOLD 제거), 배송 PARTNER→SUPPLIER - 대시보드 손질, 관련 테스트·negosium 견적유형 주석 동반 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
102 lines
4.3 KiB
Python
102 lines
4.3 KiB
Python
import uuid
|
|
from datetime import timedelta
|
|
|
|
from fastapi import Depends
|
|
|
|
from common.database.db_session_manager import DB_SESSION_MNG
|
|
from common.database.model.models import quotations
|
|
from common.enums import DBWRType, ErrorType
|
|
from common.utils.gtime import GTime
|
|
from crud.dashboard_crud import DashboardCRUD, IDashboardCRUD
|
|
from router.v1.dashboard.protocol import (
|
|
DashboardActionList,
|
|
DashboardEmailUnsent,
|
|
DashboardEmailUnsentItem,
|
|
DashboardQuotationRef,
|
|
DashboardScope,
|
|
Res_DashboardSummary,
|
|
)
|
|
|
|
|
|
class DashboardService:
|
|
"""대시보드 요약 집계. 회사 전체(company)와 내가 만든 견적(mine) 두 스코프를 한 응답으로 내린다.
|
|
|
|
KPI 숫자는 전수 COUNT, 액션 리스트는 정렬해서 상위 N개만. 읽기 전용(사이드이펙트 없음).
|
|
"""
|
|
|
|
LIST_LIMIT = 5 # 액션 위젯 리스트당 표시 개수
|
|
DEADLINE_HOURS = 72 # 마감 임박 기준(3일 이내)
|
|
|
|
def __init__(self, dashboard_crud: IDashboardCRUD = Depends(DashboardCRUD)):
|
|
self.dashboard_crud = dashboard_crud
|
|
|
|
async def get_summary(self, company_id: str, user_id: str) -> Res_DashboardSummary:
|
|
res = Res_DashboardSummary()
|
|
company_uuid = uuid.UUID(company_id)
|
|
user_uuid = uuid.UUID(user_id)
|
|
now = GTime.UTC()
|
|
horizon = now + timedelta(hours=self.DEADLINE_HOURS)
|
|
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
|
|
|
res.company = await self._scope_summary(company_uuid, None, now, horizon, month_start)
|
|
res.mine = await self._scope_summary(company_uuid, user_uuid, now, horizon, month_start)
|
|
return res
|
|
|
|
async def _scope_summary(self, company_uuid, owner_uuid, now, horizon, month_start) -> DashboardScope:
|
|
scope = DashboardScope()
|
|
scope.in_progress = await self._count(
|
|
lambda s: self.dashboard_crud.count_in_progress(s, company_uuid, owner_uuid)
|
|
)
|
|
scope.this_month = await self._count(
|
|
lambda s: self.dashboard_crud.count_created_since(s, company_uuid, owner_uuid, month_start)
|
|
)
|
|
scope.awarded_this_month = await self._count(
|
|
lambda s: self.dashboard_crud.count_awarded_since(s, company_uuid, owner_uuid, month_start)
|
|
)
|
|
scope.deadline_soon = await self._action(
|
|
lambda s: self.dashboard_crud.deadline_soon(s, company_uuid, owner_uuid, now, horizon, self.LIST_LIMIT),
|
|
with_end_time=True,
|
|
)
|
|
scope.awarded = await self._action(
|
|
lambda s: self.dashboard_crud.awarded(s, company_uuid, owner_uuid, self.LIST_LIMIT)
|
|
)
|
|
scope.equal_bid = await self._action(
|
|
lambda s: self.dashboard_crud.equal_bid(s, company_uuid, owner_uuid, self.LIST_LIMIT)
|
|
)
|
|
scope.ruptured = await self._action(
|
|
lambda s: self.dashboard_crud.ruptured(s, company_uuid, owner_uuid, self.LIST_LIMIT)
|
|
)
|
|
scope.email_unsent = await self._email_unsent(company_uuid, owner_uuid)
|
|
return scope
|
|
|
|
async def _count(self, fn) -> int:
|
|
err, n = await DB_SESSION_MNG.execute_lambda(quotations.DBType(), DBWRType.DB_READ.value, fn)
|
|
return n if err == ErrorType.SUCCESS else 0
|
|
|
|
async def _action(self, fn, with_end_time: bool = False) -> DashboardActionList:
|
|
out = DashboardActionList()
|
|
err, rows, total = await DB_SESSION_MNG.execute_lambda(quotations.DBType(), DBWRType.DB_READ.value, fn)
|
|
if err != ErrorType.SUCCESS:
|
|
return out
|
|
out.total = total
|
|
out.items = [
|
|
DashboardQuotationRef(qt_id=r[0], name=r[1] or "", end_time=(r[2] if with_end_time else None))
|
|
for r in rows
|
|
]
|
|
return out
|
|
|
|
async def _email_unsent(self, company_uuid, owner_uuid) -> DashboardEmailUnsent:
|
|
out = DashboardEmailUnsent()
|
|
err, rows, total = await DB_SESSION_MNG.execute_lambda(
|
|
quotations.DBType(),
|
|
DBWRType.DB_READ.value,
|
|
lambda s: self.dashboard_crud.email_unsent(s, company_uuid, owner_uuid, self.LIST_LIMIT),
|
|
)
|
|
if err != ErrorType.SUCCESS:
|
|
return out
|
|
out.total = total
|
|
out.quotations = [
|
|
DashboardEmailUnsentItem(qt_id=r[0], name=r[1] or "", unsent_count=int(r[2] or 0)) for r in rows
|
|
]
|
|
return out
|