feat: [barobill] React 연동용 바로빌 카드/은행/홈택스 REST API 구현
- 바로빌 카드 거래 API (16 엔드포인트): 조회, 분할, 수동입력, 숨김/복원, 금액수정, 분개 - 바로빌 은행 거래 API (13 엔드포인트): 조회, 분할, 오버라이드, 수동입력, 잔액요약, 분개 - 홈택스 세금계산서 API (13 엔드포인트): 매출/매입 조회, 수동입력, 자체분개, 통합분개 - JournalEntry 소스 타입 상수 추가 (barobill_card, barobill_bank, hometax_invoice)
This commit is contained in:
222
app/Services/HometaxInvoiceService.php
Normal file
222
app/Services/HometaxInvoiceService.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?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,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user