feat:입출금내역 계정과목 추가 및 엑셀 다운로드 기능
- BankTransaction 모델: 입출금 내역 저장 (계정과목 포함) - 바로빌 데이터와 DB 저장 데이터 매칭하여 계정과목 유지 - 계정과목 드롭다운 선택 및 저장 기능 - 엑셀(CSV) 다운로드 기능 - 저장된 행은 녹색 배경으로 표시 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,12 +5,15 @@
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Barobill\BarobillConfig;
|
||||
use App\Models\Barobill\BarobillMember;
|
||||
use App\Models\Barobill\BankTransaction;
|
||||
use App\Models\Tenants\Tenant;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* 바로빌 계좌 입출금내역 조회 컨트롤러
|
||||
@@ -203,9 +206,12 @@ public function transactions(Request $request): JsonResponse
|
||||
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
||||
$userId = $barobillMember?->barobill_id ?? '';
|
||||
|
||||
// DB에서 저장된 계정과목 데이터 조회
|
||||
$savedData = BankTransaction::getByDateRange($tenantId, $startDate, $endDate, $bankAccountNum ?: null);
|
||||
|
||||
// 전체 계좌 조회: 빈 값이면 모든 계좌의 거래 내역 조회
|
||||
if (empty($bankAccountNum)) {
|
||||
return $this->getAllAccountsTransactions($userId, $startDate, $endDate, $page, $limit);
|
||||
return $this->getAllAccountsTransactions($userId, $startDate, $endDate, $page, $limit, $savedData);
|
||||
}
|
||||
|
||||
// 단일 계좌 조회
|
||||
@@ -252,8 +258,8 @@ public function transactions(Request $request): JsonResponse
|
||||
]);
|
||||
}
|
||||
|
||||
// 데이터 파싱
|
||||
$logs = $this->parseTransactionLogs($resultData);
|
||||
// 데이터 파싱 (저장된 계정과목 병합)
|
||||
$logs = $this->parseTransactionLogs($resultData, '', $savedData);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
@@ -280,7 +286,7 @@ public function transactions(Request $request): JsonResponse
|
||||
/**
|
||||
* 전체 계좌의 거래 내역 조회
|
||||
*/
|
||||
private function getAllAccountsTransactions(string $userId, string $startDate, string $endDate, int $page, int $limit): JsonResponse
|
||||
private function getAllAccountsTransactions(string $userId, string $startDate, string $endDate, int $page, int $limit, $savedData = null): JsonResponse
|
||||
{
|
||||
// 먼저 계좌 목록 조회
|
||||
$accountResult = $this->callSoap('GetBankAccountEx', ['AvailOnly' => 0]);
|
||||
@@ -326,7 +332,7 @@ private function getAllAccountsTransactions(string $userId, string $startDate, s
|
||||
$errorCode = $this->checkErrorCode($accData);
|
||||
|
||||
if (!$errorCode || in_array($errorCode, [-25005, -25001])) {
|
||||
$parsed = $this->parseTransactionLogs($accData, $acc->BankName ?? '');
|
||||
$parsed = $this->parseTransactionLogs($accData, $acc->BankName ?? '', $savedData);
|
||||
foreach ($parsed['logs'] as $log) {
|
||||
$log['bankName'] = $acc->BankName ?? $this->getBankName($acc->BankCode ?? '');
|
||||
$allLogs[] = $log;
|
||||
@@ -370,9 +376,9 @@ private function getAllAccountsTransactions(string $userId, string $startDate, s
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래 내역 파싱
|
||||
* 거래 내역 파싱 (저장된 계정과목 병합)
|
||||
*/
|
||||
private function parseTransactionLogs($resultData, string $defaultBankName = ''): array
|
||||
private function parseTransactionLogs($resultData, string $defaultBankName = '', $savedData = null): array
|
||||
{
|
||||
$logs = [];
|
||||
$totalDeposit = 0;
|
||||
@@ -388,6 +394,7 @@ private function parseTransactionLogs($resultData, string $defaultBankName = '')
|
||||
foreach ($rawLogs as $log) {
|
||||
$deposit = floatval($log->Deposit ?? 0);
|
||||
$withdraw = floatval($log->Withdraw ?? 0);
|
||||
$balance = floatval($log->Balance ?? 0);
|
||||
$totalDeposit += $deposit;
|
||||
$totalWithdraw += $withdraw;
|
||||
|
||||
@@ -413,23 +420,35 @@ private function parseTransactionLogs($resultData, string $defaultBankName = '')
|
||||
$fullSummary = $fullSummary ? $fullSummary . ' ' . $remark2 : $remark2;
|
||||
}
|
||||
|
||||
$logs[] = [
|
||||
$bankAccountNum = $log->BankAccountNum ?? '';
|
||||
|
||||
// 고유 키 생성하여 저장된 데이터와 매칭
|
||||
$uniqueKey = implode('|', [$bankAccountNum, $transDT, $deposit, $withdraw, $balance]);
|
||||
$savedItem = $savedData?->get($uniqueKey);
|
||||
|
||||
$logItem = [
|
||||
'transDate' => $transDate,
|
||||
'transTime' => $transTime,
|
||||
'transDateTime' => $dateTime,
|
||||
'bankAccountNum' => $log->BankAccountNum ?? '',
|
||||
'bankAccountNum' => $bankAccountNum,
|
||||
'bankName' => $log->BankName ?? $defaultBankName,
|
||||
'deposit' => $deposit,
|
||||
'withdraw' => $withdraw,
|
||||
'depositFormatted' => number_format($deposit),
|
||||
'withdrawFormatted' => number_format($withdraw),
|
||||
'balance' => floatval($log->Balance ?? 0),
|
||||
'balanceFormatted' => number_format(floatval($log->Balance ?? 0)),
|
||||
'balance' => $balance,
|
||||
'balanceFormatted' => number_format($balance),
|
||||
'summary' => $fullSummary,
|
||||
'cast' => $log->Cast ?? '',
|
||||
'memo' => $log->Memo ?? '',
|
||||
'transOffice' => $log->TransOffice ?? ''
|
||||
'transOffice' => $log->TransOffice ?? '',
|
||||
// 저장된 계정과목 정보 병합
|
||||
'accountCode' => $savedItem?->account_code ?? '',
|
||||
'accountName' => $savedItem?->account_name ?? '',
|
||||
'isSaved' => $savedItem !== null,
|
||||
];
|
||||
|
||||
$logs[] = $logItem;
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -507,6 +526,233 @@ private function getBankName(string $code): string
|
||||
return $banks[$code] ?? $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 계정과목 목록 조회
|
||||
*/
|
||||
public function accountCodes(): JsonResponse
|
||||
{
|
||||
// 자주 사용되는 계정과목 목록
|
||||
$codes = [
|
||||
['code' => '101', 'name' => '현금'],
|
||||
['code' => '103', 'name' => '보통예금'],
|
||||
['code' => '108', 'name' => '외상매출금'],
|
||||
['code' => '110', 'name' => '받을어음'],
|
||||
['code' => '253', 'name' => '외상매입금'],
|
||||
['code' => '255', 'name' => '지급어음'],
|
||||
['code' => '401', 'name' => '매출'],
|
||||
['code' => '501', 'name' => '매입'],
|
||||
['code' => '511', 'name' => '급여'],
|
||||
['code' => '521', 'name' => '복리후생비'],
|
||||
['code' => '522', 'name' => '여비교통비'],
|
||||
['code' => '523', 'name' => '접대비'],
|
||||
['code' => '524', 'name' => '통신비'],
|
||||
['code' => '525', 'name' => '수도광열비'],
|
||||
['code' => '526', 'name' => '세금과공과'],
|
||||
['code' => '527', 'name' => '임차료'],
|
||||
['code' => '528', 'name' => '수선비'],
|
||||
['code' => '529', 'name' => '보험료'],
|
||||
['code' => '530', 'name' => '차량유지비'],
|
||||
['code' => '531', 'name' => '운반비'],
|
||||
['code' => '532', 'name' => '교육훈련비'],
|
||||
['code' => '533', 'name' => '도서인쇄비'],
|
||||
['code' => '534', 'name' => '사무용품비'],
|
||||
['code' => '535', 'name' => '소모품비'],
|
||||
['code' => '536', 'name' => '지급수수료'],
|
||||
['code' => '537', 'name' => '광고선전비'],
|
||||
['code' => '538', 'name' => '대손상각비'],
|
||||
['code' => '539', 'name' => '감가상각비'],
|
||||
['code' => '540', 'name' => '잡비'],
|
||||
['code' => '901', 'name' => '이자수익'],
|
||||
['code' => '902', 'name' => '이자비용'],
|
||||
['code' => '910', 'name' => '잡이익'],
|
||||
['code' => '920', 'name' => '잡손실'],
|
||||
];
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $codes
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 입출금 내역 저장 (계정과목 포함)
|
||||
*/
|
||||
public function save(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
|
||||
$transactions = $request->input('transactions', []);
|
||||
|
||||
if (empty($transactions)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => '저장할 데이터가 없습니다.'
|
||||
]);
|
||||
}
|
||||
|
||||
$saved = 0;
|
||||
$updated = 0;
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
foreach ($transactions as $trans) {
|
||||
// 거래일시 생성
|
||||
$transDt = ($trans['transDate'] ?? '') . ($trans['transTime'] ?? '');
|
||||
|
||||
$data = [
|
||||
'tenant_id' => $tenantId,
|
||||
'bank_account_num' => $trans['bankAccountNum'] ?? '',
|
||||
'bank_code' => $trans['bankCode'] ?? '',
|
||||
'bank_name' => $trans['bankName'] ?? '',
|
||||
'trans_date' => $trans['transDate'] ?? '',
|
||||
'trans_time' => $trans['transTime'] ?? '',
|
||||
'trans_dt' => $transDt,
|
||||
'deposit' => floatval($trans['deposit'] ?? 0),
|
||||
'withdraw' => floatval($trans['withdraw'] ?? 0),
|
||||
'balance' => floatval($trans['balance'] ?? 0),
|
||||
'summary' => $trans['summary'] ?? '',
|
||||
'cast' => $trans['cast'] ?? '',
|
||||
'memo' => $trans['memo'] ?? '',
|
||||
'trans_office' => $trans['transOffice'] ?? '',
|
||||
'account_code' => $trans['accountCode'] ?? null,
|
||||
'account_name' => $trans['accountName'] ?? null,
|
||||
];
|
||||
|
||||
// Upsert: 있으면 업데이트, 없으면 생성
|
||||
$existing = BankTransaction::where('tenant_id', $tenantId)
|
||||
->where('bank_account_num', $data['bank_account_num'])
|
||||
->where('trans_dt', $transDt)
|
||||
->where('deposit', $data['deposit'])
|
||||
->where('withdraw', $data['withdraw'])
|
||||
->where('balance', $data['balance'])
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
// 계정과목만 업데이트
|
||||
$existing->update([
|
||||
'account_code' => $data['account_code'],
|
||||
'account_name' => $data['account_name'],
|
||||
]);
|
||||
$updated++;
|
||||
} else {
|
||||
BankTransaction::create($data);
|
||||
$saved++;
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => "저장 완료: 신규 {$saved}건, 수정 {$updated}건",
|
||||
'saved' => $saved,
|
||||
'updated' => $updated
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
Log::error('입출금 내역 저장 오류: ' . $e->getMessage());
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => '저장 오류: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 엑셀 다운로드
|
||||
*/
|
||||
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'));
|
||||
$accountNum = $request->input('accountNum', '');
|
||||
|
||||
// DB에서 저장된 데이터 조회
|
||||
$query = BankTransaction::where('tenant_id', $tenantId)
|
||||
->whereBetween('trans_date', [$startDate, $endDate])
|
||||
->orderBy('trans_date', 'desc')
|
||||
->orderBy('trans_time', 'desc');
|
||||
|
||||
if (!empty($accountNum)) {
|
||||
$query->where('bank_account_num', $accountNum);
|
||||
}
|
||||
|
||||
$transactions = $query->get();
|
||||
|
||||
// 데이터가 없으면 바로빌에서 조회 (저장 안된 경우)
|
||||
if ($transactions->isEmpty()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => '저장된 데이터가 없습니다. 먼저 데이터를 조회하고 저장해주세요.'
|
||||
]);
|
||||
}
|
||||
|
||||
$filename = "입출금내역_{$startDate}_{$endDate}.csv";
|
||||
|
||||
return response()->streamDownload(function () use ($transactions) {
|
||||
$handle = fopen('php://output', 'w');
|
||||
|
||||
// UTF-8 BOM for Excel
|
||||
fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||
|
||||
// 헤더
|
||||
fputcsv($handle, [
|
||||
'거래일시',
|
||||
'은행명',
|
||||
'계좌번호',
|
||||
'적요',
|
||||
'입금',
|
||||
'출금',
|
||||
'잔액',
|
||||
'상대방',
|
||||
'계정과목코드',
|
||||
'계정과목명'
|
||||
]);
|
||||
|
||||
// 데이터
|
||||
foreach ($transactions as $trans) {
|
||||
$dateTime = '';
|
||||
if ($trans->trans_date) {
|
||||
$dateTime = substr($trans->trans_date, 0, 4) . '-' .
|
||||
substr($trans->trans_date, 4, 2) . '-' .
|
||||
substr($trans->trans_date, 6, 2);
|
||||
if ($trans->trans_time) {
|
||||
$dateTime .= ' ' . substr($trans->trans_time, 0, 2) . ':' .
|
||||
substr($trans->trans_time, 2, 2) . ':' .
|
||||
substr($trans->trans_time, 4, 2);
|
||||
}
|
||||
}
|
||||
|
||||
fputcsv($handle, [
|
||||
$dateTime,
|
||||
$trans->bank_name,
|
||||
$trans->bank_account_num,
|
||||
$trans->summary,
|
||||
$trans->deposit,
|
||||
$trans->withdraw,
|
||||
$trans->balance,
|
||||
$trans->cast,
|
||||
$trans->account_code,
|
||||
$trans->account_name
|
||||
]);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}, $filename, [
|
||||
'Content-Type' => 'text/csv; charset=utf-8',
|
||||
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('엑셀 다운로드 오류: ' . $e->getMessage());
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => '다운로드 오류: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SOAP 호출
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user