feat: [barobill] React 연동용 바로빌 카드/은행/홈택스 REST API 구현
- 바로빌 카드 거래 API (16 엔드포인트): 조회, 분할, 수동입력, 숨김/복원, 금액수정, 분개 - 바로빌 은행 거래 API (13 엔드포인트): 조회, 분할, 오버라이드, 수동입력, 잔액요약, 분개 - 홈택스 세금계산서 API (13 엔드포인트): 매출/매입 조회, 수동입력, 자체분개, 통합분개 - JournalEntry 소스 타입 상수 추가 (barobill_card, barobill_bank, hometax_invoice)
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Tenants\JournalEntry;
|
||||
use App\Services\BarobillBankTransactionService;
|
||||
use App\Services\JournalSyncService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* 바로빌 은행 거래 API 컨트롤러 (React 연동용)
|
||||
*
|
||||
* MNG에서 동기화된 은행 거래 데이터를 React에서 조회/관리
|
||||
*/
|
||||
class BarobillBankTransactionController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected BarobillBankTransactionService $service,
|
||||
protected JournalSyncService $journalSyncService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 은행 거래 목록 조회
|
||||
*/
|
||||
public function index(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',
|
||||
'bank_account_num' => 'nullable|string|max:50',
|
||||
'search' => 'nullable|string|max:100',
|
||||
'per_page' => 'nullable|integer|min:1|max:100',
|
||||
'page' => 'nullable|integer|min:1',
|
||||
]);
|
||||
|
||||
return $this->service->index($params);
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 계좌 목록 (필터용)
|
||||
*/
|
||||
public function accounts(): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
return $this->service->accounts();
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 잔액 요약
|
||||
*/
|
||||
public function balanceSummary(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$params = $request->validate([
|
||||
'date' => 'nullable|date',
|
||||
]);
|
||||
|
||||
return $this->service->balanceSummary($params);
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 분할 (Splits)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 거래 분할 조회
|
||||
*/
|
||||
public function getSplits(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'unique_key' => 'required|string|max:500',
|
||||
]);
|
||||
|
||||
return $this->service->getSplits($validated['unique_key']);
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래 분할 저장
|
||||
*/
|
||||
public function saveSplits(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'unique_key' => 'required|string|max:500',
|
||||
'items' => 'required|array|min:1',
|
||||
'items.*.split_amount' => 'required|numeric',
|
||||
'items.*.account_code' => 'nullable|string|max:20',
|
||||
'items.*.account_name' => 'nullable|string|max:100',
|
||||
'items.*.deduction_type' => 'nullable|string|max:20',
|
||||
'items.*.evidence_name' => 'nullable|string|max:100',
|
||||
'items.*.description' => 'nullable|string|max:500',
|
||||
'items.*.memo' => 'nullable|string|max:500',
|
||||
'items.*.bank_account_num' => 'nullable|string|max:50',
|
||||
'items.*.trans_dt' => 'nullable|string|max:20',
|
||||
'items.*.trans_date' => 'nullable|date',
|
||||
'items.*.original_deposit' => 'nullable|numeric',
|
||||
'items.*.original_withdraw' => 'nullable|numeric',
|
||||
'items.*.summary' => 'nullable|string|max:500',
|
||||
]);
|
||||
|
||||
return $this->service->saveSplits($validated['unique_key'], $validated['items']);
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래 분할 삭제
|
||||
*/
|
||||
public function deleteSplits(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'unique_key' => 'required|string|max:500',
|
||||
]);
|
||||
|
||||
return $this->service->deleteSplits($validated['unique_key']);
|
||||
}, __('message.deleted'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 오버라이드 (Override)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 적요/분류 오버라이드 저장
|
||||
*/
|
||||
public function saveOverride(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'unique_key' => 'required|string|max:500',
|
||||
'modified_summary' => 'nullable|string|max:500',
|
||||
'modified_cast' => 'nullable|string|max:100',
|
||||
]);
|
||||
|
||||
return $this->service->saveOverride(
|
||||
$validated['unique_key'],
|
||||
$validated['modified_summary'] ?? null,
|
||||
$validated['modified_cast'] ?? null
|
||||
);
|
||||
}, __('message.updated'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 수동 입력 (Manual)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 수동 은행 거래 등록
|
||||
*/
|
||||
public function storeManual(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'bank_account_num' => 'required|string|max:50',
|
||||
'bank_code' => 'nullable|string|max:10',
|
||||
'bank_name' => 'nullable|string|max:50',
|
||||
'trans_date' => 'required|date',
|
||||
'trans_time' => 'nullable|string|max:10',
|
||||
'trans_dt' => 'nullable|string|max:20',
|
||||
'deposit' => 'nullable|numeric|min:0',
|
||||
'withdraw' => 'nullable|numeric|min:0',
|
||||
'balance' => 'nullable|numeric',
|
||||
'summary' => 'nullable|string|max:500',
|
||||
'cast' => 'nullable|string|max:100',
|
||||
'memo' => 'nullable|string|max:500',
|
||||
'trans_office' => 'nullable|string|max:100',
|
||||
'account_code' => 'nullable|string|max:20',
|
||||
'account_name' => 'nullable|string|max:100',
|
||||
'client_code' => 'nullable|string|max:20',
|
||||
'client_name' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
return $this->service->storeManual($validated);
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 은행 거래 수정
|
||||
*/
|
||||
public function updateManual(Request $request, int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request, $id) {
|
||||
$validated = $request->validate([
|
||||
'deposit' => 'nullable|numeric|min:0',
|
||||
'withdraw' => 'nullable|numeric|min:0',
|
||||
'balance' => 'nullable|numeric',
|
||||
'summary' => 'nullable|string|max:500',
|
||||
'cast' => 'nullable|string|max:100',
|
||||
'memo' => 'nullable|string|max:500',
|
||||
'account_code' => 'nullable|string|max:20',
|
||||
'account_name' => 'nullable|string|max:100',
|
||||
'client_code' => 'nullable|string|max:20',
|
||||
'client_name' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
return $this->service->updateManual($id, $validated);
|
||||
}, __('message.updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 은행 거래 삭제
|
||||
*/
|
||||
public function destroyManual(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
return $this->service->destroyManual($id);
|
||||
}, __('message.deleted'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 분개 (Journal Entries)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 은행 거래 분개 조회
|
||||
*/
|
||||
public function getJournalEntries(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$sourceKey = "barobill_bank_{$id}";
|
||||
|
||||
return $this->journalSyncService->getForSource(
|
||||
JournalEntry::SOURCE_BAROBILL_BANK,
|
||||
$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',
|
||||
]);
|
||||
|
||||
$bankTx = \App\Models\Barobill\BarobillBankTransaction::find($id);
|
||||
if (! $bankTx) {
|
||||
throw new \Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
}
|
||||
|
||||
$entryDate = $bankTx->trans_date ?? now()->format('Y-m-d');
|
||||
$sourceKey = "barobill_bank_{$id}";
|
||||
|
||||
return $this->journalSyncService->saveForSource(
|
||||
JournalEntry::SOURCE_BAROBILL_BANK,
|
||||
$sourceKey,
|
||||
$entryDate,
|
||||
"바로빌 은행거래 분개 (#{$id})",
|
||||
$validated['items'],
|
||||
);
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 은행 거래 분개 삭제
|
||||
*/
|
||||
public function deleteJournalEntries(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$sourceKey = "barobill_bank_{$id}";
|
||||
|
||||
return $this->journalSyncService->deleteForSource(
|
||||
JournalEntry::SOURCE_BAROBILL_BANK,
|
||||
$sourceKey
|
||||
);
|
||||
}, __('message.deleted'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Tenants\JournalEntry;
|
||||
use App\Services\BarobillCardTransactionService;
|
||||
use App\Services\JournalSyncService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* 바로빌 카드 거래 API 컨트롤러 (React 연동용)
|
||||
*
|
||||
* MNG에서 동기화된 카드 거래 데이터를 React에서 조회/관리
|
||||
*/
|
||||
class BarobillCardTransactionController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected BarobillCardTransactionService $service,
|
||||
protected JournalSyncService $journalSyncService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 카드 거래 목록 조회
|
||||
*/
|
||||
public function index(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',
|
||||
'card_num' => 'nullable|string|max:50',
|
||||
'search' => 'nullable|string|max:100',
|
||||
'include_hidden' => 'nullable|boolean',
|
||||
'per_page' => 'nullable|integer|min:1|max:100',
|
||||
'page' => 'nullable|integer|min:1',
|
||||
]);
|
||||
|
||||
return $this->service->index($params);
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 카드 거래 상세
|
||||
*/
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$tx = $this->service->show($id);
|
||||
if (! $tx) {
|
||||
throw new \Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
}
|
||||
|
||||
return $tx;
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 번호 목록 (필터용)
|
||||
*/
|
||||
public function cardNumbers(): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
return $this->service->cardNumbers();
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 분할 (Splits)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 카드 거래 분할 조회
|
||||
*/
|
||||
public function getSplits(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'unique_key' => 'required|string|max:500',
|
||||
]);
|
||||
|
||||
return $this->service->getSplits($validated['unique_key']);
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 분할 저장
|
||||
*/
|
||||
public function saveSplits(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'unique_key' => 'required|string|max:500',
|
||||
'items' => 'required|array|min:1',
|
||||
'items.*.split_amount' => 'required|numeric',
|
||||
'items.*.split_supply_amount' => 'nullable|numeric',
|
||||
'items.*.split_tax' => 'nullable|numeric',
|
||||
'items.*.account_code' => 'nullable|string|max:20',
|
||||
'items.*.account_name' => 'nullable|string|max:100',
|
||||
'items.*.deduction_type' => 'nullable|string|max:20',
|
||||
'items.*.evidence_name' => 'nullable|string|max:100',
|
||||
'items.*.description' => 'nullable|string|max:500',
|
||||
'items.*.memo' => 'nullable|string|max:500',
|
||||
'items.*.card_num' => 'nullable|string|max:50',
|
||||
'items.*.use_dt' => 'nullable|string|max:20',
|
||||
'items.*.use_date' => 'nullable|date',
|
||||
'items.*.approval_num' => 'nullable|string|max:50',
|
||||
'items.*.original_amount' => 'nullable|numeric',
|
||||
'items.*.merchant_name' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
return $this->service->saveSplits($validated['unique_key'], $validated['items']);
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 분할 삭제
|
||||
*/
|
||||
public function deleteSplits(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'unique_key' => 'required|string|max:500',
|
||||
]);
|
||||
|
||||
return $this->service->deleteSplits($validated['unique_key']);
|
||||
}, __('message.deleted'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 수동 입력 (Manual)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 수동 카드 거래 등록
|
||||
*/
|
||||
public function storeManual(Request $request): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$validated = $request->validate([
|
||||
'card_num' => 'required|string|max:50',
|
||||
'card_company' => 'nullable|string|max:10',
|
||||
'card_company_name' => 'nullable|string|max:50',
|
||||
'use_dt' => 'required|string|max:20',
|
||||
'use_date' => 'required|date',
|
||||
'use_time' => 'nullable|string|max:10',
|
||||
'approval_num' => 'nullable|string|max:50',
|
||||
'approval_type' => 'nullable|string|max:10',
|
||||
'approval_amount' => 'required|numeric',
|
||||
'tax' => 'nullable|numeric',
|
||||
'service_charge' => 'nullable|numeric',
|
||||
'merchant_name' => 'required|string|max:200',
|
||||
'merchant_biz_num' => 'nullable|string|max:20',
|
||||
'account_code' => 'nullable|string|max:20',
|
||||
'account_name' => 'nullable|string|max:100',
|
||||
'deduction_type' => 'nullable|string|max:20',
|
||||
'evidence_name' => 'nullable|string|max:100',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'memo' => 'nullable|string|max:500',
|
||||
]);
|
||||
|
||||
return $this->service->storeManual($validated);
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 카드 거래 수정
|
||||
*/
|
||||
public function updateManual(Request $request, int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request, $id) {
|
||||
$validated = $request->validate([
|
||||
'approval_amount' => 'nullable|numeric',
|
||||
'tax' => 'nullable|numeric',
|
||||
'merchant_name' => 'nullable|string|max:200',
|
||||
'account_code' => 'nullable|string|max:20',
|
||||
'account_name' => 'nullable|string|max:100',
|
||||
'deduction_type' => 'nullable|string|max:20',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'memo' => 'nullable|string|max:500',
|
||||
]);
|
||||
|
||||
return $this->service->updateManual($id, $validated);
|
||||
}, __('message.updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 카드 거래 삭제
|
||||
*/
|
||||
public function destroyManual(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
return $this->service->destroyManual($id);
|
||||
}, __('message.deleted'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 숨김/복원 (Hide/Restore)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 카드 거래 숨김
|
||||
*/
|
||||
public function hide(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
return $this->service->hide($id);
|
||||
}, __('message.updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 숨김 복원
|
||||
*/
|
||||
public function restore(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
return $this->service->restore($id);
|
||||
}, __('message.updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 숨겨진 거래 목록
|
||||
*/
|
||||
public function hiddenList(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->hiddenList($params);
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 금액 수정
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 공급가액/세액 수정
|
||||
*/
|
||||
public function updateAmount(Request $request, int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request, $id) {
|
||||
$validated = $request->validate([
|
||||
'supply_amount' => 'required|numeric',
|
||||
'tax' => 'required|numeric',
|
||||
'modified_by_name' => 'nullable|string|max:50',
|
||||
]);
|
||||
|
||||
return $this->service->updateAmount($id, $validated);
|
||||
}, __('message.updated'));
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 분개 (Journal Entries)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 카드 거래 분개 조회
|
||||
*/
|
||||
public function getJournalEntries(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$sourceKey = "barobill_card_{$id}";
|
||||
|
||||
return $this->journalSyncService->getForSource(
|
||||
JournalEntry::SOURCE_BAROBILL_CARD,
|
||||
$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',
|
||||
]);
|
||||
|
||||
$tx = $this->service->show($id);
|
||||
if (! $tx) {
|
||||
throw new \Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
}
|
||||
|
||||
$entryDate = $tx->use_date ?? now()->format('Y-m-d');
|
||||
$sourceKey = "barobill_card_{$id}";
|
||||
|
||||
return $this->journalSyncService->saveForSource(
|
||||
JournalEntry::SOURCE_BAROBILL_CARD,
|
||||
$sourceKey,
|
||||
$entryDate,
|
||||
"바로빌 카드거래 분개 (#{$id})",
|
||||
$validated['items'],
|
||||
);
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 분개 삭제
|
||||
*/
|
||||
public function deleteJournalEntries(int $id): JsonResponse
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$sourceKey = "barobill_card_{$id}";
|
||||
|
||||
return $this->journalSyncService->deleteForSource(
|
||||
JournalEntry::SOURCE_BAROBILL_CARD,
|
||||
$sourceKey
|
||||
);
|
||||
}, __('message.deleted'));
|
||||
}
|
||||
}
|
||||
278
app/Http/Controllers/Api/V1/HometaxInvoiceController.php
Normal file
278
app/Http/Controllers/Api/V1/HometaxInvoiceController.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Tenants\JournalEntry;
|
||||
use App\Services\HometaxInvoiceService;
|
||||
use App\Services\JournalSyncService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* 홈택스 세금계산서 API 컨트롤러 (React 연동용)
|
||||
*
|
||||
* MNG에서 동기화된 홈택스 세금계산서를 React에서 조회/관리
|
||||
*/
|
||||
class HometaxInvoiceController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected HometaxInvoiceService $service,
|
||||
protected JournalSyncService $journalSyncService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 매출 세금계산서 목록
|
||||
*/
|
||||
public function sales(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->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'));
|
||||
}
|
||||
}
|
||||
@@ -34,14 +34,24 @@ class JournalEntry extends Model
|
||||
|
||||
// Status
|
||||
public const STATUS_DRAFT = 'draft';
|
||||
|
||||
public const STATUS_CONFIRMED = 'confirmed';
|
||||
|
||||
// Source type
|
||||
public const SOURCE_MANUAL = 'manual';
|
||||
|
||||
public const SOURCE_BANK_TRANSACTION = 'bank_transaction';
|
||||
|
||||
public const SOURCE_TAX_INVOICE = 'tax_invoice';
|
||||
|
||||
public const SOURCE_CARD_TRANSACTION = 'card_transaction';
|
||||
|
||||
public const SOURCE_BAROBILL_CARD = 'barobill_card';
|
||||
|
||||
public const SOURCE_BAROBILL_BANK = 'barobill_bank';
|
||||
|
||||
public const SOURCE_HOMETAX_INVOICE = 'hometax_invoice';
|
||||
|
||||
// Entry type
|
||||
public const TYPE_GENERAL = 'general';
|
||||
|
||||
|
||||
249
app/Services/BarobillBankTransactionService.php
Normal file
249
app/Services/BarobillBankTransactionService.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Barobill\BarobillBankTransaction;
|
||||
use App\Models\Barobill\BarobillBankTransactionOverride;
|
||||
use App\Models\Barobill\BarobillBankTransactionSplit;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 바로빌 은행 거래 서비스 (React 연동용)
|
||||
*
|
||||
* MNG에서 동기화된 barobill_bank_transactions 데이터를
|
||||
* React 프론트엔드에서 조회/분개/수정 등 처리
|
||||
*/
|
||||
class BarobillBankTransactionService extends Service
|
||||
{
|
||||
/**
|
||||
* 은행 거래 목록 조회 (기간별, 계좌번호별)
|
||||
*/
|
||||
public function index(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');
|
||||
$accountNum = $params['bank_account_num'] ?? null;
|
||||
$search = $params['search'] ?? null;
|
||||
$perPage = $params['per_page'] ?? 50;
|
||||
|
||||
$query = BarobillBankTransaction::where('tenant_id', $tenantId)
|
||||
->whereBetween('trans_date', [$startDate, $endDate]);
|
||||
|
||||
if ($accountNum) {
|
||||
$query->where('bank_account_num', $accountNum);
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('summary', 'like', "%{$search}%")
|
||||
->orWhere('memo', 'like', "%{$search}%")
|
||||
->orWhere('client_name', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
$query->orderByDesc('trans_date')->orderByDesc('trans_dt');
|
||||
|
||||
$transactions = $query->paginate($perPage);
|
||||
|
||||
// 분할/오버라이드 정보 로드
|
||||
$uniqueKeys = $transactions->getCollection()->map->unique_key->toArray();
|
||||
|
||||
$splits = BarobillBankTransactionSplit::where('tenant_id', $tenantId)
|
||||
->whereIn('original_unique_key', $uniqueKeys)
|
||||
->orderBy('sort_order')
|
||||
->get()
|
||||
->groupBy('original_unique_key');
|
||||
|
||||
$overrides = BarobillBankTransactionOverride::getByUniqueKeys($tenantId, $uniqueKeys);
|
||||
|
||||
$transactions->getCollection()->transform(function ($tx) use ($splits, $overrides) {
|
||||
$tx->splits = $splits->get($tx->unique_key, collect());
|
||||
$tx->has_splits = $tx->splits->isNotEmpty();
|
||||
$tx->override = $overrides->get($tx->unique_key);
|
||||
|
||||
return $tx;
|
||||
});
|
||||
|
||||
return [
|
||||
'data' => $transactions,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 계좌 목록 (필터용)
|
||||
*/
|
||||
public function accounts(): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$accounts = BarobillBankTransaction::where('tenant_id', $tenantId)
|
||||
->select('bank_account_num', 'bank_name')
|
||||
->distinct()
|
||||
->orderBy('bank_account_num')
|
||||
->get();
|
||||
|
||||
return ['items' => $accounts];
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래 분할 조회
|
||||
*/
|
||||
public function getSplits(string $uniqueKey): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$splits = BarobillBankTransactionSplit::getByUniqueKey($tenantId, $uniqueKey);
|
||||
|
||||
return ['items' => $splits];
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래 분할 저장
|
||||
*/
|
||||
public function saveSplits(string $uniqueKey, array $items): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
return DB::transaction(function () use ($tenantId, $uniqueKey, $items) {
|
||||
BarobillBankTransactionSplit::where('tenant_id', $tenantId)
|
||||
->where('original_unique_key', $uniqueKey)
|
||||
->delete();
|
||||
|
||||
$created = [];
|
||||
foreach ($items as $index => $item) {
|
||||
$created[] = BarobillBankTransactionSplit::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'original_unique_key' => $uniqueKey,
|
||||
'split_amount' => $item['split_amount'],
|
||||
'account_code' => $item['account_code'] ?? null,
|
||||
'account_name' => $item['account_name'] ?? null,
|
||||
'deduction_type' => $item['deduction_type'] ?? null,
|
||||
'evidence_name' => $item['evidence_name'] ?? null,
|
||||
'description' => $item['description'] ?? null,
|
||||
'memo' => $item['memo'] ?? null,
|
||||
'sort_order' => $index + 1,
|
||||
'bank_account_num' => $item['bank_account_num'] ?? null,
|
||||
'trans_dt' => $item['trans_dt'] ?? null,
|
||||
'trans_date' => $item['trans_date'] ?? null,
|
||||
'original_deposit' => $item['original_deposit'] ?? 0,
|
||||
'original_withdraw' => $item['original_withdraw'] ?? 0,
|
||||
'summary' => $item['summary'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
return ['items' => $created, 'count' => count($created)];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 거래 분할 삭제
|
||||
*/
|
||||
public function deleteSplits(string $uniqueKey): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$deleted = BarobillBankTransactionSplit::where('tenant_id', $tenantId)
|
||||
->where('original_unique_key', $uniqueKey)
|
||||
->delete();
|
||||
|
||||
return ['deleted_count' => $deleted];
|
||||
}
|
||||
|
||||
/**
|
||||
* 적요/분류 오버라이드 저장
|
||||
*/
|
||||
public function saveOverride(string $uniqueKey, ?string $modifiedSummary, ?string $modifiedCast): BarobillBankTransactionOverride
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
return BarobillBankTransactionOverride::saveOverride($tenantId, $uniqueKey, $modifiedSummary, $modifiedCast);
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 은행 거래 등록
|
||||
*/
|
||||
public function storeManual(array $data): BarobillBankTransaction
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
return BarobillBankTransaction::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'bank_account_num' => $data['bank_account_num'],
|
||||
'bank_code' => $data['bank_code'] ?? null,
|
||||
'bank_name' => $data['bank_name'] ?? null,
|
||||
'trans_date' => $data['trans_date'],
|
||||
'trans_time' => $data['trans_time'] ?? null,
|
||||
'trans_dt' => $data['trans_dt'] ?? $data['trans_date'].($data['trans_time'] ?? '000000'),
|
||||
'deposit' => $data['deposit'] ?? 0,
|
||||
'withdraw' => $data['withdraw'] ?? 0,
|
||||
'balance' => $data['balance'] ?? 0,
|
||||
'summary' => $data['summary'] ?? null,
|
||||
'cast' => $data['cast'] ?? null,
|
||||
'memo' => $data['memo'] ?? null,
|
||||
'trans_office' => $data['trans_office'] ?? null,
|
||||
'account_code' => $data['account_code'] ?? null,
|
||||
'account_name' => $data['account_name'] ?? null,
|
||||
'client_code' => $data['client_code'] ?? null,
|
||||
'client_name' => $data['client_name'] ?? null,
|
||||
'is_manual' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 은행 거래 수정
|
||||
*/
|
||||
public function updateManual(int $id, array $data): BarobillBankTransaction
|
||||
{
|
||||
$tx = BarobillBankTransaction::where('tenant_id', $this->tenantId())
|
||||
->where('is_manual', true)
|
||||
->findOrFail($id);
|
||||
|
||||
$tx->update($data);
|
||||
|
||||
return $tx->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 은행 거래 삭제
|
||||
*/
|
||||
public function destroyManual(int $id): bool
|
||||
{
|
||||
$tx = BarobillBankTransaction::where('tenant_id', $this->tenantId())
|
||||
->where('is_manual', true)
|
||||
->findOrFail($id);
|
||||
|
||||
return $tx->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 잔액 요약
|
||||
*/
|
||||
public function balanceSummary(array $params): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$date = $params['date'] ?? now()->format('Y-m-d');
|
||||
|
||||
$accounts = BarobillBankTransaction::where('tenant_id', $tenantId)
|
||||
->select('bank_account_num', 'bank_name')
|
||||
->distinct()
|
||||
->get();
|
||||
|
||||
$summary = [];
|
||||
foreach ($accounts as $account) {
|
||||
$lastTx = BarobillBankTransaction::where('tenant_id', $tenantId)
|
||||
->where('bank_account_num', $account->bank_account_num)
|
||||
->where('trans_date', '<=', $date)
|
||||
->orderByDesc('trans_date')
|
||||
->orderByDesc('trans_dt')
|
||||
->first();
|
||||
|
||||
$summary[] = [
|
||||
'bank_account_num' => $account->bank_account_num,
|
||||
'bank_name' => $account->bank_name,
|
||||
'balance' => $lastTx ? $lastTx->balance : 0,
|
||||
'last_trans_date' => $lastTx?->trans_date,
|
||||
];
|
||||
}
|
||||
|
||||
return ['items' => $summary];
|
||||
}
|
||||
}
|
||||
308
app/Services/BarobillCardTransactionService.php
Normal file
308
app/Services/BarobillCardTransactionService.php
Normal file
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Barobill\BarobillCardTransaction;
|
||||
use App\Models\Barobill\BarobillCardTransactionAmountLog;
|
||||
use App\Models\Barobill\BarobillCardTransactionHide;
|
||||
use App\Models\Barobill\BarobillCardTransactionSplit;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 바로빌 카드 거래 서비스 (React 연동용)
|
||||
*
|
||||
* MNG에서 동기화된 barobill_card_transactions 데이터를
|
||||
* React 프론트엔드에서 조회/분개/숨김 등 처리
|
||||
*/
|
||||
class BarobillCardTransactionService extends Service
|
||||
{
|
||||
/**
|
||||
* 카드 거래 목록 조회 (기간별, 카드번호별)
|
||||
*/
|
||||
public function index(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');
|
||||
$cardNum = $params['card_num'] ?? null;
|
||||
$search = $params['search'] ?? null;
|
||||
$includeHidden = $params['include_hidden'] ?? false;
|
||||
$perPage = $params['per_page'] ?? 50;
|
||||
|
||||
$query = BarobillCardTransaction::where('tenant_id', $tenantId)
|
||||
->whereBetween('use_date', [$startDate, $endDate]);
|
||||
|
||||
if ($cardNum) {
|
||||
$query->where('card_num', $cardNum);
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('merchant_name', 'like', "%{$search}%")
|
||||
->orWhere('memo', 'like', "%{$search}%")
|
||||
->orWhere('approval_num', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 숨김 거래 필터링
|
||||
if (! $includeHidden) {
|
||||
$hiddenKeys = BarobillCardTransactionHide::getHiddenKeys($tenantId, $startDate, $endDate);
|
||||
if (! empty($hiddenKeys)) {
|
||||
$query->whereNotIn(
|
||||
DB::raw("CONCAT(card_num, '|', use_dt, '|', approval_num, '|', approval_amount)"),
|
||||
$hiddenKeys
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$query->orderByDesc('use_date')->orderByDesc('use_dt');
|
||||
|
||||
$transactions = $query->paginate($perPage);
|
||||
|
||||
// 분할 거래 정보 로드
|
||||
$uniqueKeys = $transactions->getCollection()->map->unique_key->toArray();
|
||||
$splits = BarobillCardTransactionSplit::where('tenant_id', $tenantId)
|
||||
->whereIn('original_unique_key', $uniqueKeys)
|
||||
->orderBy('sort_order')
|
||||
->get()
|
||||
->groupBy('original_unique_key');
|
||||
|
||||
$transactions->getCollection()->transform(function ($tx) use ($splits) {
|
||||
$tx->splits = $splits->get($tx->unique_key, collect());
|
||||
$tx->has_splits = $tx->splits->isNotEmpty();
|
||||
|
||||
return $tx;
|
||||
});
|
||||
|
||||
return [
|
||||
'data' => $transactions,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 단일 카드 거래 상세
|
||||
*/
|
||||
public function show(int $id): ?BarobillCardTransaction
|
||||
{
|
||||
return BarobillCardTransaction::where('tenant_id', $this->tenantId())
|
||||
->find($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 분할 조회
|
||||
*/
|
||||
public function getSplits(string $uniqueKey): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$splits = BarobillCardTransactionSplit::getByUniqueKey($tenantId, $uniqueKey);
|
||||
|
||||
return ['items' => $splits];
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 분할 저장
|
||||
*/
|
||||
public function saveSplits(string $uniqueKey, array $items): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
return DB::transaction(function () use ($tenantId, $uniqueKey, $items) {
|
||||
// 기존 분할 삭제
|
||||
BarobillCardTransactionSplit::where('tenant_id', $tenantId)
|
||||
->where('original_unique_key', $uniqueKey)
|
||||
->delete();
|
||||
|
||||
$created = [];
|
||||
foreach ($items as $index => $item) {
|
||||
$created[] = BarobillCardTransactionSplit::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'original_unique_key' => $uniqueKey,
|
||||
'split_amount' => $item['split_amount'],
|
||||
'split_supply_amount' => $item['split_supply_amount'] ?? 0,
|
||||
'split_tax' => $item['split_tax'] ?? 0,
|
||||
'account_code' => $item['account_code'] ?? null,
|
||||
'account_name' => $item['account_name'] ?? null,
|
||||
'deduction_type' => $item['deduction_type'] ?? null,
|
||||
'evidence_name' => $item['evidence_name'] ?? null,
|
||||
'description' => $item['description'] ?? null,
|
||||
'memo' => $item['memo'] ?? null,
|
||||
'sort_order' => $index + 1,
|
||||
'card_num' => $item['card_num'] ?? null,
|
||||
'use_dt' => $item['use_dt'] ?? null,
|
||||
'use_date' => $item['use_date'] ?? null,
|
||||
'approval_num' => $item['approval_num'] ?? null,
|
||||
'original_amount' => $item['original_amount'] ?? 0,
|
||||
'merchant_name' => $item['merchant_name'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
return ['items' => $created, 'count' => count($created)];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 분할 삭제
|
||||
*/
|
||||
public function deleteSplits(string $uniqueKey): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$deleted = BarobillCardTransactionSplit::where('tenant_id', $tenantId)
|
||||
->where('original_unique_key', $uniqueKey)
|
||||
->delete();
|
||||
|
||||
return ['deleted_count' => $deleted];
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 카드 거래 등록
|
||||
*/
|
||||
public function storeManual(array $data): BarobillCardTransaction
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
return BarobillCardTransaction::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'card_num' => $data['card_num'],
|
||||
'card_company' => $data['card_company'] ?? null,
|
||||
'card_company_name' => $data['card_company_name'] ?? null,
|
||||
'use_dt' => $data['use_dt'],
|
||||
'use_date' => $data['use_date'],
|
||||
'use_time' => $data['use_time'] ?? null,
|
||||
'approval_num' => $data['approval_num'] ?? 'MANUAL-'.now()->format('YmdHis'),
|
||||
'approval_type' => $data['approval_type'] ?? '1',
|
||||
'approval_amount' => $data['approval_amount'],
|
||||
'tax' => $data['tax'] ?? 0,
|
||||
'service_charge' => $data['service_charge'] ?? 0,
|
||||
'payment_plan' => $data['payment_plan'] ?? null,
|
||||
'merchant_name' => $data['merchant_name'],
|
||||
'merchant_biz_num' => $data['merchant_biz_num'] ?? null,
|
||||
'account_code' => $data['account_code'] ?? null,
|
||||
'account_name' => $data['account_name'] ?? null,
|
||||
'deduction_type' => $data['deduction_type'] ?? null,
|
||||
'evidence_name' => $data['evidence_name'] ?? null,
|
||||
'description' => $data['description'] ?? null,
|
||||
'memo' => $data['memo'] ?? null,
|
||||
'is_manual' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 카드 거래 수정
|
||||
*/
|
||||
public function updateManual(int $id, array $data): BarobillCardTransaction
|
||||
{
|
||||
$tx = BarobillCardTransaction::where('tenant_id', $this->tenantId())
|
||||
->where('is_manual', true)
|
||||
->findOrFail($id);
|
||||
|
||||
$tx->update($data);
|
||||
|
||||
return $tx->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 수동 카드 거래 삭제
|
||||
*/
|
||||
public function destroyManual(int $id): bool
|
||||
{
|
||||
$tx = BarobillCardTransaction::where('tenant_id', $this->tenantId())
|
||||
->where('is_manual', true)
|
||||
->findOrFail($id);
|
||||
|
||||
return $tx->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 숨김
|
||||
*/
|
||||
public function hide(int $id): BarobillCardTransactionHide
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
$tx = BarobillCardTransaction::where('tenant_id', $tenantId)->findOrFail($id);
|
||||
|
||||
return BarobillCardTransactionHide::hideTransaction($tenantId, $tx->unique_key, [
|
||||
'card_num' => $tx->card_num,
|
||||
'use_date' => $tx->use_date,
|
||||
'approval_num' => $tx->approval_num,
|
||||
'approval_amount' => $tx->approval_amount,
|
||||
'merchant_name' => $tx->merchant_name,
|
||||
], $userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 거래 숨김 복원
|
||||
*/
|
||||
public function restore(int $id): bool
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$tx = BarobillCardTransaction::where('tenant_id', $tenantId)->findOrFail($id);
|
||||
|
||||
return BarobillCardTransactionHide::restoreTransaction($tenantId, $tx->unique_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 숨겨진 거래 목록
|
||||
*/
|
||||
public function hiddenList(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');
|
||||
|
||||
$hiddenItems = BarobillCardTransactionHide::where('tenant_id', $tenantId)
|
||||
->whereBetween('use_date', [$startDate, $endDate])
|
||||
->orderByDesc('created_at')
|
||||
->get();
|
||||
|
||||
return ['items' => $hiddenItems];
|
||||
}
|
||||
|
||||
/**
|
||||
* 금액 수정 (공급가액/세액 수정)
|
||||
*/
|
||||
public function updateAmount(int $id, array $data): BarobillCardTransaction
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
$tx = BarobillCardTransaction::where('tenant_id', $tenantId)->findOrFail($id);
|
||||
|
||||
// 변경 이력 기록
|
||||
BarobillCardTransactionAmountLog::create([
|
||||
'card_transaction_id' => $tx->id,
|
||||
'original_unique_key' => $tx->unique_key,
|
||||
'before_supply_amount' => $tx->modified_supply_amount ?? $tx->approval_amount,
|
||||
'before_tax' => $tx->modified_tax ?? $tx->tax,
|
||||
'after_supply_amount' => $data['supply_amount'],
|
||||
'after_tax' => $data['tax'],
|
||||
'modified_by' => $userId,
|
||||
'modified_by_name' => $data['modified_by_name'] ?? '',
|
||||
'ip_address' => request()->ip(),
|
||||
]);
|
||||
|
||||
$tx->update([
|
||||
'modified_supply_amount' => $data['supply_amount'],
|
||||
'modified_tax' => $data['tax'],
|
||||
]);
|
||||
|
||||
return $tx->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 번호 목록 (필터용)
|
||||
*/
|
||||
public function cardNumbers(): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$cards = BarobillCardTransaction::where('tenant_id', $tenantId)
|
||||
->select('card_num', 'card_company_name')
|
||||
->distinct()
|
||||
->orderBy('card_num')
|
||||
->get();
|
||||
|
||||
return ['items' => $cards];
|
||||
}
|
||||
}
|
||||
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,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
use App\Http\Controllers\Api\V1\BadDebtController;
|
||||
use App\Http\Controllers\Api\V1\BankAccountController;
|
||||
use App\Http\Controllers\Api\V1\BankTransactionController;
|
||||
use App\Http\Controllers\Api\V1\BarobillBankTransactionController;
|
||||
use App\Http\Controllers\Api\V1\BarobillCardTransactionController;
|
||||
use App\Http\Controllers\Api\V1\BarobillController;
|
||||
use App\Http\Controllers\Api\V1\BarobillSettingController;
|
||||
use App\Http\Controllers\Api\V1\BillController;
|
||||
@@ -28,6 +30,7 @@
|
||||
use App\Http\Controllers\Api\V1\EntertainmentController;
|
||||
use App\Http\Controllers\Api\V1\ExpectedExpenseController;
|
||||
use App\Http\Controllers\Api\V1\GeneralJournalEntryController;
|
||||
use App\Http\Controllers\Api\V1\HometaxInvoiceController;
|
||||
use App\Http\Controllers\Api\V1\LoanController;
|
||||
use App\Http\Controllers\Api\V1\PaymentController;
|
||||
use App\Http\Controllers\Api\V1\PayrollController;
|
||||
@@ -282,6 +285,60 @@
|
||||
Route::get('/certificate-url', [BarobillController::class, 'certificateUrl'])->name('v1.barobill.certificate-url');
|
||||
});
|
||||
|
||||
// Barobill Card Transaction API (바로빌 카드 거래 - React 연동)
|
||||
Route::prefix('barobill-card-transactions')->group(function () {
|
||||
Route::get('', [BarobillCardTransactionController::class, 'index'])->name('v1.barobill-card-transactions.index');
|
||||
Route::get('/card-numbers', [BarobillCardTransactionController::class, 'cardNumbers'])->name('v1.barobill-card-transactions.card-numbers');
|
||||
Route::get('/hidden', [BarobillCardTransactionController::class, 'hiddenList'])->name('v1.barobill-card-transactions.hidden');
|
||||
Route::get('/splits', [BarobillCardTransactionController::class, 'getSplits'])->name('v1.barobill-card-transactions.splits.show');
|
||||
Route::post('/splits', [BarobillCardTransactionController::class, 'saveSplits'])->name('v1.barobill-card-transactions.splits.store');
|
||||
Route::delete('/splits', [BarobillCardTransactionController::class, 'deleteSplits'])->name('v1.barobill-card-transactions.splits.destroy');
|
||||
Route::post('/manual', [BarobillCardTransactionController::class, 'storeManual'])->name('v1.barobill-card-transactions.manual.store');
|
||||
Route::put('/manual/{id}', [BarobillCardTransactionController::class, 'updateManual'])->whereNumber('id')->name('v1.barobill-card-transactions.manual.update');
|
||||
Route::delete('/manual/{id}', [BarobillCardTransactionController::class, 'destroyManual'])->whereNumber('id')->name('v1.barobill-card-transactions.manual.destroy');
|
||||
Route::get('/{id}', [BarobillCardTransactionController::class, 'show'])->whereNumber('id')->name('v1.barobill-card-transactions.show');
|
||||
Route::post('/{id}/hide', [BarobillCardTransactionController::class, 'hide'])->whereNumber('id')->name('v1.barobill-card-transactions.hide');
|
||||
Route::post('/{id}/restore', [BarobillCardTransactionController::class, 'restore'])->whereNumber('id')->name('v1.barobill-card-transactions.restore');
|
||||
Route::put('/{id}/amount', [BarobillCardTransactionController::class, 'updateAmount'])->whereNumber('id')->name('v1.barobill-card-transactions.update-amount');
|
||||
Route::get('/{id}/journal-entries', [BarobillCardTransactionController::class, 'getJournalEntries'])->whereNumber('id')->name('v1.barobill-card-transactions.journal-entries.show');
|
||||
Route::post('/{id}/journal-entries', [BarobillCardTransactionController::class, 'storeJournalEntries'])->whereNumber('id')->name('v1.barobill-card-transactions.journal-entries.store');
|
||||
Route::delete('/{id}/journal-entries', [BarobillCardTransactionController::class, 'deleteJournalEntries'])->whereNumber('id')->name('v1.barobill-card-transactions.journal-entries.destroy');
|
||||
});
|
||||
|
||||
// Barobill Bank Transaction API (바로빌 은행 거래 - React 연동)
|
||||
Route::prefix('barobill-bank-transactions')->group(function () {
|
||||
Route::get('', [BarobillBankTransactionController::class, 'index'])->name('v1.barobill-bank-transactions.index');
|
||||
Route::get('/accounts', [BarobillBankTransactionController::class, 'accounts'])->name('v1.barobill-bank-transactions.accounts');
|
||||
Route::get('/balance-summary', [BarobillBankTransactionController::class, 'balanceSummary'])->name('v1.barobill-bank-transactions.balance-summary');
|
||||
Route::get('/splits', [BarobillBankTransactionController::class, 'getSplits'])->name('v1.barobill-bank-transactions.splits.show');
|
||||
Route::post('/splits', [BarobillBankTransactionController::class, 'saveSplits'])->name('v1.barobill-bank-transactions.splits.store');
|
||||
Route::delete('/splits', [BarobillBankTransactionController::class, 'deleteSplits'])->name('v1.barobill-bank-transactions.splits.destroy');
|
||||
Route::post('/override', [BarobillBankTransactionController::class, 'saveOverride'])->name('v1.barobill-bank-transactions.override');
|
||||
Route::post('/manual', [BarobillBankTransactionController::class, 'storeManual'])->name('v1.barobill-bank-transactions.manual.store');
|
||||
Route::put('/manual/{id}', [BarobillBankTransactionController::class, 'updateManual'])->whereNumber('id')->name('v1.barobill-bank-transactions.manual.update');
|
||||
Route::delete('/manual/{id}', [BarobillBankTransactionController::class, 'destroyManual'])->whereNumber('id')->name('v1.barobill-bank-transactions.manual.destroy');
|
||||
Route::get('/{id}/journal-entries', [BarobillBankTransactionController::class, 'getJournalEntries'])->whereNumber('id')->name('v1.barobill-bank-transactions.journal-entries.show');
|
||||
Route::post('/{id}/journal-entries', [BarobillBankTransactionController::class, 'storeJournalEntries'])->whereNumber('id')->name('v1.barobill-bank-transactions.journal-entries.store');
|
||||
Route::delete('/{id}/journal-entries', [BarobillBankTransactionController::class, 'deleteJournalEntries'])->whereNumber('id')->name('v1.barobill-bank-transactions.journal-entries.destroy');
|
||||
});
|
||||
|
||||
// Hometax Invoice API (홈택스 세금계산서 - React 연동)
|
||||
Route::prefix('hometax-invoices')->group(function () {
|
||||
Route::get('/sales', [HometaxInvoiceController::class, 'sales'])->name('v1.hometax-invoices.sales');
|
||||
Route::get('/purchases', [HometaxInvoiceController::class, 'purchases'])->name('v1.hometax-invoices.purchases');
|
||||
Route::get('/summary', [HometaxInvoiceController::class, 'summary'])->name('v1.hometax-invoices.summary');
|
||||
Route::post('', [HometaxInvoiceController::class, 'store'])->name('v1.hometax-invoices.store');
|
||||
Route::get('/{id}', [HometaxInvoiceController::class, 'show'])->whereNumber('id')->name('v1.hometax-invoices.show');
|
||||
Route::put('/{id}', [HometaxInvoiceController::class, 'update'])->whereNumber('id')->name('v1.hometax-invoices.update');
|
||||
Route::delete('/{id}', [HometaxInvoiceController::class, 'destroy'])->whereNumber('id')->name('v1.hometax-invoices.destroy');
|
||||
Route::get('/{id}/journals', [HometaxInvoiceController::class, 'getJournals'])->whereNumber('id')->name('v1.hometax-invoices.journals.show');
|
||||
Route::post('/{id}/journals', [HometaxInvoiceController::class, 'saveJournals'])->whereNumber('id')->name('v1.hometax-invoices.journals.store');
|
||||
Route::delete('/{id}/journals', [HometaxInvoiceController::class, 'deleteJournals'])->whereNumber('id')->name('v1.hometax-invoices.journals.destroy');
|
||||
Route::get('/{id}/journal-entries', [HometaxInvoiceController::class, 'getJournalEntries'])->whereNumber('id')->name('v1.hometax-invoices.journal-entries.show');
|
||||
Route::post('/{id}/journal-entries', [HometaxInvoiceController::class, 'storeJournalEntries'])->whereNumber('id')->name('v1.hometax-invoices.journal-entries.store');
|
||||
Route::delete('/{id}/journal-entries', [HometaxInvoiceController::class, 'deleteJournalEntries'])->whereNumber('id')->name('v1.hometax-invoices.journal-entries.destroy');
|
||||
});
|
||||
|
||||
// Tax Invoice API (세금계산서)
|
||||
Route::prefix('tax-invoices')->group(function () {
|
||||
Route::get('', [TaxInvoiceController::class, 'index'])->name('v1.tax-invoices.index');
|
||||
|
||||
Reference in New Issue
Block a user