153 خطوط
6.4 KiB
PHP
153 خطوط
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Resources\FollowUpResource;
|
|
use App\Http\Responses\ApiResponse;
|
|
use App\Models\FollowUp;
|
|
use App\Models\Lead;
|
|
use App\Services\ActivityLogger;
|
|
use App\Services\FollowUpService;
|
|
use App\Support\AccessControl;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Gate;
|
|
|
|
class FollowUpController extends Controller
|
|
{
|
|
public function __construct(private FollowUpService $service) {}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
Gate::authorize('viewAny', FollowUp::class);
|
|
$query = FollowUp::with('lead:id,first_name,last_name,company,phone', 'user:id,name', 'creator:id,name');
|
|
AccessControl::scopeFollowUps($query, $request->user());
|
|
$this->applyFilters($query, $request);
|
|
|
|
$direction = $request->get('sort', '-created_at') === 'created_at' ? 'asc' : 'desc';
|
|
$paginator = $query->orderBy('created_at', $direction)->paginate(min((int) $request->get('per_page', 15), 100));
|
|
|
|
return ApiResponse::paginated($paginator, fn (FollowUp $followUp) => $this->resource($followUp));
|
|
}
|
|
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'lead_id' => 'required|exists:leads,id',
|
|
'user_id' => 'nullable|exists:users,id',
|
|
'call_id' => 'nullable|exists:calls,id',
|
|
'scheduled_at' => 'required|date',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
$lead = Lead::findOrFail($validated['lead_id']);
|
|
Gate::authorize('create', [FollowUp::class, $lead]);
|
|
$assigneeId = (int) ($validated['user_id'] ?? $request->user()->id);
|
|
abort_unless(AccessControl::canAssignUser($request->user(), $assigneeId), 403, 'مسئول انتخابشده خارج از محدوده مجاز است.');
|
|
|
|
$followUp = $this->service->schedule(
|
|
$lead,
|
|
$assigneeId,
|
|
$validated['scheduled_at'],
|
|
$request->user(),
|
|
$validated['notes'] ?? null,
|
|
$validated['call_id'] ?? null,
|
|
);
|
|
|
|
return ApiResponse::success($this->resource($followUp), 201, 'پیگیری ایجاد شد.');
|
|
}
|
|
|
|
public function show(FollowUp $followUp): JsonResponse
|
|
{
|
|
Gate::authorize('view', $followUp);
|
|
|
|
return ApiResponse::success($this->resource($followUp->load('lead:id,first_name,last_name,company,phone', 'user:id,name', 'creator:id,name')));
|
|
}
|
|
|
|
public function update(Request $request, FollowUp $followUp): JsonResponse
|
|
{
|
|
Gate::authorize('update', $followUp);
|
|
$validated = $request->validate([
|
|
'user_id' => 'sometimes|exists:users,id',
|
|
'scheduled_at' => 'sometimes|date',
|
|
'notes' => 'nullable|string',
|
|
'status' => 'sometimes|string|in:pending,completed,cancelled',
|
|
]);
|
|
if (isset($validated['user_id'])) {
|
|
abort_unless(AccessControl::canAssignUser($request->user(), (int) $validated['user_id']), 403, 'مسئول انتخابشده خارج از محدوده مجاز است.');
|
|
}
|
|
$followUp = $this->service->update($followUp, $validated);
|
|
|
|
return ApiResponse::success($this->resource($followUp), message: 'پیگیری ویرایش شد.');
|
|
}
|
|
|
|
public function markDone(FollowUp $followUp): JsonResponse
|
|
{
|
|
Gate::authorize('complete', $followUp);
|
|
$followUp->update(['status' => 'completed', 'completed_at' => now(), 'is_overdue' => false]);
|
|
ActivityLogger::log('follow_up_completed', "Follow-up {$followUp->id} completed", $followUp);
|
|
|
|
return ApiResponse::success($this->resource($followUp->fresh(['lead:id,first_name,last_name,company,phone', 'user:id,name', 'creator:id,name'])), message: 'پیگیری تکمیل شد.');
|
|
}
|
|
|
|
public function destroy(FollowUp $followUp): JsonResponse
|
|
{
|
|
Gate::authorize('delete', $followUp);
|
|
$followUp->delete();
|
|
ActivityLogger::log('follow_up_deleted', "Follow-up {$followUp->id} deleted", $followUp);
|
|
|
|
return ApiResponse::success(null, message: 'پیگیری حذف شد.');
|
|
}
|
|
|
|
public function today(Request $request): JsonResponse
|
|
{
|
|
Gate::authorize('viewAny', FollowUp::class);
|
|
$query = FollowUp::with('lead:id,first_name,last_name,company,phone', 'user:id,name', 'creator:id,name')
|
|
->whereDate('scheduled_at', now()->toDateString())
|
|
->where('status', 'pending');
|
|
AccessControl::scopeFollowUps($query, $request->user());
|
|
$direction = $request->get('sort', '-created_at') === 'created_at' ? 'asc' : 'desc';
|
|
$paginator = $query->orderBy('created_at', $direction)->paginate(min((int) $request->get('per_page', 50), 100));
|
|
|
|
return ApiResponse::paginated($paginator, fn (FollowUp $followUp) => $this->resource($followUp));
|
|
}
|
|
|
|
public function overdue(Request $request): JsonResponse
|
|
{
|
|
Gate::authorize('viewAny', FollowUp::class);
|
|
$query = FollowUp::with('lead:id,first_name,last_name,company,phone', 'user:id,name', 'creator:id,name')
|
|
->where('status', 'pending')
|
|
->where(fn ($overdue) => $overdue->where('is_overdue', true)->orWhere('scheduled_at', '<', now()));
|
|
AccessControl::scopeFollowUps($query, $request->user());
|
|
$direction = $request->get('sort', '-created_at') === 'created_at' ? 'asc' : 'desc';
|
|
$paginator = $query->orderBy('created_at', $direction)->paginate(min((int) $request->get('per_page', 50), 100));
|
|
|
|
return ApiResponse::paginated($paginator, fn (FollowUp $followUp) => $this->resource($followUp));
|
|
}
|
|
|
|
private function applyFilters($query, Request $request): void
|
|
{
|
|
if ($request->filled('status')) {
|
|
$query->where('status', $request->get('status'));
|
|
}
|
|
if ($request->filled('lead_id')) {
|
|
$query->where('lead_id', $request->integer('lead_id'));
|
|
}
|
|
if ($request->filled('user_id')) {
|
|
$query->where('user_id', $request->integer('user_id'));
|
|
}
|
|
if ($request->filled('date_from')) {
|
|
$query->whereDate('scheduled_at', '>=', $request->get('date_from'));
|
|
}
|
|
if ($request->filled('date_to')) {
|
|
$query->whereDate('scheduled_at', '<=', $request->get('date_to'));
|
|
}
|
|
}
|
|
|
|
private function resource(FollowUp $followUp): array
|
|
{
|
|
return (new FollowUpResource($followUp))->resolve(request());
|
|
}
|
|
}
|