diff --git a/app/Http/Controllers/Sales/BusinessCardRequestController.php b/app/Http/Controllers/Sales/BusinessCardRequestController.php index 2237988c..637977d3 100644 --- a/app/Http/Controllers/Sales/BusinessCardRequestController.php +++ b/app/Http/Controllers/Sales/BusinessCardRequestController.php @@ -30,7 +30,7 @@ public function index(Request $request): View|Response } /** - * 명함신청 처리 (관리자 전용 - 대기/완료 2분할) + * 명함신청 처리 (관리자 전용) */ public function manage(Request $request): View|Response { @@ -45,11 +45,13 @@ public function manage(Request $request): View|Response $search = $request->get('search'); $stats = $this->service->getStats(); $pendingRequests = $this->service->getPendingRequests($search); + $orderedRequests = $this->service->getOrderedRequests($search); $processedRequests = $this->service->getProcessedRequests($search); return view('sales.business-cards.admin-index', compact( 'stats', 'pendingRequests', + 'orderedRequests', 'processedRequests', 'search' )); @@ -75,6 +77,28 @@ public function store(Request $request) ->with('success', '명함 신청이 완료되었습니다.'); } + /** + * 제작의뢰 (관리자 전용) + */ + public function order(Request $request, int $id) + { + if (! auth()->user()->isAdmin()) { + abort(403, '관리자만 처리할 수 있습니다.'); + } + + $this->service->order($id); + + if ($request->expectsJson() || $request->header('Accept') === 'application/json') { + return response()->json([ + 'success' => true, + 'message' => '제작의뢰 처리되었습니다.', + ]); + } + + return redirect()->route('sales.business-cards.manage') + ->with('success', '제작의뢰 처리되었습니다.'); + } + /** * 처리 완료 (관리자 전용) */ diff --git a/app/Models/Sales/BusinessCardRequest.php b/app/Models/Sales/BusinessCardRequest.php index 13322358..849dd631 100644 --- a/app/Models/Sales/BusinessCardRequest.php +++ b/app/Models/Sales/BusinessCardRequest.php @@ -10,10 +10,13 @@ class BusinessCardRequest extends Model { public const STATUS_PENDING = 'pending'; + public const STATUS_ORDERED = 'ordered'; + public const STATUS_PROCESSED = 'processed'; public const STATUS_LABELS = [ - self::STATUS_PENDING => '신청대기', + self::STATUS_PENDING => '요청', + self::STATUS_ORDERED => '제작의뢰', self::STATUS_PROCESSED => '처리완료', ]; @@ -27,12 +30,15 @@ class BusinessCardRequest extends Model 'quantity', 'memo', 'status', + 'ordered_by', + 'ordered_at', 'processed_by', 'processed_at', 'process_memo', ]; protected $casts = [ + 'ordered_at' => 'datetime', 'processed_at' => 'datetime', 'quantity' => 'integer', ]; @@ -51,6 +57,11 @@ public function requester(): BelongsTo return $this->belongsTo(User::class, 'user_id'); } + public function orderer(): BelongsTo + { + return $this->belongsTo(User::class, 'ordered_by'); + } + public function processor(): BelongsTo { return $this->belongsTo(User::class, 'processed_by'); @@ -65,6 +76,11 @@ public function scopePending($query) return $query->where('status', self::STATUS_PENDING); } + public function scopeOrdered($query) + { + return $query->where('status', self::STATUS_ORDERED); + } + public function scopeProcessed($query) { return $query->where('status', self::STATUS_PROCESSED); @@ -89,16 +105,34 @@ public function getIsPendingAttribute(): bool return $this->status === self::STATUS_PENDING; } + public function getIsOrderedAttribute(): bool + { + return $this->status === self::STATUS_ORDERED; + } + // ========================================================================= // 헬퍼 메서드 // ========================================================================= - public function markAsProcessed(int $processedBy, ?string $memo = null): bool + public function markAsOrdered(int $orderedBy): bool { if (! $this->is_pending) { return false; } + $this->status = self::STATUS_ORDERED; + $this->ordered_by = $orderedBy; + $this->ordered_at = now(); + + return $this->save(); + } + + public function markAsProcessed(int $processedBy, ?string $memo = null): bool + { + if (! $this->is_ordered) { + return false; + } + $this->status = self::STATUS_PROCESSED; $this->processed_by = $processedBy; $this->processed_at = now(); diff --git a/app/Services/Sales/BusinessCardRequestService.php b/app/Services/Sales/BusinessCardRequestService.php index 09ca94cb..91765881 100644 --- a/app/Services/Sales/BusinessCardRequestService.php +++ b/app/Services/Sales/BusinessCardRequestService.php @@ -7,13 +7,15 @@ class BusinessCardRequestService { /** - * 뱃지용 대기 건수 + * 뱃지용 대기 건수 (요청 + 제작의뢰) */ public function getPendingCount(): int { $tenantId = session('selected_tenant_id', 1); - return BusinessCardRequest::ofTenant($tenantId)->pending()->count(); + return BusinessCardRequest::ofTenant($tenantId) + ->whereIn('status', [BusinessCardRequest::STATUS_PENDING, BusinessCardRequest::STATUS_ORDERED]) + ->count(); } /** @@ -26,6 +28,7 @@ public function getStats(): array return [ 'pending' => (clone $base)->pending()->count(), + 'ordered' => (clone $base)->ordered()->count(), 'processed_today' => (clone $base)->processed() ->whereDate('processed_at', today()) ->count(), @@ -34,26 +37,25 @@ public function getStats(): array } /** - * 대기 목록 + * 신규 요청 목록 */ public function getPendingRequests(?string $search = null, int $perPage = 20) { - $tenantId = session('selected_tenant_id', 1); - - $query = BusinessCardRequest::ofTenant($tenantId) - ->pending() + return $this->buildQuery(BusinessCardRequest::STATUS_PENDING, $search) ->with('requester') - ->latest(); + ->latest() + ->paginate($perPage, ['*'], 'pending_page'); + } - if ($search) { - $query->where(function ($q) use ($search) { - $q->where('name', 'like', "%{$search}%") - ->orWhere('phone', 'like', "%{$search}%") - ->orWhere('email', 'like', "%{$search}%"); - }); - } - - return $query->paginate($perPage, ['*'], 'pending_page'); + /** + * 제작의뢰 목록 + */ + public function getOrderedRequests(?string $search = null, int $perPage = 20) + { + return $this->buildQuery(BusinessCardRequest::STATUS_ORDERED, $search) + ->with(['requester', 'orderer']) + ->latest('ordered_at') + ->paginate($perPage, ['*'], 'ordered_page'); } /** @@ -61,22 +63,10 @@ public function getPendingRequests(?string $search = null, int $perPage = 20) */ public function getProcessedRequests(?string $search = null, int $perPage = 20) { - $tenantId = session('selected_tenant_id', 1); - - $query = BusinessCardRequest::ofTenant($tenantId) - ->processed() + return $this->buildQuery(BusinessCardRequest::STATUS_PROCESSED, $search) ->with(['requester', 'processor']) - ->latest('processed_at'); - - if ($search) { - $query->where(function ($q) use ($search) { - $q->where('name', 'like', "%{$search}%") - ->orWhere('phone', 'like', "%{$search}%") - ->orWhere('email', 'like', "%{$search}%"); - }); - } - - return $query->paginate($perPage, ['*'], 'processed_page'); + ->latest('processed_at') + ->paginate($perPage, ['*'], 'processed_page'); } /** @@ -105,6 +95,18 @@ public function createRequest(array $data): BusinessCardRequest return BusinessCardRequest::create($data); } + /** + * 제작의뢰 + */ + public function order(int $id): BusinessCardRequest + { + $tenantId = session('selected_tenant_id', 1); + $request = BusinessCardRequest::ofTenant($tenantId)->findOrFail($id); + $request->markAsOrdered(auth()->id()); + + return $request; + } + /** * 처리 완료 */ @@ -116,4 +118,20 @@ public function process(int $id, ?string $memo = null): BusinessCardRequest return $request; } + + private function buildQuery(string $status, ?string $search) + { + $tenantId = session('selected_tenant_id', 1); + $query = BusinessCardRequest::ofTenant($tenantId)->where('status', $status); + + if ($search) { + $query->where(function ($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('phone', 'like', "%{$search}%") + ->orWhere('email', 'like', "%{$search}%"); + }); + } + + return $query; + } } diff --git a/resources/views/sales/business-cards/admin-index.blade.php b/resources/views/sales/business-cards/admin-index.blade.php index 3e77756f..9450b4f7 100644 --- a/resources/views/sales/business-cards/admin-index.blade.php +++ b/resources/views/sales/business-cards/admin-index.blade.php @@ -1,30 +1,36 @@ @extends('layouts.app') -@section('title', '명함신청 관리') +@section('title', '명함신청 처리') @section('content')
-

