feat:부가세관리 외부 데이터 소스 연동 (홈택스/바로빌 카드)
홈택스 매출/매입 세금계산서 + 바로빌 카드 공제분 자동 조회 기능 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,10 @@
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Finance\VatRecord;
|
||||
use App\Models\Finance\CardTransaction;
|
||||
use App\Models\Barobill\CardTransaction as BarobillCardTransaction;
|
||||
use App\Models\Barobill\CardTransactionSplit;
|
||||
use App\Models\Barobill\CardTransactionHide;
|
||||
use App\Models\Barobill\HometaxInvoice;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -13,38 +16,157 @@ class VatRecordController extends Controller
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
$period = $request->input('period');
|
||||
|
||||
$query = VatRecord::forTenant($tenantId);
|
||||
// Step 1: 기간 → 날짜 범위 변환
|
||||
[$startDate, $endDate] = $period ? $this->periodToDateRange($period) : [null, null];
|
||||
$startDateYmd = $startDate ? str_replace('-', '', $startDate) : null;
|
||||
$endDateYmd = $endDate ? str_replace('-', '', $endDate) : null;
|
||||
|
||||
if ($search = $request->input('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('partner_name', 'like', "%{$search}%");
|
||||
$taxTypeMap = [
|
||||
1 => 'taxable',
|
||||
2 => 'zero_rated',
|
||||
3 => 'exempt',
|
||||
];
|
||||
|
||||
// Step 2: 홈택스 매출 조회
|
||||
$hometaxSalesRecords = collect();
|
||||
$hometaxPurchaseRecords = collect();
|
||||
$cardRecords = collect();
|
||||
|
||||
if ($startDate && $endDate) {
|
||||
// 홈택스 매출
|
||||
$hometaxSales = HometaxInvoice::where('tenant_id', $tenantId)
|
||||
->sales()
|
||||
->period($startDate, $endDate)
|
||||
->get();
|
||||
|
||||
$hometaxSalesRecords = $hometaxSales->map(function ($inv) use ($period, $taxTypeMap) {
|
||||
return [
|
||||
'id' => 'hometax_' . $inv->id,
|
||||
'period' => $period,
|
||||
'type' => 'sales',
|
||||
'taxType' => $taxTypeMap[$inv->tax_type] ?? 'taxable',
|
||||
'partnerName' => $inv->invoicee_corp_name,
|
||||
'invoiceNo' => $inv->nts_confirm_num ?? '',
|
||||
'invoiceDate' => $inv->write_date ? \Carbon\Carbon::parse($inv->write_date)->format('Y-m-d') : null,
|
||||
'supplyAmount' => (int) $inv->supply_amount,
|
||||
'vatAmount' => (int) $inv->tax_amount,
|
||||
'totalAmount' => (int) $inv->total_amount,
|
||||
'status' => 'filed',
|
||||
'memo' => null,
|
||||
'isCardTransaction' => false,
|
||||
'isHometax' => true,
|
||||
'source' => 'hometax',
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
if ($period = $request->input('period')) {
|
||||
$query->where('period', $period);
|
||||
}
|
||||
// Step 3: 홈택스 매입 조회
|
||||
$hometaxPurchases = HometaxInvoice::where('tenant_id', $tenantId)
|
||||
->purchase()
|
||||
->period($startDate, $endDate)
|
||||
->get();
|
||||
|
||||
if ($type = $request->input('type')) {
|
||||
if ($type !== 'all') {
|
||||
$query->where('type', $type);
|
||||
$hometaxPurchaseRecords = $hometaxPurchases->map(function ($inv) use ($period, $taxTypeMap) {
|
||||
return [
|
||||
'id' => 'hometax_' . $inv->id,
|
||||
'period' => $period,
|
||||
'type' => 'purchase',
|
||||
'taxType' => $taxTypeMap[$inv->tax_type] ?? 'taxable',
|
||||
'partnerName' => $inv->invoicer_corp_name,
|
||||
'invoiceNo' => $inv->nts_confirm_num ?? '',
|
||||
'invoiceDate' => $inv->write_date ? \Carbon\Carbon::parse($inv->write_date)->format('Y-m-d') : null,
|
||||
'supplyAmount' => (int) $inv->supply_amount,
|
||||
'vatAmount' => (int) $inv->tax_amount,
|
||||
'totalAmount' => (int) $inv->total_amount,
|
||||
'status' => 'filed',
|
||||
'memo' => null,
|
||||
'isCardTransaction' => false,
|
||||
'isHometax' => true,
|
||||
'source' => 'hometax',
|
||||
];
|
||||
});
|
||||
|
||||
// Step 4: 카드 공제분 조회
|
||||
if ($startDateYmd && $endDateYmd) {
|
||||
$hiddenKeys = CardTransactionHide::getHiddenKeys($tenantId, $startDateYmd, $endDateYmd);
|
||||
|
||||
$cardTransactions = BarobillCardTransaction::where('tenant_id', $tenantId)
|
||||
->whereBetween('use_date', [$startDateYmd, $endDateYmd])
|
||||
->orderBy('use_date', 'desc')
|
||||
->get();
|
||||
|
||||
$splitsByKey = CardTransactionSplit::getByDateRange($tenantId, $startDateYmd, $endDateYmd);
|
||||
|
||||
foreach ($cardTransactions as $card) {
|
||||
// 숨김 처리된 거래는 skip
|
||||
if ($hiddenKeys->contains($card->unique_key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$splits = $splitsByKey->get($card->unique_key);
|
||||
|
||||
if ($splits && $splits->count() > 0) {
|
||||
// 분개가 있으면: deductible 분개만 포함
|
||||
foreach ($splits as $split) {
|
||||
if ($split->deduction_type === 'deductible') {
|
||||
$cardRecords->push([
|
||||
'id' => 'card_split_' . $split->id,
|
||||
'period' => $period,
|
||||
'type' => 'purchase',
|
||||
'taxType' => 'taxable',
|
||||
'partnerName' => $card->merchant_name,
|
||||
'invoiceNo' => $card->approval_num ?? '',
|
||||
'invoiceDate' => $card->use_date ? \Carbon\Carbon::createFromFormat('Ymd', $card->use_date)->format('Y-m-d') : null,
|
||||
'supplyAmount' => (int) $split->split_supply_amount,
|
||||
'vatAmount' => (int) $split->split_tax,
|
||||
'totalAmount' => (int) $split->split_amount,
|
||||
'status' => 'filed',
|
||||
'memo' => $split->account_name ?? null,
|
||||
'isCardTransaction' => true,
|
||||
'isHometax' => false,
|
||||
'source' => 'card',
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 분개가 없으면: deduction_type='deductible'인 경우만 포함
|
||||
if ($card->deduction_type === 'deductible') {
|
||||
$effectiveSupply = $card->modified_supply_amount
|
||||
?? ($card->approval_amount - $card->tax);
|
||||
$effectiveTax = $card->modified_tax ?? $card->tax;
|
||||
|
||||
$cardRecords->push([
|
||||
'id' => 'card_' . $card->id,
|
||||
'period' => $period,
|
||||
'type' => 'purchase',
|
||||
'taxType' => 'taxable',
|
||||
'partnerName' => $card->merchant_name,
|
||||
'invoiceNo' => $card->approval_num ?? '',
|
||||
'invoiceDate' => $card->use_date ? \Carbon\Carbon::createFromFormat('Ymd', $card->use_date)->format('Y-m-d') : null,
|
||||
'supplyAmount' => (int) $effectiveSupply,
|
||||
'vatAmount' => (int) $effectiveTax,
|
||||
'totalAmount' => (int) ($effectiveSupply + $effectiveTax),
|
||||
'status' => 'filed',
|
||||
'memo' => $card->memo,
|
||||
'isCardTransaction' => true,
|
||||
'isHometax' => false,
|
||||
'source' => 'card',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($taxType = $request->input('tax_type')) {
|
||||
if ($taxType !== 'all') {
|
||||
$query->where('tax_type', $taxType);
|
||||
}
|
||||
// Step 5: 수동 입력 (vat_records) - 기존 유지
|
||||
$manualQuery = VatRecord::forTenant($tenantId);
|
||||
|
||||
if ($period) {
|
||||
$manualQuery->where('period', $period);
|
||||
}
|
||||
|
||||
if ($status = $request->input('status')) {
|
||||
if ($status !== 'all') {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
}
|
||||
|
||||
$records = $query->orderBy('invoice_date', 'desc')
|
||||
$manualRecords = $manualQuery->orderBy('invoice_date', 'desc')
|
||||
->get()
|
||||
->map(function ($record) {
|
||||
return [
|
||||
@@ -61,67 +183,39 @@ public function index(Request $request): JsonResponse
|
||||
'status' => $record->status,
|
||||
'memo' => $record->memo,
|
||||
'isCardTransaction' => false,
|
||||
'isHometax' => false,
|
||||
'source' => 'manual',
|
||||
];
|
||||
});
|
||||
|
||||
// 카드 공제분 매입 반영
|
||||
$cardRecords = collect();
|
||||
$cardPurchaseSupply = 0;
|
||||
$cardPurchaseVat = 0;
|
||||
// Step 6: 통합 및 통계
|
||||
$allRecords = $hometaxSalesRecords
|
||||
->concat($hometaxPurchaseRecords)
|
||||
->concat($cardRecords)
|
||||
->concat($manualRecords)
|
||||
->values();
|
||||
|
||||
if ($period = $request->input('period')) {
|
||||
[$startDate, $endDate] = $this->periodToDateRange($period);
|
||||
|
||||
if ($startDate && $endDate) {
|
||||
$cardQuery = CardTransaction::forTenant($tenantId)
|
||||
->where('deduction_type', 'deductible')
|
||||
->whereBetween('transaction_date', [$startDate, $endDate]);
|
||||
|
||||
$cardTransactions = $cardQuery->orderBy('transaction_date', 'desc')->get();
|
||||
|
||||
$cardRecords = $cardTransactions->map(function ($card) use ($period) {
|
||||
$supply = (int) round($card->amount / 1.1);
|
||||
$vat = $card->amount - $supply;
|
||||
return [
|
||||
'id' => 'card_' . $card->id,
|
||||
'period' => $period,
|
||||
'type' => 'purchase',
|
||||
'taxType' => 'taxable',
|
||||
'partnerName' => $card->merchant,
|
||||
'invoiceNo' => $card->approval_no ?? '',
|
||||
'invoiceDate' => $card->transaction_date?->format('Y-m-d'),
|
||||
'supplyAmount' => $supply,
|
||||
'vatAmount' => $vat,
|
||||
'totalAmount' => $card->amount,
|
||||
'status' => 'filed',
|
||||
'memo' => $card->memo,
|
||||
'isCardTransaction' => true,
|
||||
];
|
||||
});
|
||||
|
||||
$cardPurchaseSupply = $cardRecords->sum('supplyAmount');
|
||||
$cardPurchaseVat = $cardRecords->sum('vatAmount');
|
||||
}
|
||||
}
|
||||
|
||||
// 통합 records
|
||||
$allRecords = $records->concat($cardRecords)->values();
|
||||
|
||||
// 해당 기간 통계
|
||||
$periodQuery = VatRecord::forTenant($tenantId);
|
||||
if ($period = $request->input('period')) {
|
||||
$periodQuery->where('period', $period);
|
||||
}
|
||||
$periodRecords = $periodQuery->get();
|
||||
$hometaxSalesSupply = $hometaxSalesRecords->sum('supplyAmount');
|
||||
$hometaxSalesVat = $hometaxSalesRecords->sum('vatAmount');
|
||||
$hometaxPurchaseSupply = $hometaxPurchaseRecords->sum('supplyAmount');
|
||||
$hometaxPurchaseVat = $hometaxPurchaseRecords->sum('vatAmount');
|
||||
$cardPurchaseSupply = $cardRecords->sum('supplyAmount');
|
||||
$cardPurchaseVat = $cardRecords->sum('vatAmount');
|
||||
$manualSalesSupply = $manualRecords->where('type', 'sales')->sum('supplyAmount');
|
||||
$manualSalesVat = $manualRecords->where('type', 'sales')->sum('vatAmount');
|
||||
$manualPurchaseSupply = $manualRecords->where('type', 'purchase')->sum('supplyAmount');
|
||||
$manualPurchaseVat = $manualRecords->where('type', 'purchase')->sum('vatAmount');
|
||||
|
||||
$stats = [
|
||||
'salesSupply' => $periodRecords->where('type', 'sales')->sum('supply_amount'),
|
||||
'salesVat' => $periodRecords->where('type', 'sales')->sum('vat_amount'),
|
||||
'purchaseSupply' => $periodRecords->where('type', 'purchase')->sum('supply_amount') + $cardPurchaseSupply,
|
||||
'purchaseVat' => $periodRecords->where('type', 'purchase')->sum('vat_amount') + $cardPurchaseVat,
|
||||
'salesSupply' => $hometaxSalesSupply + $manualSalesSupply,
|
||||
'salesVat' => $hometaxSalesVat + $manualSalesVat,
|
||||
'purchaseSupply' => $hometaxPurchaseSupply + $cardPurchaseSupply + $manualPurchaseSupply,
|
||||
'purchaseVat' => $hometaxPurchaseVat + $cardPurchaseVat + $manualPurchaseVat,
|
||||
'hometaxPurchaseSupply' => $hometaxPurchaseSupply,
|
||||
'hometaxPurchaseVat' => $hometaxPurchaseVat,
|
||||
'cardPurchaseSupply' => $cardPurchaseSupply,
|
||||
'cardPurchaseVat' => $cardPurchaseVat,
|
||||
'total' => $periodRecords->count(),
|
||||
'total' => $allRecords->count(),
|
||||
];
|
||||
|
||||
// 사용 중인 기간 목록
|
||||
|
||||
Reference in New Issue
Block a user