validate([ 'start_date' => 'nullable|date', 'end_date' => 'nullable|date|after_or_equal:start_date', 'search' => 'nullable|string|max:100', 'per_page' => 'nullable|integer|min:1|max:100', 'page' => 'nullable|integer|min:1', ]); return $this->service->sales($params); }, __('message.fetched')); } /** * 매입 세금계산서 목록 */ public function purchases(Request $request): JsonResponse { return ApiResponse::handle(function () use ($request) { $params = $request->validate([ 'start_date' => 'nullable|date', 'end_date' => 'nullable|date|after_or_equal:start_date', 'search' => 'nullable|string|max:100', 'per_page' => 'nullable|integer|min:1|max:100', 'page' => 'nullable|integer|min:1', ]); return $this->service->purchases($params); }, __('message.fetched')); } /** * 세금계산서 상세 조회 */ public function show(int $id): JsonResponse { return ApiResponse::handle(function () use ($id) { $invoice = $this->service->show($id); if (! $invoice) { throw new \Illuminate\Database\Eloquent\ModelNotFoundException; } return $invoice; }, __('message.fetched')); } /** * 요약 통계 (매출/매입 합계) */ public function summary(Request $request): JsonResponse { return ApiResponse::handle(function () use ($request) { $params = $request->validate([ 'start_date' => 'nullable|date', 'end_date' => 'nullable|date|after_or_equal:start_date', ]); return $this->service->summary($params); }, __('message.fetched')); } // ========================================================================= // 수동 입력 (Manual) // ========================================================================= /** * 수동 세금계산서 등록 */ public function store(Request $request): JsonResponse { return ApiResponse::handle(function () use ($request) { $validated = $request->validate([ 'invoice_type' => 'required|in:sales,purchase', 'nts_confirm_num' => 'nullable|string|max:50', 'write_date' => 'required|date', 'issue_date' => 'nullable|date', 'invoicer_corp_num' => 'nullable|string|max:20', 'invoicer_corp_name' => 'nullable|string|max:200', 'invoicer_ceo_name' => 'nullable|string|max:100', 'invoicee_corp_num' => 'nullable|string|max:20', 'invoicee_corp_name' => 'nullable|string|max:200', 'invoicee_ceo_name' => 'nullable|string|max:100', 'supply_amount' => 'required|integer', 'tax_amount' => 'required|integer', 'total_amount' => 'required|integer', 'tax_type' => 'nullable|string|max:10', 'purpose_type' => 'nullable|string|max:10', 'issue_type' => 'nullable|string|max:10', 'item_name' => 'nullable|string|max:200', 'account_code' => 'nullable|string|max:20', 'account_name' => 'nullable|string|max:100', 'deduction_type' => 'nullable|string|max:20', 'remark1' => 'nullable|string|max:500', ]); return $this->service->storeManual($validated); }, __('message.created')); } /** * 수동 세금계산서 수정 */ public function update(Request $request, int $id): JsonResponse { return ApiResponse::handle(function () use ($request, $id) { $validated = $request->validate([ 'write_date' => 'nullable|date', 'issue_date' => 'nullable|date', 'invoicer_corp_num' => 'nullable|string|max:20', 'invoicer_corp_name' => 'nullable|string|max:200', 'invoicee_corp_num' => 'nullable|string|max:20', 'invoicee_corp_name' => 'nullable|string|max:200', 'supply_amount' => 'nullable|integer', 'tax_amount' => 'nullable|integer', 'total_amount' => 'nullable|integer', 'item_name' => 'nullable|string|max:200', 'account_code' => 'nullable|string|max:20', 'account_name' => 'nullable|string|max:100', 'deduction_type' => 'nullable|string|max:20', 'remark1' => 'nullable|string|max:500', ]); return $this->service->updateManual($id, $validated); }, __('message.updated')); } /** * 수동 세금계산서 삭제 */ public function destroy(int $id): JsonResponse { return ApiResponse::handle(function () use ($id) { return $this->service->destroyManual($id); }, __('message.deleted')); } // ========================================================================= // 분개 (자체 테이블: hometax_invoice_journals) // ========================================================================= /** * 분개 조회 */ public function getJournals(int $id): JsonResponse { return ApiResponse::handle(function () use ($id) { return $this->service->getJournals($id); }, __('message.fetched')); } /** * 분개 저장 */ public function saveJournals(Request $request, int $id): JsonResponse { return ApiResponse::handle(function () use ($request, $id) { $validated = $request->validate([ 'items' => 'required|array|min:1', 'items.*.dc_type' => 'required|in:debit,credit', 'items.*.account_code' => 'required|string|max:20', 'items.*.account_name' => 'nullable|string|max:100', 'items.*.debit_amount' => 'required|integer|min:0', 'items.*.credit_amount' => 'required|integer|min:0', 'items.*.description' => 'nullable|string|max:500', ]); return $this->service->saveJournals($id, $validated['items']); }, __('message.created')); } /** * 분개 삭제 */ public function deleteJournals(int $id): JsonResponse { return ApiResponse::handle(function () use ($id) { return $this->service->deleteJournals($id); }, __('message.deleted')); } // ========================================================================= // 통합 분개 (JournalSyncService - CEO 대시보드 연동) // ========================================================================= /** * 통합 분개 조회 */ public function getJournalEntries(int $id): JsonResponse { return ApiResponse::handle(function () use ($id) { $sourceKey = "hometax_{$id}"; return $this->journalSyncService->getForSource( JournalEntry::SOURCE_HOMETAX_INVOICE, $sourceKey ) ?? ['items' => []]; }, __('message.fetched')); } /** * 통합 분개 저장 */ public function storeJournalEntries(Request $request, int $id): JsonResponse { return ApiResponse::handle(function () use ($request, $id) { $validated = $request->validate([ 'items' => 'required|array|min:1', 'items.*.side' => 'required|in:debit,credit', 'items.*.account_code' => 'required|string|max:20', 'items.*.account_name' => 'nullable|string|max:100', 'items.*.debit_amount' => 'required|integer|min:0', 'items.*.credit_amount' => 'required|integer|min:0', 'items.*.vendor_name' => 'nullable|string|max:200', 'items.*.memo' => 'nullable|string|max:500', ]); $invoice = $this->service->show($id); if (! $invoice) { throw new \Illuminate\Database\Eloquent\ModelNotFoundException; } $entryDate = $invoice->write_date?->format('Y-m-d') ?? now()->format('Y-m-d'); $sourceKey = "hometax_{$id}"; return $this->journalSyncService->saveForSource( JournalEntry::SOURCE_HOMETAX_INVOICE, $sourceKey, $entryDate, "홈택스 세금계산서 분개 (#{$id})", $validated['items'], ); }, __('message.created')); } /** * 통합 분개 삭제 */ public function deleteJournalEntries(int $id): JsonResponse { return ApiResponse::handle(function () use ($id) { $sourceKey = "hometax_{$id}"; return $this->journalSyncService->deleteForSource( JournalEntry::SOURCE_HOMETAX_INVOICE, $sourceKey ); }, __('message.deleted')); } }