feat: 거래처관리 API 연동 (Phase 2.2)
- partners/actions.ts: Mock → API 연동 전환 - apiRequest 헬퍼 함수 추가 (쿠키 기반 인증) - transform 함수: client_type ↔ partnerType 변환 - getPartnerList, getPartner, createPartner, updatePartner - getPartnerStats, deletePartner, deletePartners - 구현 문서 추가
This commit is contained in:
@@ -1,193 +1,241 @@
|
||||
'use server';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
import type { Partner, PartnerStats, PartnerFilter, PartnerListResponse, PartnerFormData } from './types';
|
||||
|
||||
/**
|
||||
* 주일 기업 - 거래처 관리 Server Actions
|
||||
* TODO: 실제 API 연동 시 구현
|
||||
* API 연동 버전
|
||||
*/
|
||||
|
||||
// 목업 데이터 (확장된 타입 적용)
|
||||
const mockPartners: Partner[] = [
|
||||
{
|
||||
id: '1',
|
||||
partnerCode: 'P-001',
|
||||
businessNumber: '123-12-12345',
|
||||
partnerName: '대한건설',
|
||||
representative: '홍길동',
|
||||
partnerType: 'sales',
|
||||
businessType: '건설업',
|
||||
businessCategory: '토목건축',
|
||||
zipCode: '06234',
|
||||
address1: '서울특별시 서초구 서초대로 123',
|
||||
address2: '대한건물 12층 1201호',
|
||||
phone: '02-1234-1234',
|
||||
mobile: '010-1234-1234',
|
||||
fax: '02-1234-1235',
|
||||
email: 'abc@email.com',
|
||||
manager: '담당자명',
|
||||
managerPhone: '010-1234-1234',
|
||||
systemManager: '관리자명',
|
||||
logoUrl: null,
|
||||
logoBlob: null,
|
||||
salesPaymentDay: 15,
|
||||
creditRating: 'AAA',
|
||||
transactionGrade: 'A',
|
||||
taxInvoiceEmail: 'abc@email.com',
|
||||
outstandingAmount: 11000000,
|
||||
overdueDays: 15,
|
||||
overdueToggle: true,
|
||||
badDebtToggle: false,
|
||||
memos: [
|
||||
{
|
||||
id: '1',
|
||||
content: '2025-12-12 12:21 [홍길동] 메모 내용',
|
||||
createdAt: '2025-12-12T12:21:00Z',
|
||||
},
|
||||
],
|
||||
documents: [],
|
||||
category: '건설사',
|
||||
paymentDay: 15,
|
||||
isBadDebt: false,
|
||||
isActive: true,
|
||||
createdAt: '2025-01-01',
|
||||
updatedAt: '2025-01-01',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
partnerCode: 'P-002',
|
||||
businessNumber: '456-45-45678',
|
||||
partnerName: '삼성시공',
|
||||
representative: '김철수',
|
||||
partnerType: 'purchase',
|
||||
businessType: '시공업',
|
||||
businessCategory: '건축시공',
|
||||
zipCode: '06235',
|
||||
address1: '서울특별시 강남구 테헤란로 456',
|
||||
address2: '삼성빌딩 5층',
|
||||
phone: '02-5678-5678',
|
||||
mobile: '010-5678-5678',
|
||||
fax: '02-5678-5679',
|
||||
email: 'samsung@email.com',
|
||||
manager: '이영희',
|
||||
managerPhone: '010-5678-5678',
|
||||
systemManager: '',
|
||||
logoUrl: null,
|
||||
logoBlob: null,
|
||||
salesPaymentDay: 10,
|
||||
creditRating: 'AA',
|
||||
transactionGrade: 'B',
|
||||
taxInvoiceEmail: 'tax@samsung.com',
|
||||
outstandingAmount: 5000000,
|
||||
overdueDays: 0,
|
||||
overdueToggle: false,
|
||||
badDebtToggle: false,
|
||||
memos: [],
|
||||
documents: [],
|
||||
category: '시공사',
|
||||
paymentDay: 10,
|
||||
isBadDebt: false,
|
||||
isActive: true,
|
||||
createdAt: '2025-01-02',
|
||||
updatedAt: '2025-01-02',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
partnerCode: 'P-003',
|
||||
businessNumber: '789-78-78901',
|
||||
partnerName: 'LG건설',
|
||||
representative: '박영수',
|
||||
partnerType: 'both',
|
||||
businessType: '종합건설',
|
||||
businessCategory: '건설',
|
||||
zipCode: '06236',
|
||||
address1: '서울특별시 영등포구 여의대로 789',
|
||||
address2: 'LG타워 20층',
|
||||
phone: '02-7890-7890',
|
||||
mobile: '010-7890-7890',
|
||||
fax: '02-7890-7891',
|
||||
email: 'lg@email.com',
|
||||
manager: '최민수',
|
||||
managerPhone: '010-7890-7890',
|
||||
systemManager: '시스템관리자',
|
||||
logoUrl: null,
|
||||
logoBlob: null,
|
||||
salesPaymentDay: 20,
|
||||
creditRating: 'BBB',
|
||||
transactionGrade: 'C',
|
||||
taxInvoiceEmail: 'tax@lg.com',
|
||||
outstandingAmount: 20000000,
|
||||
overdueDays: 30,
|
||||
overdueToggle: true,
|
||||
badDebtToggle: true,
|
||||
memos: [],
|
||||
documents: [],
|
||||
category: '건설사',
|
||||
paymentDay: 20,
|
||||
isBadDebt: true,
|
||||
isActive: true,
|
||||
createdAt: '2025-01-03',
|
||||
updatedAt: '2025-01-03',
|
||||
},
|
||||
];
|
||||
// API 기본 URL
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://api.sam.kr';
|
||||
const API_KEY = process.env.API_KEY || '';
|
||||
|
||||
// 거래처 목록 조회
|
||||
/**
|
||||
* API 요청 헬퍼 함수
|
||||
*/
|
||||
async function apiRequest<T>(
|
||||
endpoint: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<{ success: boolean; data?: T; error?: string; message?: string }> {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const accessToken = cookieStore.get('access_token')?.value;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-KEY': API_KEY,
|
||||
};
|
||||
|
||||
if (accessToken) {
|
||||
headers['Authorization'] = `Bearer ${accessToken}`;
|
||||
}
|
||||
|
||||
const url = `${API_BASE_URL}/api/v1${endpoint}`;
|
||||
console.log('🔵 [Partner API]', options.method || 'GET', url);
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
...headers,
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
console.log('🔵 [Partner API] Response status:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: result.message || `API 오류: ${response.status}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: result.success ?? true,
|
||||
data: result.data,
|
||||
message: result.message,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('API request error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* client_type API → Frontend partnerType 변환
|
||||
*/
|
||||
function transformClientType(clientType: string | null | undefined): Partner['partnerType'] {
|
||||
const typeMap: Record<string, Partner['partnerType']> = {
|
||||
'SALES': 'sales',
|
||||
'PURCHASE': 'purchase',
|
||||
'BOTH': 'both',
|
||||
};
|
||||
return typeMap[clientType || ''] || 'sales';
|
||||
}
|
||||
|
||||
/**
|
||||
* partnerType Frontend → API client_type 변환
|
||||
*/
|
||||
function transformPartnerType(partnerType: Partner['partnerType']): string {
|
||||
const typeMap: Record<Partner['partnerType'], string> = {
|
||||
'sales': 'SALES',
|
||||
'purchase': 'PURCHASE',
|
||||
'both': 'BOTH',
|
||||
};
|
||||
return typeMap[partnerType] || 'SALES';
|
||||
}
|
||||
|
||||
/**
|
||||
* API 응답 → 프론트엔드 Partner 타입 변환
|
||||
*/
|
||||
function transformPartner(apiData: Record<string, unknown>): Partner {
|
||||
return {
|
||||
id: String(apiData.id),
|
||||
partnerCode: String(apiData.client_code || ''),
|
||||
businessNumber: String(apiData.business_no || ''),
|
||||
partnerName: String(apiData.name || ''),
|
||||
representative: String(apiData.contact_person || ''),
|
||||
partnerType: transformClientType(apiData.client_type as string | null),
|
||||
businessType: String(apiData.business_type || ''),
|
||||
businessCategory: String(apiData.business_item || ''),
|
||||
zipCode: '', // API에 없는 필드
|
||||
address1: String(apiData.address || ''),
|
||||
address2: '', // API에 없는 필드
|
||||
phone: String(apiData.phone || ''),
|
||||
mobile: String(apiData.mobile || ''),
|
||||
fax: String(apiData.fax || ''),
|
||||
email: String(apiData.email || ''),
|
||||
manager: String(apiData.manager_name || ''),
|
||||
managerPhone: String(apiData.manager_tel || ''),
|
||||
systemManager: String(apiData.system_manager || ''),
|
||||
logoUrl: null, // API에 없는 필드
|
||||
logoBlob: null, // API에 없는 필드
|
||||
salesPaymentDay: 0, // API에 없는 필드
|
||||
creditRating: '', // API에 없는 필드
|
||||
transactionGrade: '', // API에 없는 필드
|
||||
taxInvoiceEmail: String(apiData.email || ''), // 동일한 이메일 사용
|
||||
outstandingAmount: Number(apiData.outstanding_amount || 0),
|
||||
overdueDays: apiData.is_overdue ? 30 : 0, // 연체 여부만 있음
|
||||
overdueToggle: Boolean(apiData.is_overdue),
|
||||
badDebtToggle: Boolean(apiData.has_bad_debt),
|
||||
memos: [], // API에 없는 필드
|
||||
documents: [], // API에 없는 필드
|
||||
category: '', // API에 없는 필드
|
||||
paymentDay: 0, // API에 없는 필드
|
||||
isBadDebt: Boolean(apiData.has_bad_debt),
|
||||
isActive: apiData.is_active !== false,
|
||||
createdAt: String(apiData.created_at || ''),
|
||||
updatedAt: String(apiData.updated_at || ''),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 프론트엔드 PartnerFormData → API 요청 데이터 변환
|
||||
*/
|
||||
function transformPartnerToApi(data: PartnerFormData): Record<string, unknown> {
|
||||
return {
|
||||
business_no: data.businessNumber,
|
||||
name: data.partnerName,
|
||||
contact_person: data.representative,
|
||||
client_type: transformPartnerType(data.partnerType),
|
||||
business_type: data.businessType,
|
||||
business_item: data.businessCategory,
|
||||
address: data.address1 + (data.address2 ? ` ${data.address2}` : ''),
|
||||
phone: data.phone,
|
||||
mobile: data.mobile,
|
||||
fax: data.fax,
|
||||
email: data.email,
|
||||
manager_name: data.manager,
|
||||
manager_tel: data.managerPhone,
|
||||
system_manager: data.systemManager,
|
||||
is_overdue: data.overdueToggle,
|
||||
is_active: true,
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// API 연동 함수
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* 거래처 목록 조회
|
||||
*/
|
||||
export async function getPartnerList(
|
||||
filter?: PartnerFilter
|
||||
): Promise<{ success: boolean; data?: PartnerListResponse; error?: string }> {
|
||||
try {
|
||||
// TODO: 실제 API 호출
|
||||
let filtered = [...mockPartners];
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
// 검색 필터
|
||||
// 검색어
|
||||
if (filter?.search) {
|
||||
const search = filter.search.toLowerCase();
|
||||
filtered = filtered.filter(
|
||||
(p) =>
|
||||
p.partnerName.toLowerCase().includes(search) ||
|
||||
p.partnerCode.toLowerCase().includes(search) ||
|
||||
p.representative.toLowerCase().includes(search)
|
||||
);
|
||||
queryParams.append('q', filter.search);
|
||||
}
|
||||
|
||||
// 악성채권 필터
|
||||
// 악성채권 필터 (Frontend badDebtFilter → 백엔드는 별도 필터 없음, 목록에서 처리)
|
||||
// API는 전체 데이터 반환, 프론트에서 필터링
|
||||
|
||||
// 페이지네이션
|
||||
if (filter?.page) queryParams.append('page', String(filter.page));
|
||||
if (filter?.size) queryParams.append('size', String(filter.size));
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
const endpoint = `/clients${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
const result = await apiRequest<{
|
||||
data: Record<string, unknown>[];
|
||||
current_page: number;
|
||||
per_page: number;
|
||||
total: number;
|
||||
last_page: number;
|
||||
}>(endpoint);
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
return { success: false, error: result.error || '거래처 목록 조회에 실패했습니다.' };
|
||||
}
|
||||
|
||||
const apiData = result.data;
|
||||
let items = (apiData.data || []).map(transformPartner);
|
||||
|
||||
// 악성채권 필터 (프론트엔드에서 처리)
|
||||
if (filter?.badDebtFilter && filter.badDebtFilter !== 'all') {
|
||||
filtered = filtered.filter((p) =>
|
||||
items = items.filter((p) =>
|
||||
filter.badDebtFilter === 'badDebt' ? p.isBadDebt : !p.isBadDebt
|
||||
);
|
||||
}
|
||||
|
||||
// 정렬
|
||||
// 정렬 (프론트엔드에서 처리 - API가 sort 미지원 시)
|
||||
if (filter?.sortBy) {
|
||||
switch (filter.sortBy) {
|
||||
case 'latest':
|
||||
filtered.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
items.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
break;
|
||||
case 'oldest':
|
||||
filtered.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
||||
items.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
||||
break;
|
||||
case 'nameAsc':
|
||||
filtered.sort((a, b) => a.partnerName.localeCompare(b.partnerName));
|
||||
items.sort((a, b) => a.partnerName.localeCompare(b.partnerName));
|
||||
break;
|
||||
case 'nameDesc':
|
||||
filtered.sort((a, b) => b.partnerName.localeCompare(a.partnerName));
|
||||
items.sort((a, b) => b.partnerName.localeCompare(a.partnerName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const page = filter?.page ?? 1;
|
||||
const size = filter?.size ?? 20;
|
||||
const start = (page - 1) * size;
|
||||
const paginatedItems = filtered.slice(start, start + size);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
items: paginatedItems,
|
||||
total: filtered.length,
|
||||
page,
|
||||
size,
|
||||
totalPages: Math.ceil(filtered.length / size),
|
||||
items,
|
||||
total: apiData.total || 0,
|
||||
page: apiData.current_page || 1,
|
||||
size: apiData.per_page || 20,
|
||||
totalPages: apiData.last_page || 1,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -196,150 +244,102 @@ export async function getPartnerList(
|
||||
}
|
||||
}
|
||||
|
||||
// 거래처 상세 조회
|
||||
/**
|
||||
* 거래처 상세 조회
|
||||
*/
|
||||
export async function getPartner(
|
||||
id: string
|
||||
): Promise<{ success: boolean; data?: Partner; error?: string }> {
|
||||
try {
|
||||
// TODO: 실제 API 호출
|
||||
const partner = mockPartners.find((p) => p.id === id);
|
||||
const result = await apiRequest<Record<string, unknown>>(`/clients/${id}`);
|
||||
|
||||
if (!partner) {
|
||||
return { success: false, error: '거래처를 찾을 수 없습니다.' };
|
||||
if (!result.success || !result.data) {
|
||||
return { success: false, error: result.error || '거래처를 찾을 수 없습니다.' };
|
||||
}
|
||||
|
||||
return { success: true, data: partner };
|
||||
return { success: true, data: transformPartner(result.data) };
|
||||
} catch (error) {
|
||||
console.error('getPartner error:', error);
|
||||
return { success: false, error: '거래처 조회에 실패했습니다.' };
|
||||
}
|
||||
}
|
||||
|
||||
// 거래처 등록
|
||||
/**
|
||||
* 거래처 등록
|
||||
*/
|
||||
export async function createPartner(
|
||||
data: PartnerFormData
|
||||
): Promise<{ success: boolean; data?: Partner; error?: string }> {
|
||||
try {
|
||||
// TODO: 실제 API 호출
|
||||
console.log('Create partner:', data);
|
||||
const apiData = transformPartnerToApi(data);
|
||||
|
||||
const newPartner: Partner = {
|
||||
id: String(Date.now()),
|
||||
partnerCode: `P-${String(mockPartners.length + 1).padStart(3, '0')}`,
|
||||
businessNumber: data.businessNumber,
|
||||
partnerName: data.partnerName,
|
||||
representative: data.representative,
|
||||
partnerType: data.partnerType,
|
||||
businessType: data.businessType,
|
||||
businessCategory: data.businessCategory,
|
||||
zipCode: data.zipCode,
|
||||
address1: data.address1,
|
||||
address2: data.address2,
|
||||
phone: data.phone,
|
||||
mobile: data.mobile,
|
||||
fax: data.fax,
|
||||
email: data.email,
|
||||
manager: data.manager,
|
||||
managerPhone: data.managerPhone,
|
||||
systemManager: data.systemManager,
|
||||
logoUrl: data.logoUrl,
|
||||
logoBlob: data.logoBlob,
|
||||
salesPaymentDay: data.salesPaymentDay,
|
||||
creditRating: data.creditRating,
|
||||
transactionGrade: data.transactionGrade,
|
||||
taxInvoiceEmail: data.taxInvoiceEmail,
|
||||
outstandingAmount: data.outstandingAmount,
|
||||
overdueDays: data.overdueDays,
|
||||
overdueToggle: data.overdueToggle,
|
||||
badDebtToggle: data.badDebtToggle,
|
||||
memos: data.memos,
|
||||
documents: data.documents,
|
||||
category: data.category,
|
||||
paymentDay: data.salesPaymentDay,
|
||||
isBadDebt: data.badDebtToggle,
|
||||
isActive: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
const result = await apiRequest<Record<string, unknown>>('/clients', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(apiData),
|
||||
});
|
||||
|
||||
return { success: true, data: newPartner };
|
||||
if (!result.success || !result.data) {
|
||||
return { success: false, error: result.error || '거래처 등록에 실패했습니다.' };
|
||||
}
|
||||
|
||||
return { success: true, data: transformPartner(result.data) };
|
||||
} catch (error) {
|
||||
console.error('createPartner error:', error);
|
||||
return { success: false, error: '거래처 등록에 실패했습니다.' };
|
||||
}
|
||||
}
|
||||
|
||||
// 거래처 수정
|
||||
/**
|
||||
* 거래처 수정
|
||||
*/
|
||||
export async function updatePartner(
|
||||
id: string,
|
||||
data: PartnerFormData
|
||||
): Promise<{ success: boolean; data?: Partner; error?: string }> {
|
||||
try {
|
||||
// TODO: 실제 API 호출
|
||||
console.log('Update partner:', id, data);
|
||||
const apiData = transformPartnerToApi(data);
|
||||
|
||||
const existingPartner = mockPartners.find((p) => p.id === id);
|
||||
if (!existingPartner) {
|
||||
return { success: false, error: '거래처를 찾을 수 없습니다.' };
|
||||
const result = await apiRequest<Record<string, unknown>>(`/clients/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(apiData),
|
||||
});
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
return { success: false, error: result.error || '거래처 수정에 실패했습니다.' };
|
||||
}
|
||||
|
||||
const updatedPartner: Partner = {
|
||||
...existingPartner,
|
||||
businessNumber: data.businessNumber,
|
||||
partnerName: data.partnerName,
|
||||
representative: data.representative,
|
||||
partnerType: data.partnerType,
|
||||
businessType: data.businessType,
|
||||
businessCategory: data.businessCategory,
|
||||
zipCode: data.zipCode,
|
||||
address1: data.address1,
|
||||
address2: data.address2,
|
||||
phone: data.phone,
|
||||
mobile: data.mobile,
|
||||
fax: data.fax,
|
||||
email: data.email,
|
||||
manager: data.manager,
|
||||
managerPhone: data.managerPhone,
|
||||
systemManager: data.systemManager,
|
||||
logoUrl: data.logoUrl,
|
||||
logoBlob: data.logoBlob,
|
||||
salesPaymentDay: data.salesPaymentDay,
|
||||
creditRating: data.creditRating,
|
||||
transactionGrade: data.transactionGrade,
|
||||
taxInvoiceEmail: data.taxInvoiceEmail,
|
||||
outstandingAmount: data.outstandingAmount,
|
||||
overdueDays: data.overdueDays,
|
||||
overdueToggle: data.overdueToggle,
|
||||
badDebtToggle: data.badDebtToggle,
|
||||
memos: data.memos,
|
||||
documents: data.documents,
|
||||
category: data.category,
|
||||
paymentDay: data.salesPaymentDay,
|
||||
isBadDebt: data.badDebtToggle,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
return { success: true, data: updatedPartner };
|
||||
return { success: true, data: transformPartner(result.data) };
|
||||
} catch (error) {
|
||||
console.error('updatePartner error:', error);
|
||||
return { success: false, error: '거래처 수정에 실패했습니다.' };
|
||||
}
|
||||
}
|
||||
|
||||
// 거래처 통계 조회
|
||||
/**
|
||||
* 거래처 통계 조회
|
||||
*/
|
||||
export async function getPartnerStats(): Promise<{ success: boolean; data?: PartnerStats; error?: string }> {
|
||||
try {
|
||||
// TODO: 실제 API 호출
|
||||
const total = mockPartners.length;
|
||||
const badDebt = mockPartners.filter((p) => p.isBadDebt).length;
|
||||
const result = await apiRequest<{
|
||||
total: number;
|
||||
sales: number;
|
||||
purchase: number;
|
||||
both: number;
|
||||
badDebt: number;
|
||||
normal: number;
|
||||
}>('/clients/stats');
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
return { success: false, error: result.error || '통계 조회에 실패했습니다.' };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
total,
|
||||
unregistered: 5, // 목업
|
||||
badDebt,
|
||||
normal: total - badDebt,
|
||||
total: result.data.total || 0,
|
||||
unregistered: 0, // Client API에서 미지원 (거래처는 등록 완료 상태만)
|
||||
badDebt: result.data.badDebt || 0,
|
||||
normal: result.data.normal || 0,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -348,11 +348,19 @@ export async function getPartnerStats(): Promise<{ success: boolean; data?: Part
|
||||
}
|
||||
}
|
||||
|
||||
// 거래처 삭제
|
||||
/**
|
||||
* 거래처 삭제
|
||||
*/
|
||||
export async function deletePartner(id: string): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// TODO: 실제 API 호출
|
||||
console.log('Delete partner:', id);
|
||||
const result = await apiRequest(`/clients/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '거래처 삭제에 실패했습니다.' };
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('deletePartner error:', error);
|
||||
@@ -360,12 +368,24 @@ export async function deletePartner(id: string): Promise<{ success: boolean; err
|
||||
}
|
||||
}
|
||||
|
||||
// 거래처 일괄 삭제
|
||||
/**
|
||||
* 거래처 일괄 삭제
|
||||
*/
|
||||
export async function deletePartners(ids: string[]): Promise<{ success: boolean; deletedCount?: number; error?: string }> {
|
||||
try {
|
||||
// TODO: 실제 API 호출
|
||||
console.log('Delete partners:', ids);
|
||||
return { success: true, deletedCount: ids.length };
|
||||
const result = await apiRequest<{ deleted_count: number }>('/clients/bulk', {
|
||||
method: 'DELETE',
|
||||
body: JSON.stringify({ ids: ids.map((id) => Number(id)) }),
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '일괄 삭제에 실패했습니다.' };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
deletedCount: result.data?.deleted_count || ids.length,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('deletePartners error:', error);
|
||||
return { success: false, error: '일괄 삭제에 실패했습니다.' };
|
||||
|
||||
Reference in New Issue
Block a user