feat:카드 사용내역 분개 기능 추가

- CardTransactionSplit 모델 및 마이그레이션 추가
- EcardController에 splits, saveSplits, deleteSplits API 추가
- 분개 라우트 추가 (GET/POST/DELETE /barobill/ecard/splits)
- 프론트엔드 SplitModal 컴포넌트 추가
- 각 거래 행에 +/- 버튼으로 분개 추가/삭제
- 분개 행 시각적 구분 (들여쓰기, 배경색)
- 분개 금액 합계 검증
- 고유키(cardNum|useDt|approvalNum|amount)로 원본 데이터 연결

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
pro
2026-01-23 14:08:44 +09:00
parent 4167fe1c8d
commit d868e8d0e9
5 changed files with 720 additions and 60 deletions

View File

@@ -7,6 +7,7 @@
use App\Models\Barobill\BarobillConfig;
use App\Models\Barobill\BarobillMember;
use App\Models\Barobill\CardTransaction;
use App\Models\Barobill\CardTransactionSplit;
use App\Models\Tenants\Tenant;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -855,6 +856,113 @@ public function exportExcel(Request $request): StreamedResponse|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 = CardTransactionSplit::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(fn($s) => floatval($s['amount'] ?? 0), $splits));
if (abs($originalAmount - $splitTotal) > 0.01) {
return response()->json([
'success' => false,
'error' => "분개 금액 합계({$splitTotal})가 원본 금액({$originalAmount})과 일치하지 않습니다."
]);
}
DB::beginTransaction();
CardTransactionSplit::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 = CardTransactionSplit::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()
]);
}
}
/**
* SOAP 호출
*/