input('search')) { $query->where(function ($q) use ($search) { $q->where('partner_name', 'like', "%{$search}%"); }); } if ($period = $request->input('period')) { $query->where('period', $period); } if ($type = $request->input('type')) { if ($type !== 'all') { $query->where('type', $type); } } if ($taxType = $request->input('tax_type')) { if ($taxType !== 'all') { $query->where('tax_type', $taxType); } } if ($status = $request->input('status')) { if ($status !== 'all') { $query->where('status', $status); } } $records = $query->orderBy('invoice_date', 'desc') ->get() ->map(function ($record) { return [ 'id' => $record->id, 'period' => $record->period, 'type' => $record->type, 'taxType' => $record->tax_type ?? 'taxable', 'partnerName' => $record->partner_name, 'invoiceNo' => $record->invoice_no, 'invoiceDate' => $record->invoice_date?->format('Y-m-d'), 'supplyAmount' => $record->supply_amount, 'vatAmount' => $record->vat_amount, 'totalAmount' => $record->total_amount, 'status' => $record->status, 'memo' => $record->memo, 'isCardTransaction' => false, ]; }); // 카드 공제분 매입 반영 $cardRecords = collect(); $cardPurchaseSupply = 0; $cardPurchaseVat = 0; 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(); $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, 'cardPurchaseSupply' => $cardPurchaseSupply, 'cardPurchaseVat' => $cardPurchaseVat, 'total' => $periodRecords->count(), ]; // 사용 중인 기간 목록 $periods = VatRecord::forTenant($tenantId) ->select('period') ->distinct() ->orderBy('period', 'desc') ->pluck('period') ->toArray(); return response()->json([ 'success' => true, 'data' => $allRecords, 'stats' => $stats, 'periods' => $periods, ]); } public function store(Request $request): JsonResponse { $request->validate([ 'partnerName' => 'required|string|max:100', 'period' => 'required|string|max:20', 'type' => 'required|in:sales,purchase', 'taxType' => 'nullable|in:taxable,zero_rated,exempt', 'supplyAmount' => 'required|integer|min:0', ]); $tenantId = session('selected_tenant_id', 1); $record = VatRecord::create([ 'tenant_id' => $tenantId, 'period' => $request->input('period'), 'type' => $request->input('type', 'sales'), 'tax_type' => $request->input('taxType', 'taxable'), 'partner_name' => $request->input('partnerName'), 'invoice_no' => $request->input('invoiceNo'), 'invoice_date' => $request->input('invoiceDate'), 'supply_amount' => $request->input('supplyAmount', 0), 'vat_amount' => $request->input('vatAmount', 0), 'total_amount' => $request->input('totalAmount', 0), 'status' => $request->input('status', 'pending'), 'memo' => $request->input('memo'), ]); return response()->json([ 'success' => true, 'message' => '세금계산서가 등록되었습니다.', 'data' => ['id' => $record->id], ]); } public function update(Request $request, int $id): JsonResponse { $tenantId = session('selected_tenant_id', 1); $record = VatRecord::forTenant($tenantId)->findOrFail($id); $request->validate([ 'partnerName' => 'required|string|max:100', 'period' => 'required|string|max:20', 'type' => 'required|in:sales,purchase', 'taxType' => 'nullable|in:taxable,zero_rated,exempt', 'supplyAmount' => 'required|integer|min:0', ]); $record->update([ 'period' => $request->input('period'), 'type' => $request->input('type'), 'tax_type' => $request->input('taxType', $record->tax_type), 'partner_name' => $request->input('partnerName'), 'invoice_no' => $request->input('invoiceNo'), 'invoice_date' => $request->input('invoiceDate'), 'supply_amount' => $request->input('supplyAmount', 0), 'vat_amount' => $request->input('vatAmount', 0), 'total_amount' => $request->input('totalAmount', 0), 'status' => $request->input('status'), 'memo' => $request->input('memo'), ]); return response()->json([ 'success' => true, 'message' => '세금계산서가 수정되었습니다.', ]); } public function destroy(int $id): JsonResponse { $tenantId = session('selected_tenant_id', 1); $record = VatRecord::forTenant($tenantId)->findOrFail($id); $record->delete(); return response()->json([ 'success' => true, 'message' => '세금계산서가 삭제되었습니다.', ]); } /** * 부가세 신고기간을 날짜 범위로 변환 * YYYY-1P: 1기 예정 (0101~0331) * YYYY-1C: 1기 확정 (0401~0630) * YYYY-2P: 2기 예정 (0701~0930) * YYYY-2C: 2기 확정 (1001~1231) */ private function periodToDateRange(string $period): array { $parts = explode('-', $period); if (count($parts) !== 2) { return [null, null]; } $year = $parts[0]; $code = $parts[1]; $ranges = [ '1P' => ["{$year}-01-01", "{$year}-03-31"], '1C' => ["{$year}-04-01", "{$year}-06-30"], '2P' => ["{$year}-07-01", "{$year}-09-30"], '2C' => ["{$year}-10-01", "{$year}-12-31"], ]; return $ranges[$code] ?? [null, null]; } }