- 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>
272 lines
8.4 KiB
PHP
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);
|
|
}
|
|
}
|