feat: [출하/배차/회계] 배차 다중행 + 어음 리팩토링 + 출고관리
- 배차차량관리 목업→API 연동, 배차정보 다중 행 - ShipmentManagement 출고관리 API 매핑 - BillManagement 리팩토링 (섹션 분리, hooks, constants) - 상품권 actions/types 확장 - 출하관리 캘린더 기본 뷰 week-time
This commit is contained in:
@@ -32,9 +32,10 @@ import {
|
||||
CATEGORY_LABELS,
|
||||
SORT_OPTIONS,
|
||||
} from './types';
|
||||
import { getReceivablesList, getReceivablesSummary, updateOverdueStatus, updateMemos, exportReceivablesExcel } from './actions';
|
||||
import { getReceivablesList, getReceivablesSummary, updateOverdueStatus, updateMemos } from './actions';
|
||||
import { toast } from 'sonner';
|
||||
import { filterByText } from '@/lib/utils/search';
|
||||
import { downloadExcel, type ExcelColumn } from '@/lib/utils/excel-download';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { usePermission } from '@/hooks/usePermission';
|
||||
|
||||
@@ -213,27 +214,45 @@ export function ReceivablesStatus({ highlightVendorId, initialData, initialSumma
|
||||
});
|
||||
}, []);
|
||||
|
||||
// ===== 엑셀 다운로드 핸들러 =====
|
||||
// ===== 엑셀 다운로드 핸들러 (프론트 xlsx 생성) =====
|
||||
const handleExcelDownload = useCallback(async () => {
|
||||
const result = await exportReceivablesExcel({
|
||||
year: selectedYear,
|
||||
search: searchQuery || undefined,
|
||||
});
|
||||
|
||||
if (result.success && result.data) {
|
||||
const url = URL.createObjectURL(result.data);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = result.filename || '채권현황.xlsx';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
toast.success('엑셀 파일이 다운로드되었습니다.');
|
||||
} else {
|
||||
toast.error(result.error || '엑셀 다운로드에 실패했습니다.');
|
||||
try {
|
||||
toast.info('엑셀 파일 생성 중...');
|
||||
// 데이터가 이미 로드되어 있으므로 sortedData 사용
|
||||
if (sortedData.length === 0) {
|
||||
toast.warning('다운로드할 데이터가 없습니다.');
|
||||
return;
|
||||
}
|
||||
// 동적 월 컬럼 포함 엑셀 컬럼 생성
|
||||
const columns: ExcelColumn<Record<string, unknown>>[] = [
|
||||
{ header: '거래처', key: 'vendorName', width: 20 },
|
||||
{ header: '연체', key: 'isOverdue', width: 8 },
|
||||
...monthLabels.map((label, idx) => ({
|
||||
header: label, key: `month_${idx}`, width: 12,
|
||||
})),
|
||||
{ header: '합계', key: 'total', width: 14 },
|
||||
{ header: '메모', key: 'memo', width: 20 },
|
||||
];
|
||||
// 미수금 카테고리 기준으로 플랫 데이터 생성
|
||||
const exportData = sortedData.map(vendor => {
|
||||
const receivable = vendor.categories.find(c => c.category === 'receivable');
|
||||
const row: Record<string, unknown> = {
|
||||
vendorName: vendor.vendorName,
|
||||
isOverdue: vendor.isOverdue ? '연체' : '',
|
||||
};
|
||||
monthLabels.forEach((_, idx) => {
|
||||
row[`month_${idx}`] = receivable?.amounts.values[idx] || 0;
|
||||
});
|
||||
row.total = receivable?.amounts.total || 0;
|
||||
row.memo = vendor.memo || '';
|
||||
return row;
|
||||
});
|
||||
await downloadExcel({ data: exportData, columns, filename: '미수금현황', sheetName: '미수금현황' });
|
||||
toast.success('엑셀 다운로드 완료');
|
||||
} catch {
|
||||
toast.error('엑셀 다운로드에 실패했습니다.');
|
||||
}
|
||||
}, [selectedYear, searchQuery]);
|
||||
}, [sortedData, monthLabels]);
|
||||
|
||||
// ===== 변경된 연체 항목 확인 =====
|
||||
const changedOverdueItems = useMemo(() => {
|
||||
|
||||
Reference in New Issue
Block a user