명함신청 관리

-

영업파트너의 명함 신청을 확인하고 처리합니다

+

명함신청 처리

+

+ 요청 → 제작의뢰처리완료 +

-
+
-
신청 대기
+
신규 요청
{{ number_format($stats['pending']) }}건
+
+
제작의뢰
+
{{ number_format($stats['ordered']) }}건
+
오늘 처리
{{ number_format($stats['processed_today']) }}건
-
-
전체 신청
-
{{ number_format($stats['total']) }}건
+
+
전체
+
{{ number_format($stats['total']) }}건
@@ -44,15 +50,15 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
- -
- + +
+
- 신청 대기 + 신규 요청 {{ $pendingRequests->total() }}건
@@ -67,7 +73,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc @forelse($pendingRequests as $req) - +
{{ $req->name }}
{{ $req->title ?: '-' }}
@@ -82,15 +88,15 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc @if($req->memo) - - + +
메모: {{ $req->memo }}
@@ -101,7 +107,7 @@ class="px-3 py-1 bg-emerald-500 hover:bg-emerald-600 text-white text-xs font-med - 대기 중인 명함 신청이 없습니다. + 신규 요청이 없습니다. @endforelse @@ -115,14 +121,14 @@ class="px-3 py-1 bg-emerald-500 hover:bg-emerald-600 text-white text-xs font-med @endif
- +
-
+
- + - 처리 완료 - {{ $processedRequests->total() }}건 + 제작 중 + {{ $orderedRequests->total() }}건
@@ -131,12 +137,13 @@ class="px-3 py-1 bg-emerald-500 hover:bg-emerald-600 text-white text-xs font-med - + + - @forelse($processedRequests as $req) - + @forelse($orderedRequests as $req) + + @empty - @endforelse
신청자 연락처 수량처리일의뢰일처리
{{ $req->name }}
{{ $req->title ?: '-' }}
@@ -149,38 +156,136 @@ class="px-3 py-1 bg-emerald-500 hover:bg-emerald-600 text-white text-xs font-med {{ number_format($req->quantity) }}매
-
{{ $req->processed_at?->format('m/d H:i') }}
-
{{ $req->processor?->name }}
+
{{ $req->ordered_at?->format('m/d H:i') }}
+
{{ $req->orderer?->name }}
+
+
+ - + - 처리된 명함 신청이 없습니다. + 제작의뢰 중인 건이 없습니다.
- @if($processedRequests->hasPages()) + @if($orderedRequests->hasPages())
- {{ $processedRequests->withQueryString()->links() }} + {{ $orderedRequests->withQueryString()->links() }}
@endif
+ + +
+
+ + + + 처리 완료 + {{ $processedRequests->total() }}건 +
+
+ + + + + + + + + + + + + @forelse($processedRequests as $req) + + + + + + + + + @empty + + + + @endforelse + +
신청자연락처수량의뢰일처리일처리자
+
{{ $req->name }}
+
{{ $req->title ?: '-' }}
+
+
{{ $req->phone }}
+
+ {{ number_format($req->quantity) }}매 + + {{ $req->ordered_at?->format('m/d H:i') ?? '-' }} + + {{ $req->processed_at?->format('m/d H:i') }} + + {{ $req->processor?->name }} +
+ 처리된 명함 신청이 없습니다. +
+
+ @if($processedRequests->hasPages()) +
+ {{ $processedRequests->withQueryString()->links() }} +
+ @endif +
@endsection @push('scripts') diff --git a/resources/views/sales/business-cards/partner-index.blade.php b/resources/views/sales/business-cards/partner-index.blade.php index 9045c933..4c1779c4 100644 --- a/resources/views/sales/business-cards/partner-index.blade.php +++ b/resources/views/sales/business-cards/partner-index.blade.php @@ -118,7 +118,9 @@ class="w-full sm:w-auto px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white font- {{ number_format($req->quantity) }}매 @if($req->status === 'pending') - 대기중 + 요청 + @elseif($req->status === 'ordered') + 제작중 @else 처리완료 @endif diff --git a/routes/web.php b/routes/web.php index 99780f80..e05884cc 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1287,6 +1287,7 @@ Route::get('business-cards', [\App\Http\Controllers\Sales\BusinessCardRequestController::class, 'index'])->name('business-cards.index'); Route::post('business-cards', [\App\Http\Controllers\Sales\BusinessCardRequestController::class, 'store'])->name('business-cards.store'); Route::get('business-cards/manage', [\App\Http\Controllers\Sales\BusinessCardRequestController::class, 'manage'])->name('business-cards.manage'); + Route::post('business-cards/{id}/order', [\App\Http\Controllers\Sales\BusinessCardRequestController::class, 'order'])->name('business-cards.order'); Route::post('business-cards/{id}/process', [\App\Http\Controllers\Sales\BusinessCardRequestController::class, 'process'])->name('business-cards.process'); // 매니저 검색 (리소스 라우트보다 먼저 정의해야 함!)