fix:엑셀 내보내기를 현재 화면 데이터 기반으로 변경

- GET → POST 방식으로 변경
- 저장된 DB 데이터 대신 현재 화면에 표시된 데이터 내보내기
- 프론트엔드에서 logs, splits 데이터를 JSON으로 전송
- Blob 다운로드 방식으로 파일 저장
- 금액 필드에 콤마 포맷팅 적용

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
pro
2026-01-23 16:13:16 +09:00
parent 32a3987895
commit b934bc2e12
3 changed files with 101 additions and 74 deletions

View File

@@ -773,37 +773,22 @@ public function save(Request $request): JsonResponse
public function exportExcel(Request $request): StreamedResponse|JsonResponse
{
try {
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
$startDate = $request->input('startDate', date('Ymd'));
$endDate = $request->input('endDate', date('Ymd'));
$cardNum = $request->input('cardNum', '');
// DB에서 저장된 데이터 조회
$query = CardTransaction::where('tenant_id', $tenantId)
->whereBetween('use_date', [$startDate, $endDate])
->orderBy('use_date', 'desc')
->orderBy('use_time', 'desc');
if (!empty($cardNum)) {
$query->where('card_num', $cardNum);
}
$transactions = $query->get();
$logs = $request->input('logs', []);
$splitsData = $request->input('splits', []);
// 데이터가 없으면 안내
if ($transactions->isEmpty()) {
if (empty($logs)) {
return response()->json([
'success' => false,
'error' => '저장된 데이터가 없습니다. 먼저 데이터를 조회하고 저장해주세요.'
'error' => '내보낼 데이터가 없습니다.'
]);
}
// 분개 데이터 조회
$splitsData = CardTransactionSplit::getByDateRange($tenantId, $startDate, $endDate);
$filename = "카드사용내역_{$startDate}_{$endDate}.csv";
return response()->streamDownload(function () use ($transactions, $splitsData) {
return response()->streamDownload(function () use ($logs, $splitsData) {
$handle = fopen('php://output', 'w');
// UTF-8 BOM for Excel
@@ -827,44 +812,46 @@ public function exportExcel(Request $request): StreamedResponse|JsonResponse
]);
// 데이터
foreach ($transactions as $trans) {
$dateTime = '';
if ($trans->use_date) {
$dateTime = substr($trans->use_date, 0, 4) . '-' .
substr($trans->use_date, 4, 2) . '-' .
substr($trans->use_date, 6, 2);
if ($trans->use_time) {
$dateTime .= ' ' . substr($trans->use_time, 0, 2) . ':' .
substr($trans->use_time, 2, 2) . ':' .
substr($trans->use_time, 4, 2);
}
}
foreach ($logs as $log) {
$dateTime = $log['useDateTime'] ?? '';
$cardNum = $log['cardNum'] ?? '';
$cardBrand = $log['cardBrand'] ?? '';
$approvalNum = $log['approvalNum'] ?? '';
$approvalAmount = $log['approvalAmount'] ?? 0;
$tax = $log['tax'] ?? 0;
// 해당 거래의 고유키 생성
$uniqueKey = implode('|', [
$trans->card_num,
$trans->use_dt,
$trans->approval_num,
(int) $trans->approval_amount,
// 고유키로 분개 데이터 확인
$uniqueKey = $log['uniqueKey'] ?? implode('|', [
$cardNum,
$log['useDt'] ?? '',
$approvalNum,
(int) $approvalAmount,
]);
// 분개 데이터 확인
$splits = $splitsData[$uniqueKey] ?? [];
$hasSplits = count($splits) > 0;
// 공제여부
$deductionType = $log['deductionType'] ?? ($log['merchantBizNum'] ? 'deductible' : 'non_deductible');
$deductionText = ($deductionType === 'non_deductible') ? '불공' : '공제';
// 증빙/판매자상호, 내역
$evidenceName = $log['evidenceName'] ?? $log['merchantName'] ?? '';
$description = $log['description'] ?? $log['merchantBizType'] ?? '';
if ($hasSplits) {
// 분개가 있는 경우: 원본 행 (합계 표시)
fputcsv($handle, [
'원본',
$dateTime,
$trans->card_num,
$trans->card_company_name,
$cardNum,
$cardBrand,
'-', // 분개된 경우 공제여부는 각 분개에서 표시
$trans->evidence_name ?: $trans->merchant_name,
$trans->description ?: $trans->merchant_biz_type,
$trans->approval_amount,
$trans->tax,
$trans->approval_num,
$evidenceName,
$description,
number_format($approvalAmount),
number_format($tax),
$approvalNum,
'-', // 분개된 경우 계정과목은 각 분개에서 표시
'분개됨 (' . count($splits) . '건)',
''
@@ -872,41 +859,46 @@ public function exportExcel(Request $request): StreamedResponse|JsonResponse
// 각 분개 행 출력
foreach ($splits as $index => $split) {
$deductionText = ($split->deduction_type === 'non_deductible') ? '불공' : '공제';
$splitDeductionType = $split['deduction_type'] ?? $split['deductionType'] ?? 'deductible';
$splitDeductionText = ($splitDeductionType === 'non_deductible') ? '불공' : '공제';
$splitAmount = $split['split_amount'] ?? $split['amount'] ?? 0;
$splitEvidenceName = $split['evidence_name'] ?? $split['evidenceName'] ?? '';
$splitDescription = $split['description'] ?? '';
$splitAccountCode = $split['account_code'] ?? $split['accountCode'] ?? '';
$splitAccountName = $split['account_name'] ?? $split['accountName'] ?? '';
$splitMemo = $split['memo'] ?? '';
fputcsv($handle, [
'└ 분개 #' . ($index + 1),
'', // 사용일시 (원본과 동일하므로 생략)
'', // 카드번호
'', // 카드사
$deductionText,
$split->evidence_name ?: '',
$split->description ?: '',
$split->split_amount,
$splitDeductionText,
$splitEvidenceName,
$splitDescription,
number_format($splitAmount),
'', // 부가세 (분개에서는 생략)
'', // 승인번호
$split->account_code ?: '',
$split->account_name ?: '',
$split->memo ?: ''
$splitAccountCode,
$splitAccountName,
$splitMemo
]);
}
} else {
// 분개가 없는 경우: 일반 행
$deductionType = $trans->deduction_type ?: ($trans->merchant_biz_num ? 'deductible' : 'non_deductible');
$deductionText = ($deductionType === 'non_deductible') ? '불공' : '공제';
fputcsv($handle, [
'일반',
$dateTime,
$trans->card_num,
$trans->card_company_name,
$cardNum,
$cardBrand,
$deductionText,
$trans->evidence_name ?: $trans->merchant_name,
$trans->description ?: $trans->merchant_biz_type,
$trans->approval_amount,
$trans->tax,
$trans->approval_num,
$trans->account_code ?: '',
$trans->account_name ?: '',
$evidenceName,
$description,
number_format($approvalAmount),
number_format($tax),
$approvalNum,
$log['accountCode'] ?? '',
$log['accountName'] ?? '',
''
]);
}