fix:엑셀 내보내기를 현재 화면 데이터 기반으로 변경
- GET → POST 방식으로 변경 - 저장된 DB 데이터 대신 현재 화면에 표시된 데이터 내보내기 - 프론트엔드에서 logs, splits 데이터를 JSON으로 전송 - Blob 다운로드 방식으로 파일 저장 - 금액 필드에 콤마 포맷팅 적용 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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'] ?? '',
|
||||
''
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1128,13 +1128,48 @@ className="text-xs text-amber-600 hover:text-amber-700 underline"
|
||||
};
|
||||
|
||||
// 엑셀 다운로드 핸들러
|
||||
const handleExport = () => {
|
||||
const params = new URLSearchParams({
|
||||
startDate: dateFrom.replace(/-/g, ''),
|
||||
endDate: dateTo.replace(/-/g, ''),
|
||||
cardNum: selectedCard
|
||||
});
|
||||
window.location.href = `${API.export}?${params}`;
|
||||
const handleExport = async () => {
|
||||
if (logs.length === 0) {
|
||||
notify('내보낼 데이터가 없습니다.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(API.export, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': CSRF_TOKEN,
|
||||
'Accept': 'text/csv'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
startDate: dateFrom.replace(/-/g, ''),
|
||||
endDate: dateTo.replace(/-/g, ''),
|
||||
logs: logs,
|
||||
splits: splits
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
notify(errorData.error || '다운로드 실패', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Blob으로 파일 다운로드
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `카드사용내역_${dateFrom.replace(/-/g, '')}_${dateTo.replace(/-/g, '')}.csv`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
a.remove();
|
||||
notify('엑셀 다운로드 완료', 'success');
|
||||
} catch (err) {
|
||||
notify('다운로드 오류: ' + err.message, 'error');
|
||||
}
|
||||
};
|
||||
|
||||
// 이번 달 버튼
|
||||
|
||||
@@ -308,7 +308,7 @@
|
||||
Route::get('/transactions', [\App\Http\Controllers\Barobill\EcardController::class, 'transactions'])->name('transactions');
|
||||
Route::get('/account-codes', [\App\Http\Controllers\Barobill\EcardController::class, 'accountCodes'])->name('account-codes');
|
||||
Route::post('/save', [\App\Http\Controllers\Barobill\EcardController::class, 'save'])->name('save');
|
||||
Route::get('/export', [\App\Http\Controllers\Barobill\EcardController::class, 'exportExcel'])->name('export');
|
||||
Route::post('/export', [\App\Http\Controllers\Barobill\EcardController::class, 'exportExcel'])->name('export');
|
||||
// 분개 관련
|
||||
Route::get('/splits', [\App\Http\Controllers\Barobill\EcardController::class, 'splits'])->name('splits');
|
||||
Route::post('/splits', [\App\Http\Controllers\Barobill\EcardController::class, 'saveSplits'])->name('splits.save');
|
||||
|
||||
Reference in New Issue
Block a user