Files
sam-api/app/Services/BarobillCardTransactionService.php
김보곤 eeda6d980e feat: [barobill] React 연동용 바로빌 카드/은행/홈택스 REST API 구현
- 바로빌 카드 거래 API (16 엔드포인트): 조회, 분할, 수동입력, 숨김/복원, 금액수정, 분개
- 바로빌 은행 거래 API (13 엔드포인트): 조회, 분할, 오버라이드, 수동입력, 잔액요약, 분개
- 홈택스 세금계산서 API (13 엔드포인트): 매출/매입 조회, 수동입력, 자체분개, 통합분개
- JournalEntry 소스 타입 상수 추가 (barobill_card, barobill_bank, hometax_invoice)
2026-03-11 19:41:07 +09:00

309 lines
10 KiB
PHP

<?php
namespace App\Services;
use App\Models\Barobill\BarobillCardTransaction;
use App\Models\Barobill\BarobillCardTransactionAmountLog;
use App\Models\Barobill\BarobillCardTransactionHide;
use App\Models\Barobill\BarobillCardTransactionSplit;
use Illuminate\Support\Facades\DB;
/**
* 바로빌 카드 거래 서비스 (React 연동용)
*
* MNG에서 동기화된 barobill_card_transactions 데이터를
* React 프론트엔드에서 조회/분개/숨김 등 처리
*/
class BarobillCardTransactionService extends Service
{
/**
* 카드 거래 목록 조회 (기간별, 카드번호별)
*/
public function index(array $params): array
{
$tenantId = $this->tenantId();
$startDate = $params['start_date'] ?? now()->startOfMonth()->format('Y-m-d');
$endDate = $params['end_date'] ?? now()->format('Y-m-d');
$cardNum = $params['card_num'] ?? null;
$search = $params['search'] ?? null;
$includeHidden = $params['include_hidden'] ?? false;
$perPage = $params['per_page'] ?? 50;
$query = BarobillCardTransaction::where('tenant_id', $tenantId)
->whereBetween('use_date', [$startDate, $endDate]);
if ($cardNum) {
$query->where('card_num', $cardNum);
}
if ($search) {
$query->where(function ($q) use ($search) {
$q->where('merchant_name', 'like', "%{$search}%")
->orWhere('memo', 'like', "%{$search}%")
->orWhere('approval_num', 'like', "%{$search}%");
});
}
// 숨김 거래 필터링
if (! $includeHidden) {
$hiddenKeys = BarobillCardTransactionHide::getHiddenKeys($tenantId, $startDate, $endDate);
if (! empty($hiddenKeys)) {
$query->whereNotIn(
DB::raw("CONCAT(card_num, '|', use_dt, '|', approval_num, '|', approval_amount)"),
$hiddenKeys
);
}
}
$query->orderByDesc('use_date')->orderByDesc('use_dt');
$transactions = $query->paginate($perPage);
// 분할 거래 정보 로드
$uniqueKeys = $transactions->getCollection()->map->unique_key->toArray();
$splits = BarobillCardTransactionSplit::where('tenant_id', $tenantId)
->whereIn('original_unique_key', $uniqueKeys)
->orderBy('sort_order')
->get()
->groupBy('original_unique_key');
$transactions->getCollection()->transform(function ($tx) use ($splits) {
$tx->splits = $splits->get($tx->unique_key, collect());
$tx->has_splits = $tx->splits->isNotEmpty();
return $tx;
});
return [
'data' => $transactions,
];
}
/**
* 단일 카드 거래 상세
*/
public function show(int $id): ?BarobillCardTransaction
{
return BarobillCardTransaction::where('tenant_id', $this->tenantId())
->find($id);
}
/**
* 카드 거래 분할 조회
*/
public function getSplits(string $uniqueKey): array
{
$tenantId = $this->tenantId();
$splits = BarobillCardTransactionSplit::getByUniqueKey($tenantId, $uniqueKey);
return ['items' => $splits];
}
/**
* 카드 거래 분할 저장
*/
public function saveSplits(string $uniqueKey, array $items): array
{
$tenantId = $this->tenantId();
return DB::transaction(function () use ($tenantId, $uniqueKey, $items) {
// 기존 분할 삭제
BarobillCardTransactionSplit::where('tenant_id', $tenantId)
->where('original_unique_key', $uniqueKey)
->delete();
$created = [];
foreach ($items as $index => $item) {
$created[] = BarobillCardTransactionSplit::create([
'tenant_id' => $tenantId,
'original_unique_key' => $uniqueKey,
'split_amount' => $item['split_amount'],
'split_supply_amount' => $item['split_supply_amount'] ?? 0,
'split_tax' => $item['split_tax'] ?? 0,
'account_code' => $item['account_code'] ?? null,
'account_name' => $item['account_name'] ?? null,
'deduction_type' => $item['deduction_type'] ?? null,
'evidence_name' => $item['evidence_name'] ?? null,
'description' => $item['description'] ?? null,
'memo' => $item['memo'] ?? null,
'sort_order' => $index + 1,
'card_num' => $item['card_num'] ?? null,
'use_dt' => $item['use_dt'] ?? null,
'use_date' => $item['use_date'] ?? null,
'approval_num' => $item['approval_num'] ?? null,
'original_amount' => $item['original_amount'] ?? 0,
'merchant_name' => $item['merchant_name'] ?? null,
]);
}
return ['items' => $created, 'count' => count($created)];
});
}
/**
* 카드 거래 분할 삭제
*/
public function deleteSplits(string $uniqueKey): array
{
$tenantId = $this->tenantId();
$deleted = BarobillCardTransactionSplit::where('tenant_id', $tenantId)
->where('original_unique_key', $uniqueKey)
->delete();
return ['deleted_count' => $deleted];
}
/**
* 수동 카드 거래 등록
*/
public function storeManual(array $data): BarobillCardTransaction
{
$tenantId = $this->tenantId();
return BarobillCardTransaction::create([
'tenant_id' => $tenantId,
'card_num' => $data['card_num'],
'card_company' => $data['card_company'] ?? null,
'card_company_name' => $data['card_company_name'] ?? null,
'use_dt' => $data['use_dt'],
'use_date' => $data['use_date'],
'use_time' => $data['use_time'] ?? null,
'approval_num' => $data['approval_num'] ?? 'MANUAL-'.now()->format('YmdHis'),
'approval_type' => $data['approval_type'] ?? '1',
'approval_amount' => $data['approval_amount'],
'tax' => $data['tax'] ?? 0,
'service_charge' => $data['service_charge'] ?? 0,
'payment_plan' => $data['payment_plan'] ?? null,
'merchant_name' => $data['merchant_name'],
'merchant_biz_num' => $data['merchant_biz_num'] ?? null,
'account_code' => $data['account_code'] ?? null,
'account_name' => $data['account_name'] ?? null,
'deduction_type' => $data['deduction_type'] ?? null,
'evidence_name' => $data['evidence_name'] ?? null,
'description' => $data['description'] ?? null,
'memo' => $data['memo'] ?? null,
'is_manual' => true,
]);
}
/**
* 수동 카드 거래 수정
*/
public function updateManual(int $id, array $data): BarobillCardTransaction
{
$tx = BarobillCardTransaction::where('tenant_id', $this->tenantId())
->where('is_manual', true)
->findOrFail($id);
$tx->update($data);
return $tx->fresh();
}
/**
* 수동 카드 거래 삭제
*/
public function destroyManual(int $id): bool
{
$tx = BarobillCardTransaction::where('tenant_id', $this->tenantId())
->where('is_manual', true)
->findOrFail($id);
return $tx->delete();
}
/**
* 카드 거래 숨김
*/
public function hide(int $id): BarobillCardTransactionHide
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$tx = BarobillCardTransaction::where('tenant_id', $tenantId)->findOrFail($id);
return BarobillCardTransactionHide::hideTransaction($tenantId, $tx->unique_key, [
'card_num' => $tx->card_num,
'use_date' => $tx->use_date,
'approval_num' => $tx->approval_num,
'approval_amount' => $tx->approval_amount,
'merchant_name' => $tx->merchant_name,
], $userId);
}
/**
* 카드 거래 숨김 복원
*/
public function restore(int $id): bool
{
$tenantId = $this->tenantId();
$tx = BarobillCardTransaction::where('tenant_id', $tenantId)->findOrFail($id);
return BarobillCardTransactionHide::restoreTransaction($tenantId, $tx->unique_key);
}
/**
* 숨겨진 거래 목록
*/
public function hiddenList(array $params): array
{
$tenantId = $this->tenantId();
$startDate = $params['start_date'] ?? now()->startOfMonth()->format('Y-m-d');
$endDate = $params['end_date'] ?? now()->format('Y-m-d');
$hiddenItems = BarobillCardTransactionHide::where('tenant_id', $tenantId)
->whereBetween('use_date', [$startDate, $endDate])
->orderByDesc('created_at')
->get();
return ['items' => $hiddenItems];
}
/**
* 금액 수정 (공급가액/세액 수정)
*/
public function updateAmount(int $id, array $data): BarobillCardTransaction
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
$tx = BarobillCardTransaction::where('tenant_id', $tenantId)->findOrFail($id);
// 변경 이력 기록
BarobillCardTransactionAmountLog::create([
'card_transaction_id' => $tx->id,
'original_unique_key' => $tx->unique_key,
'before_supply_amount' => $tx->modified_supply_amount ?? $tx->approval_amount,
'before_tax' => $tx->modified_tax ?? $tx->tax,
'after_supply_amount' => $data['supply_amount'],
'after_tax' => $data['tax'],
'modified_by' => $userId,
'modified_by_name' => $data['modified_by_name'] ?? '',
'ip_address' => request()->ip(),
]);
$tx->update([
'modified_supply_amount' => $data['supply_amount'],
'modified_tax' => $data['tax'],
]);
return $tx->fresh();
}
/**
* 카드 번호 목록 (필터용)
*/
public function cardNumbers(): array
{
$tenantId = $this->tenantId();
$cards = BarobillCardTransaction::where('tenant_id', $tenantId)
->select('card_num', 'card_company_name')
->distinct()
->orderBy('card_num')
->get();
return ['items' => $cards];
}
}