117 خطوط
4.9 KiB
PHP
117 خطوط
4.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Company;
|
|
use App\Models\Contact;
|
|
use App\Models\Deal;
|
|
use App\Models\Lead;
|
|
use App\Models\Product;
|
|
use App\Services\ActivityLogger;
|
|
use App\Support\AccessControl;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Gate;
|
|
|
|
class DealController extends Controller
|
|
{
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
Gate::authorize('viewAny', Deal::class);
|
|
$query = Deal::with('company:id,name', 'lead:id,company,first_name,last_name', 'contact:id,name', 'product:id,name', 'owner:id,name', 'pipeline:id,name', 'stage:id,name,color,probability');
|
|
AccessControl::scopeDeals($query, auth()->user());
|
|
foreach (['status', 'sales_stage', 'company_id', 'lead_id', 'owner_id', 'product_id', 'pipeline_id', 'deal_stage_id', 'forecast_category'] as $filter) {
|
|
if ($request->filled($filter)) {
|
|
$query->where($filter, $request->get($filter));
|
|
}
|
|
}
|
|
if ($request->search) {
|
|
$query->where('title', 'like', "%{$request->search}%");
|
|
}
|
|
|
|
return response()->json($query->latest()->paginate(min((int) $request->get('per_page', 15), 100)));
|
|
}
|
|
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
Gate::authorize('create', Deal::class);
|
|
$validated = $this->validated($request);
|
|
$validated['owner_id'] ??= auth()->id();
|
|
$this->authorizeRelations($validated);
|
|
$deal = Deal::create($validated + ['created_by' => auth()->id()]);
|
|
ActivityLogger::log('deal_created', "Deal {$deal->title} created", $deal);
|
|
|
|
return response()->json($deal->load('company', 'contact', 'product', 'owner'), 201);
|
|
}
|
|
|
|
public function show(Deal $deal): JsonResponse
|
|
{
|
|
Gate::authorize('view', $deal);
|
|
|
|
return response()->json($deal->load('company', 'lead', 'contact.phones', 'product.salesScript.sections', 'owner:id,name', 'pipeline', 'stage', 'stageHistory.fromStage', 'stageHistory.toStage', 'stageHistory.actor:id,name', 'customFieldValues.definition', 'tasks.assignee:id,name', 'notes.user:id,name', 'attachments.uploader:id,name'));
|
|
}
|
|
|
|
public function update(Request $request, Deal $deal): JsonResponse
|
|
{
|
|
Gate::authorize('update', $deal);
|
|
$validated = $this->validated($request, true);
|
|
$this->authorizeRelations($validated);
|
|
$deal->update($validated);
|
|
ActivityLogger::log('deal_updated', "Deal {$deal->title} updated", $deal);
|
|
|
|
return response()->json($deal->fresh('company', 'contact', 'product', 'owner'));
|
|
}
|
|
|
|
public function destroy(Deal $deal): JsonResponse
|
|
{
|
|
Gate::authorize('delete', $deal);
|
|
$deal->delete();
|
|
ActivityLogger::log('deal_deleted', "Deal {$deal->id} deleted");
|
|
|
|
return response()->json(['message' => 'فرصت فروش حذف شد']);
|
|
}
|
|
|
|
private function validated(Request $request, bool $partial = false): array
|
|
{
|
|
$sometimes = $partial ? 'sometimes|' : '';
|
|
|
|
return $request->validate([
|
|
'title' => $sometimes.'required|string|max:255',
|
|
'company_id' => 'nullable|exists:companies,id',
|
|
'lead_id' => 'nullable|exists:leads,id',
|
|
'contact_id' => 'nullable|exists:contacts,id',
|
|
'product_id' => 'nullable|exists:products,id',
|
|
'pipeline_id' => 'nullable|exists:pipelines,id',
|
|
'deal_stage_id' => 'nullable|exists:deal_stages,id',
|
|
'estimated_value' => 'nullable|numeric|min:0',
|
|
'final_amount' => 'nullable|numeric|min:0',
|
|
'win_probability' => 'nullable|integer|min:0|max:100',
|
|
'sales_stage' => 'nullable|string|max:80',
|
|
'expected_close_date' => 'nullable|date',
|
|
'owner_id' => 'nullable|exists:users,id',
|
|
'status' => 'nullable|string|max:40',
|
|
'won_lost_reason' => 'nullable|string|max:255',
|
|
'competitor' => 'nullable|string|max:255',
|
|
'forecast_category' => 'nullable|in:pipeline,best_case,commit,closed',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
}
|
|
|
|
private function authorizeRelations(array $validated): void
|
|
{
|
|
$user = auth()->user();
|
|
if (array_key_exists('owner_id', $validated)) {
|
|
abort_unless(AccessControl::canAssignUser($user, $validated['owner_id']), 403, 'مالک انتخابشده خارج از محدوده مجاز است.');
|
|
}
|
|
foreach (['company_id' => Company::class, 'lead_id' => Lead::class, 'contact_id' => Contact::class] as $key => $model) {
|
|
if (! empty($validated[$key])) {
|
|
Gate::authorize('view', $model::findOrFail($validated[$key]));
|
|
}
|
|
}
|
|
if (! empty($validated['product_id'])) {
|
|
Gate::authorize('view', Product::findOrFail($validated['product_id']));
|
|
}
|
|
}
|
|
}
|