feat:영업파트너 등록/수정에 사업자 정보(상호, 사업자등록번호, 주소) 필드 추가

- 컨트롤러 store/update 유효성 검사에 3개 필드 추가
- 서비스 create/update에서 SalesPartner 레코드 생성/업데이트
- 등록 폼(create.blade.php)에 사업자 정보 섹션 추가
- 수정 모달(edit-modal.blade.php)에 사업자 정보 섹션 추가
- 테스트 데이터 자동입력에 사업자 정보 포함

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-14 11:11:32 +09:00
parent 2fdf151c14
commit 7e9dec8bd3
4 changed files with 98 additions and 1 deletions

View File

@@ -68,6 +68,9 @@ public function store(Request $request)
'password' => 'required|string|min:4|confirmed',
'role_ids' => 'required|array|min:1',
'role_ids.*' => 'exists:roles,id',
'company_name' => 'nullable|string|max:100',
'biz_no' => 'nullable|string|max:20',
'address' => 'nullable|string|max:255',
'documents' => 'nullable|array',
'documents.*.file' => 'nullable|file|max:10240',
'documents.*.document_type' => 'nullable|string',
@@ -140,7 +143,7 @@ public function modalShow(int $id): View
*/
public function modalEdit(int $id): View
{
$partner = User::with(['userRoles.role', 'salesDocuments', 'parent'])->findOrFail($id);
$partner = User::with(['userRoles.role', 'salesDocuments', 'parent', 'salesPartner'])->findOrFail($id);
$roles = $this->service->getSalesRoles();
$currentRoleIds = $partner->userRoles->pluck('role_id')->toArray();
$documentTypes = SalesManagerDocument::DOCUMENT_TYPES;
@@ -185,6 +188,9 @@ public function update(Request $request, int $id)
'password' => 'nullable|string|min:4|confirmed',
'role_ids' => 'required|array|min:1',
'role_ids.*' => 'exists:roles,id',
'company_name' => 'nullable|string|max:100',
'biz_no' => 'nullable|string|max:20',
'address' => 'nullable|string|max:255',
'documents' => 'nullable|array',
'documents.*.file' => 'nullable|file|max:10240',
'documents.*.document_type' => 'nullable|string',

View File

@@ -6,6 +6,7 @@
use App\Models\DepartmentUser;
use App\Models\Role;
use App\Models\Sales\SalesManagerDocument;
use App\Models\Sales\SalesPartner;
use App\Models\User;
use App\Models\UserRole;
use Illuminate\Http\UploadedFile;
@@ -57,6 +58,21 @@ public function createSalesPartner(array $data, array $documents = []): User
// 4. 영업팀 부서 자동 할당
$this->assignSalesDepartment($user, $tenantId);
// 4-1. 사업자 정보 저장 (선택)
if (!empty($data['company_name']) || !empty($data['biz_no']) || !empty($data['address'])) {
SalesPartner::updateOrCreate(
['user_id' => $user->id],
[
'partner_code' => SalesPartner::generatePartnerCode(),
'partner_type' => 'sales',
'status' => 'active',
'company_name' => $data['company_name'] ?? null,
'biz_no' => $data['biz_no'] ?? null,
'address' => $data['address'] ?? null,
]
);
}
// 5. 첨부 서류 저장
if (!empty($documents)) {
$this->uploadDocuments($user, $tenantId, $documents);
@@ -93,6 +109,20 @@ public function updateSalesPartner(User $user, array $data, array $documents = [
$this->syncRoles($user, $tenantId, $data['role_ids']);
}
// 2-1. 사업자 정보 업데이트
if (array_key_exists('company_name', $data) || array_key_exists('biz_no', $data) || array_key_exists('address', $data)) {
$sp = SalesPartner::firstOrNew(['user_id' => $user->id]);
if (!$sp->exists) {
$sp->partner_code = SalesPartner::generatePartnerCode();
$sp->partner_type = 'sales';
$sp->status = 'active';
}
$sp->company_name = $data['company_name'] ?? $sp->company_name;
$sp->biz_no = $data['biz_no'] ?? $sp->biz_no;
$sp->address = $data['address'] ?? $sp->address;
$sp->save();
}
// 3. 새 첨부 서류 저장
if (!empty($documents)) {
$this->uploadDocuments($user, $tenantId, $documents);

View File

@@ -171,6 +171,33 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
</div>
</div>
<!-- 사업자 정보 (전자서명용) -->
<div class="bg-white rounded-lg shadow-sm p-6">
<h2 class="text-lg font-semibold text-gray-800 mb-1">사업자 정보</h2>
<p class="text-sm text-gray-400 mb-4">전자서명 계약 자동으로 채워집니다 (선택사항)</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">상호</label>
<input type="text" name="company_name" value="{{ old('company_name') }}"
placeholder="예: (주)홍길동무역"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">사업자등록번호</label>
<input type="text" name="biz_no" value="{{ old('biz_no') }}"
placeholder="000-00-00000"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-2">주소</label>
<input type="text" name="address" value="{{ old('address') }}"
placeholder="사업장 주소"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
</div>
<!-- 역할 조직 -->
<div class="bg-white rounded-lg shadow-sm p-6">
<h2 class="text-lg font-semibold text-gray-800 mb-4">역할 조직</h2>
@@ -544,6 +571,14 @@ function fillRandomData() {
document.querySelector('input[name="password"]').value = password;
document.querySelector('input[name="password_confirmation"]').value = password;
// 사업자 정보
const companyInput = document.querySelector('input[name="company_name"]');
const bizNoInput = document.querySelector('input[name="biz_no"]');
const addressInput = document.querySelector('input[name="address"]');
if (companyInput) companyInput.value = name + ' 개인사업자';
if (bizNoInput) bizNoInput.value = randomNum(100, 999) + '-' + randomNum(10, 99) + '-' + randomNum(10000, 99999);
if (addressInput) addressInput.value = '서울특별시 강남구 테헤란로 ' + randomNum(1, 500) + '번길 ' + randomNum(1, 30);
// 역할 체크박스 모두 체크
document.querySelectorAll('input[name="role_ids[]"]').forEach(cb => {
cb.checked = true;

View File

@@ -55,6 +55,32 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none foc
</div>
</div>
<!-- 사업자 정보 (전자서명용) -->
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-1">사업자 정보</h3>
<p class="text-xs text-gray-400 mb-3">전자서명 계약 자동으로 채워집니다 (선택사항)</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">상호</label>
<input type="text" name="company_name" value="{{ $partner->salesPartner?->company_name }}"
placeholder="예: (주)홍길동무역"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-700 mb-1">사업자등록번호</label>
<input type="text" name="biz_no" value="{{ $partner->salesPartner?->biz_no }}"
placeholder="000-00-00000"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
<div class="md:col-span-2">
<label class="block text-xs font-medium text-gray-700 mb-1">주소</label>
<input type="text" name="address" value="{{ $partner->salesPartner?->address }}"
placeholder="사업장 주소"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
</div>
</div>
</div>
<!-- 역할 -->
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h3 class="text-sm font-semibold text-gray-800 mb-3">역할 <span class="text-red-500">*</span></h3>