o2o-negosium-original/negodata/front/src/features/partners/hooks/useSupplierItems.ts
Mina Choi 43dbcbcd96 [feat] negodata: 협력사↔상품 매핑(취급상품·공급유형) + 견적 픽리스트 서버검색 콤보박스
- partner.supplier_items 매핑테이블 신설: supply_type(0없음/1유통/2제조/3총판), 부분 유니크 인덱스(soft-delete 인지). quotations.supplier_type와 의미단위 달라 컬럼명 supply_type.
- 백엔드: supplier_item CRUD/service/router(/v1/supplier-item, by-supplier·by-item·create·bulk·update·delete). 소유권 company 스코프. ErrorType.SUPPLIER_ITEM_NOT_FOUND 추가.
- DDL: 01-schema/04-alter(날짜접미 리네임) + dev DB 반영.
- 프론트(feature): 협력사 엑셀 '취급상품' 컬럼(콤마 상품명→일괄매핑, 이름 미매칭 스킵+리포트, 유형 안받고 없음), 협력사 상세 취급상품 관리(추가/삭제/유형변경 즉시반영), 견적모달 협력사 리스트에 선택상품 공급유형 배지.
- 재사용 서버검색 Combobox 신설 → 취급상품·견적상품·협력사초청·협상카드 픽리스트에 적용(size 100 캡 해소, 검색은 서버 ILIKE). 견적 선택상품 파생값은 useGetItem 단건조회로 안정화.
- orval 재생성(supplier-item 클라이언트/모델).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:59:13 +09:00

46 lines
1.7 KiB
TypeScript

import { useQueryClient } from '@tanstack/react-query';
import {
useListSupplierItems,
createSupplierItem,
updateSupplyType,
deleteSupplierItem,
getListSupplierItemsQueryKey,
} from '@/api/generated/supplier-item/supplier-item';
import type { SupplierItemData } from '@/api/generated/model/supplierItemData';
// 협력사 취급상품(매핑) 서버 데이터 + CRUD. 협력사 상세(PartnerFormSheet)에서 쓴다.
// supplierId 가 없으면(신규 등록 폼) 쿼리는 비활성.
export function useSupplierItems(supplierId: string | undefined) {
const queryClient = useQueryClient();
const listQuery = useListSupplierItems(supplierId ?? '', { query: { enabled: !!supplierId } });
const refresh = () =>
supplierId
? queryClient.invalidateQueries({ queryKey: getListSupplierItemsQueryKey(supplierId) })
: Promise.resolve();
const items: SupplierItemData[] = listQuery.data?.supplier_items ?? [];
const addItem = async (itemId: string, supplyType: number) => {
if (!supplierId) return;
const res = await createSupplierItem({ supplier_id: supplierId, item_id: itemId, supply_type: supplyType });
const r = res.result;
if (r && r.success === false) {
throw new Error(r.desc === 'DB_ALREADY_SAME_KEY' ? '이미 등록된 취급상품입니다.' : r.desc || '취급상품 추가 실패');
}
await refresh();
};
const changeType = async (supplierItemId: string, supplyType: number) => {
await updateSupplyType(supplierItemId, { supply_type: supplyType });
await refresh();
};
const removeItem = async (supplierItemId: string) => {
await deleteSupplierItem(supplierItemId);
await refresh();
};
return { items, isLoading: listQuery.isLoading, addItem, changeType, removeItem };
}