fix: [barobill] 회원사 수정 시 바로빌 아이디 변경 가능하도록 개선

- 수정 모달에서 바로빌 아이디 필드 표시 (기존: hidden)
- 컨트롤러 update 메서드에 barobill_id 유효성 검증 추가
- 아이디/비밀번호 모두 '변경 시에만 입력' 힌트 표시
This commit is contained in:
김보곤
2026-02-21 13:54:08 +09:00
parent c5f4c53a50
commit b4d737948f
3 changed files with 71 additions and 57 deletions

View File

@@ -9,7 +9,6 @@
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;
@@ -19,6 +18,7 @@ class BarobillMemberController extends Controller
public function __construct(
protected BarobillService $barobillService
) {}
// 바로빌 파트너사 (본사) 테넌트 ID
private const HEADQUARTERS_TENANT_ID = 1;
@@ -33,7 +33,7 @@ private function validateMemberForUrlApi(int $id): BarobillMember|JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -59,6 +59,7 @@ private function validateMemberForUrlApi(int $id): BarobillMember|JsonResponse
*
* - 테넌트 1(본사)이면 자동으로 모든 회원사 표시
* - 다른 테넌트면 해당 테넌트의 회원사만 표시
*
* @param all_tenants bool 전체 테넌트 조회 모드 (수동 선택용)
*/
public function index(Request $request): JsonResponse|Response
@@ -76,30 +77,30 @@ public function index(Request $request): JsonResponse|Response
foreach ($missingTenants as $tenant) {
BarobillMember::create([
'tenant_id' => $tenant->id,
'biz_no' => $tenant->business_num ?? '',
'corp_name' => $tenant->company_name,
'ceo_name' => $tenant->ceo_name ?? '',
'barobill_id' => '',
'tenant_id' => $tenant->id,
'biz_no' => $tenant->business_num ?? '',
'corp_name' => $tenant->company_name,
'ceo_name' => $tenant->ceo_name ?? '',
'barobill_id' => '',
'barobill_pwd' => '',
'status' => 'pending',
'server_mode' => 'test',
'status' => 'pending',
'server_mode' => 'test',
]);
}
}
$query = BarobillMember::query()
// 본사(테넌트 1)이거나 전체 테넌트 모드일 때는 필터 없음
->when(!$isHeadquarters && !$allTenants && $tenantId, fn($q) => $q->where('tenant_id', $tenantId))
->when(! $isHeadquarters && ! $allTenants && $tenantId, fn ($q) => $q->where('tenant_id', $tenantId))
->when($request->search, function ($q, $search) {
$q->where(function ($q) use ($search) {
$q->where('corp_name', 'like', "%{$search}%")
->orWhere('biz_no', 'like', "%{$search}%")
->orWhere('barobill_id', 'like', "%{$search}%")
->orWhere('manager_name', 'like', "%{$search}%");
->orWhere('biz_no', 'like', "%{$search}%")
->orWhere('barobill_id', 'like', "%{$search}%")
->orWhere('manager_name', 'like', "%{$search}%");
});
})
->when($request->status, fn($q, $status) => $q->where('status', $status))
->when($request->status, fn ($q, $status) => $q->where('status', $status))
->with('tenant:id,company_name')
->orderBy('created_at', 'desc');
@@ -147,7 +148,7 @@ public function store(Request $request): JsonResponse
? $request->integer('tenant_id')
: session('selected_tenant_id');
if (!$tenantId) {
if (! $tenantId) {
return response()->json([
'success' => false,
'message' => '테넌트를 선택해주세요.',
@@ -192,7 +193,7 @@ public function store(Request $request): JsonResponse
unset($validated['skip_api']);
// 바로빌 API 호출 (skip_api가 false일 때만)
if (!$skipApi) {
if (! $skipApi) {
$apiResult = $this->barobillService->registCorp([
'biz_no' => $validated['biz_no'],
'corp_name' => $validated['corp_name'],
@@ -209,7 +210,7 @@ public function store(Request $request): JsonResponse
'tel' => $validated['tel'] ?? '',
]);
if (!$apiResult['success']) {
if (! $apiResult['success']) {
Log::error('바로빌 회원사 등록 API 실패', [
'biz_no' => $validated['biz_no'],
'error' => $apiResult['error'] ?? 'Unknown error',
@@ -253,7 +254,7 @@ public function show(Request $request, int $id): JsonResponse
{
$member = BarobillMember::with('tenant:id,company_name')->find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -269,7 +270,7 @@ public function show(Request $request, int $id): JsonResponse
// 비밀번호 설정 여부 추가
$memberData = $member->toArray();
$memberData['has_password'] = !empty($member->barobill_pwd);
$memberData['has_password'] = ! empty($member->barobill_pwd);
return response()->json([
'success' => true,
@@ -286,7 +287,7 @@ public function update(Request $request, int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -303,10 +304,14 @@ public function update(Request $request, int $id): JsonResponse
'manager_email' => 'nullable|email|max:100',
'manager_hp' => 'nullable|string|max:20',
'status' => 'nullable|in:active,inactive,pending',
'barobill_pwd' => 'nullable|string|max:255', // 비밀번호 선택적 업데이트
'barobill_id' => 'nullable|string|max:50',
'barobill_pwd' => 'nullable|string|max:255',
]);
// 비밀번호가 비어있으면 제외 (기존 값 유지)
// 비어있으면 제외 (기존 값 유지)
if (empty($validated['barobill_id'])) {
unset($validated['barobill_id']);
}
if (empty($validated['barobill_pwd'])) {
unset($validated['barobill_pwd']);
}
@@ -327,7 +332,7 @@ public function destroy(int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -357,7 +362,7 @@ public function stats(Request $request): JsonResponse|Response
$isHeadquarters = $tenantId == self::HEADQUARTERS_TENANT_ID;
$query = BarobillMember::query()
->when(!$isHeadquarters && !$allTenants && $tenantId, fn($q) => $q->where('tenant_id', $tenantId));
->when(! $isHeadquarters && ! $allTenants && $tenantId, fn ($q) => $q->where('tenant_id', $tenantId));
$stats = [
'total' => (clone $query)->count(),
@@ -403,7 +408,7 @@ public function getBankAccountUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '계좌 등록 URL을 가져오는데 실패했습니다.',
@@ -438,7 +443,7 @@ public function getBankAccountManageUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '계좌 관리 URL을 가져오는데 실패했습니다.',
@@ -473,7 +478,7 @@ public function getCertificateUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '인증서 등록 URL을 가져오는데 실패했습니다.',
@@ -508,7 +513,7 @@ public function getCashChargeUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '충전 URL을 가져오는데 실패했습니다.',
@@ -534,7 +539,7 @@ public function getCertificateStatus(int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -569,7 +574,7 @@ public function getBalance(int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -578,7 +583,7 @@ public function getBalance(int $id): JsonResponse
$result = $this->barobillService->getBalanceCostAmount($member->biz_no);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '잔액 조회에 실패했습니다.',
@@ -603,7 +608,7 @@ public function getBankAccounts(int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -612,7 +617,7 @@ public function getBankAccounts(int $id): JsonResponse
$result = $this->barobillService->getBankAccounts($member->biz_no);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '계좌 목록 조회에 실패했습니다.',
@@ -642,7 +647,7 @@ public function getBankAccountLogUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '입출금내역 URL을 가져오는데 실패했습니다.',
@@ -681,7 +686,7 @@ public function getCardUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '카드 등록 URL을 가져오는데 실패했습니다.',
@@ -716,7 +721,7 @@ public function getCardManageUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '카드 관리 URL을 가져오는데 실패했습니다.',
@@ -749,7 +754,7 @@ public function getCardLogUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '카드 사용내역 URL을 가져오는데 실패했습니다.',
@@ -775,7 +780,7 @@ public function getCards(int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -784,7 +789,7 @@ public function getCards(int $id): JsonResponse
$result = $this->barobillService->getCards($member->biz_no);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '카드 목록 조회에 실패했습니다.',
@@ -818,7 +823,7 @@ public function getTaxInvoiceUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '세금계산서 발행 URL을 가져오는데 실패했습니다.',
@@ -851,7 +856,7 @@ public function getTaxInvoiceListUrl(Request $request, int $id): JsonResponse
$member->barobill_pwd
);
if (!$result['success']) {
if (! $result['success']) {
return response()->json([
'success' => false,
'message' => '세금계산서 목록 URL을 가져오는데 실패했습니다.',
@@ -892,7 +897,7 @@ public function updateServerMode(Request $request, int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',
@@ -909,7 +914,7 @@ public function updateServerMode(Request $request, int $id): JsonResponse
]);
// 확인 체크
if (!$validated['confirmed']) {
if (! $validated['confirmed']) {
return response()->json([
'success' => false,
'message' => '서버 변경 경고를 확인해주세요.',
@@ -923,7 +928,7 @@ public function updateServerMode(Request $request, int $id): JsonResponse
if ($oldMode === $newMode) {
return response()->json([
'success' => true,
'message' => '서버 모드가 이미 ' . ($newMode === 'test' ? '테스트' : '운영') . ' 서버입니다.',
'message' => '서버 모드가 이미 '.($newMode === 'test' ? '테스트' : '운영').' 서버입니다.',
'data' => $member,
]);
}
@@ -941,7 +946,7 @@ public function updateServerMode(Request $request, int $id): JsonResponse
return response()->json([
'success' => true,
'message' => ($newMode === 'test' ? '테스트' : '운영') . ' 서버로 변경되었습니다.',
'message' => ($newMode === 'test' ? '테스트' : '운영').' 서버로 변경되었습니다.',
'data' => $member->fresh(),
]);
}
@@ -953,7 +958,7 @@ public function getServerMode(int $id): JsonResponse
{
$member = BarobillMember::find($id);
if (!$member) {
if (! $member) {
return response()->json([
'success' => false,
'message' => '회원사를 찾을 수 없습니다.',

View File

@@ -226,8 +226,10 @@ class="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 tran
// 비밀번호 필드 표시 (필수 입력)
document.getElementById('passwordFields').classList.remove('hidden');
document.getElementById('barobillIdField').classList.remove('hidden'); // 아이디 표시
document.getElementById('pwdRequired').classList.remove('hidden'); // 필수 표시
document.getElementById('pwdHint').classList.add('hidden'); // 힌트 숨김
document.getElementById('idRequired').classList.remove('hidden'); // 아이디 필수 표시
document.getElementById('idHint').classList.add('hidden'); // 아이디 힌트 숨김
document.getElementById('pwdRequired').classList.remove('hidden'); // 비밀번호 필수 표시
document.getElementById('pwdHint').classList.add('hidden'); // 비밀번호 힌트 숨김
document.getElementById('pwdStatusBadge').classList.add('hidden'); // 상태 뱃지 숨김
// API 검증 건너뛰기 체크박스 표시 (신규 등록 시에만)
document.getElementById('skipApiField').classList.remove('hidden');
@@ -252,8 +254,10 @@ class="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 tran
document.getElementById('submitBtn').textContent = '수정하기';
// 비밀번호 필드 표시 (선택적 입력)
document.getElementById('passwordFields').classList.remove('hidden');
document.getElementById('barobillIdField').classList.add('hidden'); // 아이디는 숨김
document.getElementById('pwdRequired').classList.add('hidden'); // 필수 표시 숨김
document.getElementById('barobillIdField').classList.remove('hidden'); // 아이디 표시 (수정 가능)
document.getElementById('idRequired').classList.add('hidden'); // 아이디 필수 표시 숨김
document.getElementById('idHint').classList.remove('hidden'); // 아이디 변경 힌트 표시
document.getElementById('pwdRequired').classList.add('hidden'); // 비밀번호 필수 표시 숨김
document.getElementById('pwdHint').classList.remove('hidden'); // 힌트 표시
// API 검증 건너뛰기 체크박스 숨김 (수정 시에는 불필요)
document.getElementById('skipApiField').classList.add('hidden');
@@ -279,7 +283,7 @@ class="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 tran
this.form.biz_type.value = m.biz_type || '';
this.form.biz_class.value = m.biz_class || '';
this.form.barobill_id.value = m.barobill_id || '';
this.form.barobill_id.disabled = true;
this.form.barobill_id.disabled = false;
this.form.manager_name.value = m.manager_name || '';
this.form.manager_email.value = m.manager_email || '';
this.form.manager_hp.value = m.manager_hp || '';
@@ -330,12 +334,14 @@ class="flex-1 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 tran
// skip_api 체크박스 값을 boolean으로 변환
data.skip_api = this.form.querySelector('input[name="skip_api"]')?.checked || false;
// 수정 시 disabled 필드 제외
// 수정 시 불필요 필드 제외
if (this.isEditing) {
delete data.biz_no;
delete data.barobill_id;
delete data.skip_api; // 수정 시에는 skip_api 불필요
// 비밀번호가 비어있으면 제외 (서버에서도 빈 값은 무시)
delete data.skip_api;
// 아이디/비밀번호가 비어있으면 제외 (서버에서 기존 값 유지)
if (!data.barobill_id) {
delete data.barobill_id;
}
if (!data.barobill_pwd) {
delete data.barobill_pwd;
}

View File

@@ -91,8 +91,11 @@ class="w-10 h-10 flex items-center justify-center rounded-full text-gray-600 hov
<!-- 바로빌 계정 -->
<div id="passwordFields" class="col-span-2 grid grid-cols-2 gap-4">
<div id="barobillIdField">
<label class="block text-xs font-bold text-gray-400 uppercase mb-1">바로빌 아이디 <span class="text-red-500">*</span></label>
<input type="text" name="barobill_id" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:opacity-50" placeholder="barobill_id">
<label class="block text-xs font-bold text-gray-400 uppercase mb-1">
바로빌 아이디 <span id="idRequired" class="text-red-500">*</span>
</label>
<input type="text" name="barobill_id" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="barobill_id">
<p id="idHint" class="hidden text-xs text-gray-500 mt-1">변경 시에만 입력</p>
</div>
<div>
<label id="pwdLabel" class="block text-xs font-bold text-gray-400 uppercase mb-1">