feat:바로빌 설정 페이지에 서비스 이용 옵션 체크박스 추가

- 4가지 서비스 옵션 체크박스 추가 (전자세금계산서, 계좌조회, 카드사용내역, 홈텍스매입/매출)
- BarobillSetting 모델 및 BarobillSettingController 생성
- 설정 API 라우트 추가 (/api/admin/barobill/settings)
- 담당자 정보 입력 필드 추가 (이름, 연락처, 이메일)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
pro
2026-01-22 16:51:56 +09:00
parent bddac80b75
commit ec9bcbe906
5 changed files with 475 additions and 116 deletions

View File

@@ -0,0 +1,114 @@
<?php
namespace App\Http\Controllers\Api\Admin\Barobill;
use App\Http\Controllers\Controller;
use App\Models\Barobill\BarobillSetting;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class BarobillSettingController extends Controller
{
/**
* 현재 테넌트의 바로빌 설정 조회
*/
public function show(): JsonResponse
{
$tenantId = session('tenant_id', 1);
$setting = BarobillSetting::where('tenant_id', $tenantId)->first();
if (!$setting) {
// 설정이 없으면 기본값 반환
return response()->json([
'success' => true,
'data' => [
'use_tax_invoice' => false,
'use_bank_account' => false,
'use_card_usage' => false,
'use_hometax' => false,
],
]);
}
return response()->json([
'success' => true,
'data' => $setting,
]);
}
/**
* 바로빌 설정 저장/수정
*/
public function store(Request $request): JsonResponse
{
$tenantId = session('tenant_id', 1);
$validated = $request->validate([
'use_tax_invoice' => 'nullable|boolean',
'use_bank_account' => 'nullable|boolean',
'use_card_usage' => 'nullable|boolean',
'use_hometax' => 'nullable|boolean',
'contact_name' => 'nullable|string|max:50',
'contact_tel' => 'nullable|string|max:20',
'contact_id' => 'nullable|email|max:100',
]);
try {
$setting = BarobillSetting::updateOrCreate(
['tenant_id' => $tenantId],
array_merge($validated, [
'corp_name' => $validated['corp_name'] ?? '미설정',
'ceo_name' => $validated['ceo_name'] ?? '미설정',
'corp_num' => $validated['corp_num'] ?? '0000000000',
])
);
return response()->json([
'success' => true,
'message' => '설정이 저장되었습니다.',
'data' => $setting,
]);
} catch (\Exception $e) {
Log::error('바로빌 설정 저장 실패', ['error' => $e->getMessage()]);
return response()->json([
'success' => false,
'message' => '설정 저장에 실패했습니다.',
], 500);
}
}
/**
* 서비스 이용 여부 확인 (다른 메뉴에서 참조용)
*/
public function checkService(string $service): JsonResponse
{
$tenantId = session('tenant_id', 1);
$setting = BarobillSetting::where('tenant_id', $tenantId)->first();
if (!$setting) {
return response()->json([
'success' => true,
'enabled' => false,
'message' => '바로빌 설정이 없습니다.',
]);
}
$enabled = match ($service) {
'tax_invoice', 'tax-invoice' => $setting->use_tax_invoice,
'bank_account', 'bank-account' => $setting->use_bank_account,
'card_usage', 'card-usage' => $setting->use_card_usage,
'hometax' => $setting->use_hometax,
default => false,
};
return response()->json([
'success' => true,
'enabled' => $enabled,
'service' => $service,
]);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Models\Barobill;
use App\Models\Tenants\Tenant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BarobillSetting extends Model
{
protected $table = 'barobill_settings';
protected $fillable = [
'tenant_id',
'corp_num',
'cert_key',
'barobill_id',
'corp_name',
'ceo_name',
'addr',
'biz_type',
'biz_class',
'contact_id',
'contact_name',
'contact_tel',
'is_active',
'auto_issue',
'use_tax_invoice',
'use_bank_account',
'use_card_usage',
'use_hometax',
'verified_at',
'created_by',
'updated_by',
];
protected $casts = [
'is_active' => 'boolean',
'auto_issue' => 'boolean',
'use_tax_invoice' => 'boolean',
'use_bank_account' => 'boolean',
'use_card_usage' => 'boolean',
'use_hometax' => 'boolean',
'verified_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* 테넌트 관계
*/
public function tenant(): BelongsTo
{
return $this->belongsTo(Tenant::class);
}
/**
* 활성화된 서비스 목록 반환
*/
public function getActiveServicesAttribute(): array
{
$services = [];
if ($this->use_tax_invoice) $services[] = 'tax_invoice';
if ($this->use_bank_account) $services[] = 'bank_account';
if ($this->use_card_usage) $services[] = 'card_usage';
if ($this->use_hometax) $services[] = 'hometax';
return $services;
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('barobill_settings', function (Blueprint $table) {
// 서비스 이용 옵션
$table->boolean('use_tax_invoice')->default(false)->after('auto_issue')->comment('전자세금계산서 이용');
$table->boolean('use_bank_account')->default(false)->after('use_tax_invoice')->comment('계좌조회 이용');
$table->boolean('use_card_usage')->default(false)->after('use_bank_account')->comment('카드사용내역 이용');
$table->boolean('use_hometax')->default(false)->after('use_card_usage')->comment('홈텍스매입/매출 이용');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('barobill_settings', function (Blueprint $table) {
$table->dropColumn(['use_tax_invoice', 'use_bank_account', 'use_card_usage', 'use_hometax']);
});
}
};

View File

@@ -13,136 +13,273 @@
</div>
<!-- 설정 카드 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- 이메일 설정 -->
<div class="bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<div>
<h3 class="font-semibold text-gray-800">이메일 설정</h3>
<p class="text-xs text-gray-500">세금계산서 발송 이메일 설정</p>
</div>
</div>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-600 mb-1">발신 이메일 주소</label>
<input type="email" class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="noreply@company.com">
</div>
<div>
<label class="block text-sm font-medium text-gray-600 mb-1">담당자 이메일</label>
<input type="email" class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="manager@company.com">
</div>
</div>
</div>
<!-- 동기화 설정 -->
<div class="bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</div>
<div>
<h3 class="font-semibold text-gray-800">데이터 동기화</h3>
<p class="text-xs text-gray-500">바로빌 데이터 동기화 설정</p>
</div>
</div>
<div class="space-y-4">
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p class="text-sm font-medium text-gray-700">자동 동기화</p>
<p class="text-xs text-gray-500">매일 자정 자동 동기화</p>
<form id="settings-form" class="space-y-6">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- 서비스 이용 설정 -->
<div class="bg-white rounded-lg shadow-sm p-6 lg:col-span-2">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-indigo-100 rounded-lg flex items-center justify-center">
<svg class="w-5 h-5 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:ring-2 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
<div>
<h3 class="font-semibold text-gray-800">서비스 이용 설정</h3>
<p class="text-xs text-gray-500">이용할 바로빌 서비스를 선택하세요</p>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- 전자세금계산서 -->
<label class="flex items-start gap-3 p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition border-2 border-transparent has-[:checked]:border-blue-500 has-[:checked]:bg-blue-50">
<input type="checkbox" name="use_tax_invoice" id="use_tax_invoice" class="mt-1 w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500">
<div>
<div class="flex items-center gap-2">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<span class="font-medium text-gray-800">전자세금계산서</span>
</div>
<p class="text-xs text-gray-500 mt-1">세금계산서 발행/수신 서비스</p>
</div>
</label>
<!-- 계좌조회 -->
<label class="flex items-start gap-3 p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition border-2 border-transparent has-[:checked]:border-green-500 has-[:checked]:bg-green-50">
<input type="checkbox" name="use_bank_account" id="use_bank_account" class="mt-1 w-4 h-4 text-green-600 bg-gray-100 border-gray-300 rounded focus:ring-green-500">
<div>
<div class="flex items-center gap-2">
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
</svg>
<span class="font-medium text-gray-800">계좌조회</span>
</div>
<p class="text-xs text-gray-500 mt-1">은행 계좌 거래내역 조회</p>
</div>
</label>
<!-- 카드사용내역 -->
<label class="flex items-start gap-3 p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition border-2 border-transparent has-[:checked]:border-purple-500 has-[:checked]:bg-purple-50">
<input type="checkbox" name="use_card_usage" id="use_card_usage" class="mt-1 w-4 h-4 text-purple-600 bg-gray-100 border-gray-300 rounded focus:ring-purple-500">
<div>
<div class="flex items-center gap-2">
<svg class="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
</svg>
<span class="font-medium text-gray-800">카드사용내역</span>
</div>
<p class="text-xs text-gray-500 mt-1">카드 결제내역 조회</p>
</div>
</label>
<!-- 홈텍스매입/매출 -->
<label class="flex items-start gap-3 p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition border-2 border-transparent has-[:checked]:border-orange-500 has-[:checked]:bg-orange-50">
<input type="checkbox" name="use_hometax" id="use_hometax" class="mt-1 w-4 h-4 text-orange-600 bg-gray-100 border-gray-300 rounded focus:ring-orange-500">
<div>
<div class="flex items-center gap-2">
<svg class="w-5 h-5 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
<span class="font-medium text-gray-800">홈텍스매입/매출</span>
</div>
<p class="text-xs text-gray-500 mt-1">홈텍스 매입/매출 자료 조회</p>
</div>
</label>
</div>
<button type="button" class="w-full px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition flex items-center justify-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
지금 동기화
</button>
</div>
</div>
<!-- API -->
<div class="bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
<svg class="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
<!-- 담당자 -->
<div class="bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
<div>
<h3 class="font-semibold text-gray-800">담당자 정보</h3>
<p class="text-xs text-gray-500">세금계산서 담당자 정보</p>
</div>
</div>
<div>
<h3 class="font-semibold text-gray-800">API 설정</h3>
<p class="text-xs text-gray-500">바로빌 API 연동 설정</p>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-600 mb-1">담당자명</label>
<input type="text" name="contact_name" id="contact_name" class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="홍길동">
</div>
<div>
<label class="block text-sm font-medium text-gray-600 mb-1">연락처</label>
<input type="tel" name="contact_tel" id="contact_tel" class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="02-1234-5678">
</div>
<div>
<label class="block text-sm font-medium text-gray-600 mb-1">담당자 이메일</label>
<input type="email" name="contact_id" id="contact_id" class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="manager@company.com">
</div>
</div>
</div>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-600 mb-1">API CERT KEY</label>
<input type="password" class="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="**********************">
<!-- 동기화 설정 -->
<div class="bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center">
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</div>
<div>
<h3 class="font-semibold text-gray-800">데이터 동기화</h3>
<p class="text-xs text-gray-500">바로빌 데이터 동기화 설정</p>
</div>
</div>
<div class="flex items-center gap-2 p-3 bg-green-50 rounded-lg border border-green-200">
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-sm text-green-700">API 연결 상태: 정상</span>
<div class="space-y-4">
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p class="text-sm font-medium text-gray-700">자동 동기화</p>
<p class="text-xs text-gray-500">매일 자정 자동 동기화</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:ring-2 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<button type="button" class="w-full px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition flex items-center justify-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
지금 동기화
</button>
</div>
</div>
</div>
<!-- 알림 설정 -->
<div class="bg-white rounded-lg shadow-sm p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-yellow-100 rounded-lg flex items-center justify-center">
<svg class="w-5 h-5 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
</div>
<div>
<h3 class="font-semibold text-gray-800">알림 설정</h3>
<p class="text-xs text-gray-500">세금계산서 관련 알림</p>
</div>
</div>
<div class="space-y-3">
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="text-sm text-gray-700">발행 완료 알림</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" checked class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:ring-2 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="text-sm text-gray-700">수신 알림</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" checked class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:ring-2 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="text-sm text-gray-700">만료 예정 알림</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:ring-2 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
<!-- 저장 버튼 -->
<div class="flex justify-end gap-3">
<button type="button" id="btn-reset" class="px-6 py-2.5 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition font-medium">
초기화
</button>
<button type="submit" id="btn-save" class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition font-medium flex items-center gap-2">
<svg class="w-4 h-4 hidden animate-spin" id="save-spinner" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
설정 저장
</button>
</div>
</div>
</form>
<!-- 저장 버튼 -->
<div class="mt-6 flex justify-end">
<button type="button" class="px-6 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition font-medium">
설정 저장
</button>
</div>
<!-- 토스트 메시지 -->
<div id="toast" class="fixed bottom-4 right-4 px-4 py-3 rounded-lg shadow-lg transform translate-y-full opacity-0 transition-all duration-300 z-50"></div>
</div>
@endsection
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('settings-form');
const toast = document.getElementById('toast');
const saveSpinner = document.getElementById('save-spinner');
const btnSave = document.getElementById('btn-save');
const btnReset = document.getElementById('btn-reset');
// 토스트 표시
function showToast(message, type = 'success') {
toast.textContent = message;
toast.className = 'fixed bottom-4 right-4 px-4 py-3 rounded-lg shadow-lg transform transition-all duration-300 z-50';
if (type === 'success') {
toast.classList.add('bg-green-600', 'text-white');
} else {
toast.classList.add('bg-red-600', 'text-white');
}
toast.classList.remove('translate-y-full', 'opacity-0');
setTimeout(() => {
toast.classList.add('translate-y-full', 'opacity-0');
}, 3000);
}
// 설정 로드
async function loadSettings() {
try {
const response = await fetch('/api/admin/barobill/settings');
const result = await response.json();
if (result.success && result.data) {
const data = result.data;
// 서비스 이용 설정
document.getElementById('use_tax_invoice').checked = data.use_tax_invoice || false;
document.getElementById('use_bank_account').checked = data.use_bank_account || false;
document.getElementById('use_card_usage').checked = data.use_card_usage || false;
document.getElementById('use_hometax').checked = data.use_hometax || false;
// 담당자 정보
document.getElementById('contact_name').value = data.contact_name || '';
document.getElementById('contact_tel').value = data.contact_tel || '';
document.getElementById('contact_id').value = data.contact_id || '';
}
} catch (error) {
console.error('설정 로드 실패:', error);
}
}
// 설정 저장
async function saveSettings(e) {
e.preventDefault();
saveSpinner.classList.remove('hidden');
btnSave.disabled = true;
const formData = {
use_tax_invoice: document.getElementById('use_tax_invoice').checked,
use_bank_account: document.getElementById('use_bank_account').checked,
use_card_usage: document.getElementById('use_card_usage').checked,
use_hometax: document.getElementById('use_hometax').checked,
contact_name: document.getElementById('contact_name').value,
contact_tel: document.getElementById('contact_tel').value,
contact_id: document.getElementById('contact_id').value,
};
try {
const response = await fetch('/api/admin/barobill/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
},
body: JSON.stringify(formData),
});
const result = await response.json();
if (result.success) {
showToast(result.message || '설정이 저장되었습니다.', 'success');
} else {
showToast(result.message || '설정 저장에 실패했습니다.', 'error');
}
} catch (error) {
console.error('설정 저장 실패:', error);
showToast('설정 저장 중 오류가 발생했습니다.', 'error');
} finally {
saveSpinner.classList.add('hidden');
btnSave.disabled = false;
}
}
// 초기화 버튼
btnReset.addEventListener('click', function() {
document.getElementById('use_tax_invoice').checked = false;
document.getElementById('use_bank_account').checked = false;
document.getElementById('use_card_usage').checked = false;
document.getElementById('use_hometax').checked = false;
document.getElementById('contact_name').value = '';
document.getElementById('contact_tel').value = '';
document.getElementById('contact_id').value = '';
showToast('설정이 초기화되었습니다.', 'success');
});
// 이벤트 리스너
form.addEventListener('submit', saveSettings);
// 초기 로드
loadSettings();
});
</script>
@endpush

View File

@@ -100,6 +100,13 @@
Route::post('/sync', [\App\Http\Controllers\Api\Admin\Barobill\BarobillConfigController::class, 'syncCompanies'])->name('sync');
});
// 바로빌 설정 API (회원사용)
Route::prefix('barobill/settings')->name('barobill.settings.')->group(function () {
Route::get('/', [\App\Http\Controllers\Api\Admin\Barobill\BarobillSettingController::class, 'show'])->name('show');
Route::post('/', [\App\Http\Controllers\Api\Admin\Barobill\BarobillSettingController::class, 'store'])->name('store');
Route::get('/check/{service}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillSettingController::class, 'checkService'])->name('check');
});
// 바로빌 회원사 관리 API
Route::prefix('barobill/members')->name('barobill.members.')->group(function () {
// 통계