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 @@