diff --git a/app/Http/Controllers/Finance/VatRecordController.php b/app/Http/Controllers/Finance/VatRecordController.php index e6443b07..65c481b3 100644 --- a/app/Http/Controllers/Finance/VatRecordController.php +++ b/app/Http/Controllers/Finance/VatRecordController.php @@ -263,8 +263,15 @@ public function index(Request $request): JsonResponse 'cardPurchaseSupply' => $cardPurchaseSupply, 'cardPurchaseVat' => $cardPurchaseVat, 'total' => $allRecords->count(), + 'preliminaryVat' => null, ]; + // 확정(C) 기간이면 대응하는 예정(P)의 netVat를 계산 + if ($period && str_ends_with($period, 'C')) { + $prelimPeriod = substr($period, 0, -1) . 'P'; + $stats['preliminaryVat'] = $this->calculatePeriodNetVat($tenantId, $prelimPeriod); + } + // 사용 중인 기간 목록 $periods = VatRecord::forTenant($tenantId) ->select('period') @@ -360,6 +367,99 @@ public function destroy(int $id): JsonResponse ]); } + /** + * 특정 기간의 순 부가세 계산 (매출세액 - 매입세액) + * 양수: 납부세액, 음수: 환급세액 + */ + private function calculatePeriodNetVat(int $tenantId, string $period): int + { + [$startDate, $endDate] = $this->periodToDateRange($period); + if (!$startDate || !$endDate) { + return 0; + } + + $startDateYmd = str_replace('-', '', $startDate); + $endDateYmd = str_replace('-', '', $endDate); + + $taxTypeMap = [1 => 'taxable', 2 => 'zero_rated', 3 => 'exempt']; + + // 홈택스 매출세액 (과세+영세) + $salesVat = (int) HometaxInvoice::where('tenant_id', $tenantId) + ->sales() + ->period($startDate, $endDate) + ->whereIn('tax_type', [1, 2]) + ->sum('tax_amount'); + + // 홈택스 매입세액 (과세+영세) + $purchaseVat = (int) HometaxInvoice::where('tenant_id', $tenantId) + ->purchase() + ->period($startDate, $endDate) + ->whereIn('tax_type', [1, 2]) + ->sum('tax_amount'); + + // 카드 매입세액 (공제분만) + $cardVat = 0; + $hiddenKeys = CardTransactionHide::getHiddenKeys($tenantId, $startDateYmd, $endDateYmd); + + $cardTransactions = BarobillCardTransaction::where('tenant_id', $tenantId) + ->whereBetween('use_date', [$startDateYmd, $endDateYmd]) + ->get(); + + $splitsByKey = CardTransactionSplit::getByDateRange($tenantId, $startDateYmd, $endDateYmd); + + $splitsByPartialKey = []; + foreach ($splitsByKey as $fullKey => $splits) { + $parts = explode('|', $fullKey); + if (count($parts) >= 3) { + $partialKey = $parts[0] . '|' . $parts[1] . '|' . $parts[2]; + $splitsByPartialKey[$partialKey] = $splits; + } + } + + foreach ($cardTransactions as $card) { + if ($hiddenKeys->contains($card->unique_key)) { + continue; + } + + $splits = $splitsByKey[$card->unique_key] ?? null; + if (!$splits) { + $cardPartialKey = $card->card_num . '|' . $card->use_dt . '|' . $card->approval_num; + $splits = $splitsByPartialKey[$cardPartialKey] ?? null; + } + + if ($splits && count($splits) > 0) { + foreach ($splits as $split) { + if ($split->deduction_type === 'deductible') { + $cardVat += (int) $split->split_tax; + } + } + } else { + if ($card->deduction_type === 'deductible') { + $cardVat += (int) ($card->modified_tax ?? $card->tax); + } + } + } + + // 수동입력 매출세액 (과세+영세) + $manualSalesVat = (int) VatRecord::forTenant($tenantId) + ->where('period', $period) + ->where('type', 'sales') + ->whereIn('tax_type', ['taxable', 'zero_rated']) + ->sum('vat_amount'); + + // 수동입력 매입세액 (과세+영세) + $manualPurchaseVat = (int) VatRecord::forTenant($tenantId) + ->where('period', $period) + ->where('type', 'purchase') + ->whereIn('tax_type', ['taxable', 'zero_rated']) + ->sum('vat_amount'); + + $totalSalesVat = $salesVat + $manualSalesVat; + $totalPurchaseVat = $purchaseVat + $cardVat + $manualPurchaseVat; + + return $totalSalesVat - $totalPurchaseVat; + } + /** * 부가세 신고기간을 날짜 범위로 변환 * YYYY-1P: 1기 예정 (0101~0331) diff --git a/resources/views/finance/vat.blade.php b/resources/views/finance/vat.blade.php index c83d143b..5ef4aa70 100644 --- a/resources/views/finance/vat.blade.php +++ b/resources/views/finance/vat.blade.php @@ -183,6 +183,14 @@ function VatManagement() { const cardPurchaseVat = stats.cardPurchaseVat || 0; const netVat = salesVat - purchaseVat; + // 확정 기간일 때 예정 세액 차감 + const isConfirmedPeriod = filterPeriod.endsWith('C'); + const preliminaryVat = stats.preliminaryVat; // 양수=납부, 음수=환급, null=예정아님 + const hasPreliminary = isConfirmedPeriod && preliminaryVat !== null && preliminaryVat !== undefined; + // 예정 납부세액이든 환급세액이든 절대값으로 차감 (이미 정산된 금액) + const preliminaryDeduction = hasPreliminary ? Math.abs(preliminaryVat) : 0; + const finalNetVat = netVat - preliminaryDeduction; + const handleAdd = () => { setModalMode('add'); setFormData({ ...initialFormState, period: filterPeriod }); setShowModal(true); }; const handleEdit = (item) => { if (item.isCardTransaction || item.isHometax) return; @@ -346,9 +354,12 @@ function VatManagement() {
{formatCurrency(purchaseVat)}원
공급가액: {formatCurrency(purchaseSupply)}원
-= 0 ? 'text-amber-600' : 'text-blue-600'}`}>{formatCurrency(Math.abs(netVat))}원
+= 0 ? 'text-amber-600' : 'text-blue-600'}`}>{formatCurrency(Math.abs(hasPreliminary ? finalNetVat : netVat))}원
+ {hasPreliminary && preliminaryDeduction > 0 && ( +예정 차감 전: {formatCurrency(Math.abs(netVat))}원
+ )}