o2o-negosium-original/negodata/backend/router/v1/card/card.py
Mina Choi 1b9ca062f3 [feat] negodata: 협상카드 회사 스코프·공용 읽기전용 + 견적 목표가 산정내역 수정
협상카드
- 목록/조회를 등록자 개인 → 회사(company.users 조인) 단위로 확장: 같은 회사 유저 카드 + 공용(user_id NULL)
- 공용 카드는 기본 제공 자산 → 수정·삭제 불가(OWNER 포함), is_shared=True API 등록 거부
- 개인 카드 변경은 본인∪최고관리자만, 프론트 버튼 disable+툴팁
- 공개범위 셀렉터·엑셀 공개범위 컬럼·CardTable 전체 배지 제거(항상 개인 등록)
- test_card.py 5케이스 재작성

견적 목표가 산정내역
- get_setting_rates: 단일 컬럼 select 결과를 이중 인덱싱해 항상 예외 → 마진율이 늘 0으로 적용되던 버그 수정
- get_target_breakdown: 채택 후보 판정을 '현재 최소값' → '저장 목표가와 값 일치'로 변경(산정 후 상품가 변동에도 초록 체크 유지)
- 견적상세 협상카드 탭 세션ID 컬럼 제거

견적생성 모달
- 목표가 미리보기·마감기한 기본값(+1h)·미래 검증, combobox 팝오버 scrollIntoView, maskPrices 비율(%) 마스킹

Little-Helped: Stupid Claude
2026-07-09 17:04:40 +09:00

56 lines
2.4 KiB
Python

from uuid import UUID
from fastapi import APIRouter, Depends, Query
from common.models.gmodel import PageParams, UserInfo
from router.v1.validator.dependencies import IsValidAccessToken, RemoveNoneResponse
from services.card_service import CardService
from .protocol import (
Req_CreateCard,
Req_UpdateCard,
Res_Card,
Res_CardList,
Res_DeleteCard,
)
router = APIRouter(prefix="/v1/card", tags=["Card"], responses={404: {"description": "Not found"}})
@router.get(path="/list", response_model=Res_CardList, summary="협상카드 목록")
async def list_cards(
service: CardService = Depends(),
user_info: UserInfo = Depends(IsValidAccessToken),
search: str | None = Query(None, description="카드명/카드번호/스크립트 검색"),
is_wildcard: bool | None = Query(None, description="탭 필터: 미지정=전체 / false=협상카드 / true=와일드카드"),
pg: PageParams = Depends(),
):
return RemoveNoneResponse(await service.list_cards(user_info.company_id, search, is_wildcard, pg))
@router.post(path="/create", response_model=Res_Card, summary="협상카드 등록")
async def create_card(req: Req_CreateCard, service: CardService = Depends(), user_info: UserInfo = Depends(IsValidAccessToken)):
return RemoveNoneResponse(
await service.create_card(user_info.user_id, user_info.company_id, req)
)
@router.get(path="/{card_id}", response_model=Res_Card, summary="협상카드 조회")
async def get_card(card_id: UUID, service: CardService = Depends(), user_info: UserInfo = Depends(IsValidAccessToken)):
return RemoveNoneResponse(await service.get_card(user_info.company_id, str(card_id)))
@router.patch(path="/update/{card_id}", response_model=Res_Card, summary="협상카드 수정")
async def update_card(
card_id: UUID, req: Req_UpdateCard, service: CardService = Depends(), user_info: UserInfo = Depends(IsValidAccessToken)
):
return RemoveNoneResponse(
await service.update_card(user_info.user_id, user_info.company_id, user_info.role, str(card_id), req)
)
@router.delete(path="/delete/{card_id}", response_model=Res_DeleteCard, summary="협상카드 삭제")
async def delete_card(card_id: UUID, service: CardService = Depends(), user_info: UserInfo = Depends(IsValidAccessToken)):
return RemoveNoneResponse(
await service.delete_card(user_info.user_id, user_info.company_id, user_info.role, str(card_id))
)