Files
sam-api/app/Http/Controllers/Api/V1/TaxInvoiceController.php
김보곤 d1c65f5465 feat: [finance] vendors 및 supplier-settings API 추가
- GET /api/v1/vendors: 거래처 간단 목록 (id, name) 반환
- GET /api/v1/tax-invoices/supplier-settings: 공급자 설정 조회
- PUT /api/v1/tax-invoices/supplier-settings: 공급자 설정 저장
2026-03-17 16:11:42 +09:00

257 lines
7.8 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Http\Requests\TaxInvoice\CancelTaxInvoiceRequest;
use App\Http\Requests\TaxInvoice\CreateTaxInvoiceRequest;
use App\Http\Requests\TaxInvoice\TaxInvoiceListRequest;
use App\Http\Requests\TaxInvoice\TaxInvoiceSummaryRequest;
use App\Http\Requests\TaxInvoice\UpdateTaxInvoiceRequest;
use App\Http\Requests\V1\TaxInvoice\BulkIssueRequest;
use App\Models\Tenants\JournalEntry;
use App\Services\JournalSyncService;
use App\Services\TaxInvoiceService;
use App\Services\TenantSettingService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class TaxInvoiceController extends Controller
{
private const SUPPLIER_GROUP = 'supplier';
private const SUPPLIER_KEYS = [
'business_number',
'company_name',
'representative_name',
'address',
'business_type',
'business_item',
'contact_name',
'contact_phone',
'contact_email',
];
public function __construct(
private TaxInvoiceService $taxInvoiceService,
private JournalSyncService $journalSyncService,
private TenantSettingService $tenantSettingService,
) {}
/**
* 세금계산서 목록 조회
*/
public function index(TaxInvoiceListRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->taxInvoiceService->list($request->validated());
}, __('message.fetched'));
}
/**
* 세금계산서 상세 조회
*/
public function show(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->taxInvoiceService->show($id);
}, __('message.fetched'));
}
/**
* 세금계산서 생성
*/
public function store(CreateTaxInvoiceRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->taxInvoiceService->create($request->validated());
}, __('message.created'));
}
/**
* 세금계산서 수정
*/
public function update(UpdateTaxInvoiceRequest $request, int $id)
{
return ApiResponse::handle(function () use ($request, $id) {
return $this->taxInvoiceService->update($id, $request->validated());
}, __('message.updated'));
}
/**
* 세금계산서 삭제
*/
public function destroy(int $id)
{
return ApiResponse::handle(function () use ($id) {
$this->taxInvoiceService->delete($id);
return null;
}, __('message.deleted'));
}
/**
* 세금계산서 발행
*/
public function issue(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->taxInvoiceService->issue($id);
}, __('message.tax_invoice.issued'));
}
/**
* 세금계산서 일괄 발행
*/
public function bulkIssue(BulkIssueRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->taxInvoiceService->bulkIssue($request->getIds());
}, __('message.tax_invoice.bulk_issued'));
}
/**
* 세금계산서 취소
*/
public function cancel(CancelTaxInvoiceRequest $request, int $id)
{
return ApiResponse::handle(function () use ($request, $id) {
return $this->taxInvoiceService->cancel($id, $request->validated()['reason']);
}, __('message.tax_invoice.cancelled'));
}
/**
* 국세청 전송 상태 조회
*/
public function checkStatus(int $id)
{
return ApiResponse::handle(function () use ($id) {
return $this->taxInvoiceService->checkStatus($id);
}, __('message.fetched'));
}
/**
* 세금계산서 요약 통계
*/
public function summary(TaxInvoiceSummaryRequest $request)
{
return ApiResponse::handle(function () use ($request) {
return $this->taxInvoiceService->summary($request->validated());
}, __('message.fetched'));
}
// =========================================================================
// 공급자 설정 (Supplier Settings)
// =========================================================================
/**
* 공급자 설정 조회
*/
public function getSupplierSettings(): JsonResponse
{
return ApiResponse::handle(function () {
$settings = $this->tenantSettingService->getByGroup(self::SUPPLIER_GROUP);
$result = [];
foreach (self::SUPPLIER_KEYS as $key) {
$result[$key] = $settings[$key] ?? null;
}
return $result;
}, __('message.fetched'));
}
/**
* 공급자 설정 저장
*/
public function saveSupplierSettings(Request $request): JsonResponse
{
return ApiResponse::handle(function () use ($request) {
$data = $request->only(self::SUPPLIER_KEYS);
$settings = [];
foreach ($data as $key => $value) {
if (in_array($key, self::SUPPLIER_KEYS)) {
$settings[$key] = $value;
}
}
$this->tenantSettingService->setMany(self::SUPPLIER_GROUP, $settings);
return $settings;
}, __('message.updated'));
}
// =========================================================================
// 분개 (Journal Entries)
// =========================================================================
/**
* 세금계산서 분개 조회
*/
public function getJournalEntries(int $id): JsonResponse
{
return ApiResponse::handle(function () use ($id) {
$sourceKey = "tax_invoice_{$id}";
$data = $this->journalSyncService->getForSource(
JournalEntry::SOURCE_TAX_INVOICE,
$sourceKey
);
return $data ?? ['rows' => []];
}, __('message.fetched'));
}
/**
* 세금계산서 분개 저장/수정
*/
public function storeJournalEntries(Request $request, int $id): JsonResponse
{
return ApiResponse::handle(function () use ($request, $id) {
$validated = $request->validate([
'rows' => 'required|array|min:1',
'rows.*.side' => 'required|in:debit,credit',
'rows.*.account_subject' => 'required|string|max:20',
'rows.*.debit_amount' => 'required|integer|min:0',
'rows.*.credit_amount' => 'required|integer|min:0',
]);
// 세금계산서 정보 조회 (entry_date용)
$taxInvoice = $this->taxInvoiceService->show($id);
$rows = array_map(fn ($row) => [
'side' => $row['side'],
'account_code' => $row['account_subject'],
'debit_amount' => $row['debit_amount'],
'credit_amount' => $row['credit_amount'],
], $validated['rows']);
$sourceKey = "tax_invoice_{$id}";
return $this->journalSyncService->saveForSource(
JournalEntry::SOURCE_TAX_INVOICE,
$sourceKey,
$taxInvoice->issue_date?->format('Y-m-d') ?? now()->format('Y-m-d'),
"세금계산서 분개 (#{$id})",
$rows,
);
}, __('message.created'));
}
/**
* 세금계산서 분개 삭제
*/
public function deleteJournalEntries(int $id): JsonResponse
{
return ApiResponse::handle(function () use ($id) {
$sourceKey = "tax_invoice_{$id}";
return $this->journalSyncService->deleteForSource(
JournalEntry::SOURCE_TAX_INVOICE,
$sourceKey
);
}, __('message.deleted'));
}
}