Files
sam-api/app/Services/HometaxInvoiceService.php
김보곤 eeda6d980e feat: [barobill] React 연동용 바로빌 카드/은행/홈택스 REST API 구현
- 바로빌 카드 거래 API (16 엔드포인트): 조회, 분할, 수동입력, 숨김/복원, 금액수정, 분개
- 바로빌 은행 거래 API (13 엔드포인트): 조회, 분할, 오버라이드, 수동입력, 잔액요약, 분개
- 홈택스 세금계산서 API (13 엔드포인트): 매출/매입 조회, 수동입력, 자체분개, 통합분개
- JournalEntry 소스 타입 상수 추가 (barobill_card, barobill_bank, hometax_invoice)
2026-03-11 19:41:07 +09:00

223 lines
7.3 KiB
PHP

<?php
namespace App\Services;
use App\Models\Barobill\HometaxInvoice;
use App\Models\Barobill\HometaxInvoiceJournal;
use Illuminate\Support\Facades\DB;
/**
* 홈택스 세금계산서 서비스 (React 연동용)
*
* MNG에서 동기화된 hometax_invoices 데이터를
* React 프론트엔드에서 조회/분개 등 처리
*/
class HometaxInvoiceService extends Service
{
/**
* 매출 세금계산서 목록
*/
public function sales(array $params): array
{
return $this->listByType('sales', $params);
}
/**
* 매입 세금계산서 목록
*/
public function purchases(array $params): array
{
return $this->listByType('purchase', $params);
}
/**
* 세금계산서 상세 조회
*/
public function show(int $id): ?HometaxInvoice
{
return HometaxInvoice::where('tenant_id', $this->tenantId())
->with('journals')
->find($id);
}
/**
* 분개 저장 (홈택스 자체 분개 테이블 사용)
*/
public function saveJournals(int $invoiceId, array $items): array
{
$tenantId = $this->tenantId();
$invoice = HometaxInvoice::where('tenant_id', $tenantId)->findOrFail($invoiceId);
return DB::transaction(function () use ($tenantId, $invoice, $items) {
// 기존 분개 삭제
HometaxInvoiceJournal::where('tenant_id', $tenantId)
->where('hometax_invoice_id', $invoice->id)
->delete();
$created = [];
foreach ($items as $index => $item) {
$created[] = HometaxInvoiceJournal::create([
'tenant_id' => $tenantId,
'hometax_invoice_id' => $invoice->id,
'nts_confirm_num' => $invoice->nts_confirm_num,
'dc_type' => $item['dc_type'],
'account_code' => $item['account_code'],
'account_name' => $item['account_name'] ?? null,
'debit_amount' => $item['debit_amount'] ?? 0,
'credit_amount' => $item['credit_amount'] ?? 0,
'description' => $item['description'] ?? null,
'sort_order' => $index + 1,
'invoice_type' => $invoice->invoice_type,
'write_date' => $invoice->write_date,
'supply_amount' => $invoice->supply_amount,
'tax_amount' => $invoice->tax_amount,
'total_amount' => $invoice->total_amount,
'trading_partner_name' => $invoice->invoice_type === 'sales'
? $invoice->invoicee_corp_name
: $invoice->invoicer_corp_name,
]);
}
return ['items' => $created, 'count' => count($created)];
});
}
/**
* 분개 조회
*/
public function getJournals(int $invoiceId): array
{
$tenantId = $this->tenantId();
$journals = HometaxInvoiceJournal::getByInvoiceId($tenantId, $invoiceId);
return ['items' => $journals];
}
/**
* 분개 삭제
*/
public function deleteJournals(int $invoiceId): array
{
$tenantId = $this->tenantId();
$deleted = HometaxInvoiceJournal::where('tenant_id', $tenantId)
->where('hometax_invoice_id', $invoiceId)
->delete();
return ['deleted_count' => $deleted];
}
/**
* 수동 세금계산서 등록
*/
public function storeManual(array $data): HometaxInvoice
{
$tenantId = $this->tenantId();
return HometaxInvoice::create(array_merge($data, [
'tenant_id' => $tenantId,
]));
}
/**
* 수동 세금계산서 수정
*/
public function updateManual(int $id, array $data): HometaxInvoice
{
$invoice = HometaxInvoice::where('tenant_id', $this->tenantId())->findOrFail($id);
$invoice->update($data);
return $invoice->fresh();
}
/**
* 수동 세금계산서 삭제 (soft delete)
*/
public function destroyManual(int $id): bool
{
$invoice = HometaxInvoice::where('tenant_id', $this->tenantId())->findOrFail($id);
return $invoice->delete();
}
/**
* 요약 통계
*/
public function summary(array $params): array
{
$tenantId = $this->tenantId();
$startDate = $params['start_date'] ?? now()->startOfMonth()->format('Y-m-d');
$endDate = $params['end_date'] ?? now()->format('Y-m-d');
$salesQuery = HometaxInvoice::where('tenant_id', $tenantId)
->sales()
->period($startDate, $endDate);
$purchaseQuery = HometaxInvoice::where('tenant_id', $tenantId)
->purchase()
->period($startDate, $endDate);
return [
'sales' => [
'count' => (clone $salesQuery)->count(),
'supply_amount' => (int) (clone $salesQuery)->sum('supply_amount'),
'tax_amount' => (int) (clone $salesQuery)->sum('tax_amount'),
'total_amount' => (int) (clone $salesQuery)->sum('total_amount'),
],
'purchase' => [
'count' => (clone $purchaseQuery)->count(),
'supply_amount' => (int) (clone $purchaseQuery)->sum('supply_amount'),
'tax_amount' => (int) (clone $purchaseQuery)->sum('tax_amount'),
'total_amount' => (int) (clone $purchaseQuery)->sum('total_amount'),
],
];
}
/**
* 타입별 목록 조회 (공통)
*/
private function listByType(string $invoiceType, array $params): array
{
$tenantId = $this->tenantId();
$startDate = $params['start_date'] ?? now()->startOfMonth()->format('Y-m-d');
$endDate = $params['end_date'] ?? now()->format('Y-m-d');
$search = $params['search'] ?? null;
$perPage = $params['per_page'] ?? 50;
$query = HometaxInvoice::where('tenant_id', $tenantId)
->where('invoice_type', $invoiceType)
->period($startDate, $endDate);
if ($search) {
$query->where(function ($q) use ($search, $invoiceType) {
if ($invoiceType === 'sales') {
$q->where('invoicee_corp_name', 'like', "%{$search}%")
->orWhere('invoicee_corp_num', 'like', "%{$search}%");
} else {
$q->where('invoicer_corp_name', 'like', "%{$search}%")
->orWhere('invoicer_corp_num', 'like', "%{$search}%");
}
$q->orWhere('nts_confirm_num', 'like', "%{$search}%")
->orWhere('item_name', 'like', "%{$search}%");
});
}
$query->orderByDesc('write_date')->orderByDesc('issue_date');
$invoices = $query->paginate($perPage);
// 분개 존재 여부 로드
$invoiceIds = $invoices->getCollection()->pluck('id')->toArray();
$journaledIds = HometaxInvoiceJournal::getJournaledInvoiceIds($tenantId, $invoiceIds);
$invoices->getCollection()->transform(function ($invoice) use ($journaledIds) {
$invoice->has_journal = in_array($invoice->id, $journaledIds);
return $invoice;
});
return [
'data' => $invoices,
];
}
}