feat:계좌 입출금내역 분개(Split) 기능 구현

- BankTransactionSplit 모델 생성
- EaccountController에 splits/saveSplits/deleteSplits 메서드 추가
- 라우트 3개 추가 (GET/POST/DELETE splits)
- BankSplitModal React 컴포넌트 추가
- TransactionTable에 분개 컬럼/하위행 렌더링
- App 컴포넌트에 분개 상태 및 핸들러 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-06 14:43:21 +09:00
parent 6520923def
commit ce08d0110a
4 changed files with 677 additions and 12 deletions

View File

@@ -8,6 +8,7 @@
use App\Models\Barobill\BarobillMember;
use App\Models\Barobill\BankTransaction;
use App\Models\Barobill\BankTransactionOverride;
use App\Models\Barobill\BankTransactionSplit;
use App\Models\Tenants\Tenant;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -1286,6 +1287,115 @@ public function destroyManual(int $id): JsonResponse
}
}
/**
* 분개 내역 조회
*/
public function splits(Request $request): JsonResponse
{
try {
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
$startDate = $request->input('startDate', date('Ymd'));
$endDate = $request->input('endDate', date('Ymd'));
$splits = BankTransactionSplit::getByDateRange($tenantId, $startDate, $endDate);
return response()->json([
'success' => true,
'data' => $splits
]);
} catch (\Throwable $e) {
Log::error('계좌 분개 내역 조회 오류: ' . $e->getMessage());
return response()->json([
'success' => false,
'error' => '조회 오류: ' . $e->getMessage()
]);
}
}
/**
* 분개 저장
*/
public function saveSplits(Request $request): JsonResponse
{
try {
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
$uniqueKey = $request->input('uniqueKey');
$originalData = $request->input('originalData', []);
$splits = $request->input('splits', []);
if (empty($uniqueKey)) {
return response()->json([
'success' => false,
'error' => '고유키가 없습니다.'
]);
}
// 분개 금액 합계 검증
$originalAmount = floatval($originalData['originalAmount'] ?? 0);
$splitTotal = array_sum(array_map(function ($s) {
return floatval($s['amount'] ?? 0);
}, $splits));
if (abs($originalAmount - $splitTotal) > 0.01) {
return response()->json([
'success' => false,
'error' => "분개 금액 합계({$splitTotal})가 원본 금액({$originalAmount})과 일치하지 않습니다."
]);
}
DB::beginTransaction();
BankTransactionSplit::saveSplits($tenantId, $uniqueKey, $originalData, $splits);
DB::commit();
return response()->json([
'success' => true,
'message' => '분개가 저장되었습니다.',
'splitCount' => count($splits)
]);
} catch (\Throwable $e) {
DB::rollBack();
Log::error('계좌 분개 저장 오류: ' . $e->getMessage());
return response()->json([
'success' => false,
'error' => '저장 오류: ' . $e->getMessage()
]);
}
}
/**
* 분개 삭제 (원본으로 복원)
*/
public function deleteSplits(Request $request): JsonResponse
{
try {
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
$uniqueKey = $request->input('uniqueKey');
if (empty($uniqueKey)) {
return response()->json([
'success' => false,
'error' => '고유키가 없습니다.'
]);
}
$deleted = BankTransactionSplit::deleteSplits($tenantId, $uniqueKey);
return response()->json([
'success' => true,
'message' => '분개가 삭제되었습니다.',
'deleted' => $deleted
]);
} catch (\Throwable $e) {
Log::error('계좌 분개 삭제 오류: ' . $e->getMessage());
return response()->json([
'success' => false,
'error' => '삭제 오류: ' . $e->getMessage()
]);
}
}
/**
* 병합된 로그에서 수동입력 건의 잔액을 직전 거래 기준으로 재계산
* 로그는 날짜 내림차순(DESC) 정렬 상태로 전달됨