Files
sam-manage/app/Http/Controllers/Sales/TenantProspectController.php
pro 32bb5795d1 feat:고객관리 상세/수정 모달창 구현
- TenantProspectController에 modalShow, modalEdit 메서드 추가
- prospects 라우트에 modal-show, modal-edit 엔드포인트 추가
- index.blade.php에 모달 컨테이너 및 JavaScript 추가
- partials/show-modal.blade.php, edit-modal.blade.php 신규 생성

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 22:20:32 +09:00

272 lines
8.4 KiB
PHP

<?php
namespace App\Http\Controllers\Sales;
use App\Http\Controllers\Controller;
use App\Models\Sales\TenantProspect;
use App\Services\Sales\TenantProspectService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;
/**
* 영업권(명함등록) 관리 컨트롤러
*/
class TenantProspectController extends Controller
{
public function __construct(
private TenantProspectService $service
) {}
/**
* 목록 페이지
*/
public function index(Request $request): View|Response
{
if ($request->header('HX-Request')) {
return response('', 200)->header('HX-Redirect', route('sales.prospects.index'));
}
$filters = [
'search' => $request->get('search'),
'status' => $request->get('status'),
'registered_by' => $request->get('registered_by'),
];
$prospects = $this->service->getProspects($filters)->paginate(20);
$stats = $this->service->getStats();
return view('sales.prospects.index', compact('prospects', 'stats'));
}
/**
* 등록 폼
*/
public function create(Request $request): View|Response
{
// HTMX 요청이면 JavaScript 로드를 위해 전체 페이지로 리다이렉트
if ($request->header('HX-Request')) {
return response('', 200)->header('HX-Redirect', route('sales.prospects.create'));
}
return view('sales.prospects.create');
}
/**
* 등록 처리
*/
public function store(Request $request)
{
$validated = $request->validate([
'business_number' => 'required|string|max:20',
'company_name' => 'required|string|max:100',
'ceo_name' => 'nullable|string|max:50',
'contact_phone' => 'nullable|string|max:20',
'contact_email' => 'nullable|email|max:100',
'address' => 'nullable|string|max:500',
'business_card' => 'nullable|image|max:5120',
'business_card_image_data' => 'nullable|string',
'memo' => 'nullable|string|max:1000',
]);
// 등록 가능 여부 확인
$checkResult = $this->service->canRegister($validated['business_number']);
if (!$checkResult['can_register']) {
return redirect()->back()
->withInput()
->with('error', $checkResult['reason']);
}
// 등록자는 현재 로그인 사용자
$validated['registered_by'] = auth()->id();
// Base64 이미지 데이터가 있으면 전달
$businessCardBase64 = $validated['business_card_image_data'] ?? null;
unset($validated['business_card_image_data']);
$this->service->register(
$validated,
$request->file('business_card'),
$businessCardBase64
);
return redirect()->route('sales.prospects.index')
->with('success', '명함이 등록되었습니다. 2개월간 영업권이 유효합니다.');
}
/**
* 상세 페이지
*/
public function show(int $id): View
{
$prospect = TenantProspect::with(['registeredBy', 'tenant', 'convertedBy'])
->findOrFail($id);
return view('sales.prospects.show', compact('prospect'));
}
/**
* 수정 폼
*/
public function edit(int $id): View
{
$prospect = TenantProspect::findOrFail($id);
// 이미 전환된 경우 수정 불가
if ($prospect->isConverted()) {
return redirect()->route('sales.prospects.show', $id)
->with('error', '이미 테넌트로 전환된 영업권은 수정할 수 없습니다.');
}
return view('sales.prospects.edit', compact('prospect'));
}
/**
* 수정 처리
*/
public function update(Request $request, int $id)
{
$prospect = TenantProspect::findOrFail($id);
// 이미 전환된 경우 수정 불가
if ($prospect->isConverted()) {
return redirect()->route('sales.prospects.show', $id)
->with('error', '이미 테넌트로 전환된 영업권은 수정할 수 없습니다.');
}
$validated = $request->validate([
'company_name' => 'required|string|max:100',
'ceo_name' => 'nullable|string|max:50',
'contact_phone' => 'nullable|string|max:20',
'contact_email' => 'nullable|email|max:100',
'address' => 'nullable|string|max:500',
'business_card' => 'nullable|image|max:5120',
'id_card' => 'nullable|image|max:5120',
'bankbook' => 'nullable|image|max:5120',
'memo' => 'nullable|string|max:1000',
]);
$this->service->update(
$prospect,
$validated,
$request->file('business_card'),
$request->file('id_card'),
$request->file('bankbook')
);
return redirect()->route('sales.prospects.show', $id)
->with('success', '영업권 정보가 수정되었습니다.');
}
/**
* 삭제 처리
*/
public function destroy(int $id)
{
$prospect = TenantProspect::findOrFail($id);
// 이미 전환된 경우 삭제 불가
if ($prospect->isConverted()) {
return redirect()->route('sales.prospects.index')
->with('error', '이미 테넌트로 전환된 영업권은 삭제할 수 없습니다.');
}
// 본인 또는 관리자만 삭제 가능
if ($prospect->registered_by !== auth()->id()) {
// TODO: 관리자 권한 체크 추가
}
$prospect->delete();
return redirect()->route('sales.prospects.index')
->with('success', '영업권이 삭제되었습니다.');
}
/**
* 테넌트 전환
*/
public function convert(int $id)
{
$prospect = TenantProspect::findOrFail($id);
// 이미 전환된 경우
if ($prospect->isConverted()) {
return redirect()->route('sales.prospects.show', $id)
->with('error', '이미 테넌트로 전환되었습니다.');
}
// 만료된 경우
if ($prospect->isExpired()) {
return redirect()->route('sales.prospects.show', $id)
->with('error', '만료된 영업권은 전환할 수 없습니다.');
}
$tenant = $this->service->convertToTenant($prospect, auth()->id());
return redirect()->route('sales.prospects.show', $id)
->with('success', "테넌트로 전환되었습니다. (테넌트 ID: {$tenant->id})");
}
/**
* 사업자번호 중복 체크 (AJAX)
*/
public function checkBusinessNumber(Request $request)
{
$businessNumber = $request->get('business_number');
$excludeId = $request->get('exclude_id');
$result = $this->service->canRegister($businessNumber, $excludeId);
return response()->json($result);
}
/**
* 모달용 상세 정보
*/
public function modalShow(int $id): View
{
$prospect = TenantProspect::with(['registeredBy', 'tenant', 'convertedBy'])
->findOrFail($id);
return view('sales.prospects.partials.show-modal', compact('prospect'));
}
/**
* 모달용 수정 폼
*/
public function modalEdit(int $id): View
{
$prospect = TenantProspect::findOrFail($id);
return view('sales.prospects.partials.edit-modal', compact('prospect'));
}
/**
* 첨부 이미지 삭제 (AJAX)
*/
public function deleteAttachment(Request $request, int $id)
{
$prospect = TenantProspect::findOrFail($id);
// 이미 전환된 경우 수정 불가
if ($prospect->isConverted()) {
return response()->json(['success' => false, 'message' => '전환된 영업권은 수정할 수 없습니다.'], 403);
}
$type = $request->get('type');
$allowedTypes = ['business_card', 'id_card', 'bankbook'];
if (!in_array($type, $allowedTypes)) {
return response()->json(['success' => false, 'message' => '잘못된 요청입니다.'], 400);
}
$result = $this->service->deleteAttachment($prospect, $type);
if ($result) {
return response()->json(['success' => true, 'message' => '이미지가 삭제되었습니다.']);
}
return response()->json(['success' => false, 'message' => '삭제할 이미지가 없습니다.'], 404);
}
}