diff --git a/app/Http/Controllers/Barobill/EaccountController.php b/app/Http/Controllers/Barobill/EaccountController.php index b81e7422..54859ece 100644 --- a/app/Http/Controllers/Barobill/EaccountController.php +++ b/app/Http/Controllers/Barobill/EaccountController.php @@ -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 호출 */ diff --git a/app/Models/Barobill/BankTransaction.php b/app/Models/Barobill/BankTransaction.php new file mode 100644 index 00000000..a6d833e6 --- /dev/null +++ b/app/Models/Barobill/BankTransaction.php @@ -0,0 +1,96 @@ + 'decimal:2', + 'withdraw' => 'decimal:2', + 'balance' => 'decimal:2', + ]; + + /** + * 테넌트 관계 + */ + public function tenant(): BelongsTo + { + return $this->belongsTo(Tenant::class); + } + + /** + * 거래 고유 키 생성 (매칭용) + */ + public function getUniqueKeyAttribute(): string + { + return implode('|', [ + $this->bank_account_num, + $this->trans_dt, + $this->deposit, + $this->withdraw, + $this->balance, + ]); + } + + /** + * 바로빌 로그 데이터로부터 고유 키 생성 (정적 메서드) + */ + public static function generateUniqueKey(array $log): string + { + // trans_dt 생성: transDate + transTime + $transDt = ($log['transDate'] ?? '') . ($log['transTime'] ?? ''); + + return implode('|', [ + $log['bankAccountNum'] ?? '', + $transDt, + $log['deposit'] ?? 0, + $log['withdraw'] ?? 0, + $log['balance'] ?? 0, + ]); + } + + /** + * 테넌트별 거래 내역 조회 (기간별) + */ + public static function getByDateRange(int $tenantId, string $startDate, string $endDate, ?string $accountNum = null) + { + $query = self::where('tenant_id', $tenantId) + ->whereBetween('trans_date', [$startDate, $endDate]) + ->orderBy('trans_date', 'desc') + ->orderBy('trans_time', 'desc'); + + if ($accountNum) { + $query->where('bank_account_num', $accountNum); + } + + return $query->get()->keyBy(fn($item) => $item->unique_key); + } +} diff --git a/resources/views/barobill/eaccount/index.blade.php b/resources/views/barobill/eaccount/index.blade.php index 3ffaf3b3..7227dbb6 100644 --- a/resources/views/barobill/eaccount/index.blade.php +++ b/resources/views/barobill/eaccount/index.blade.php @@ -63,12 +63,15 @@