$business->id, 'listing_code' => $data['listing_code'] ?? $this->aexUtil->generateListingCode($business->id), 'seller_contact_id' => $data['seller_contact_id'] ?? null, 'listing_type' => $data['listing_type'] ?? 'machine_used', 'scope_type' => $data['scope_type'] ?? 'whole', 'title' => $data['title'], 'description' => $data['description'] ?? null, 'location_text' => $data['location_text'] ?? null, 'site_country' => $data['site_country'] ?? null, 'year_manufactured' => $data['year_manufactured'] ?? null, 'condition_grade' => $data['condition_grade'] ?? null, 'asking_price' => (int) ($data['asking_price'] ?? 0), 'currency_id' => $data['currency_id'] ?? null, 'price_negotiable' => $data['price_negotiable'] ?? true, 'visibility' => $data['visibility'] ?? 'internal', 'status' => 'draft', 'ie_line_template_id' => $data['ie_line_template_id'] ?? null, 'ie_line_revision_id' => $data['ie_line_revision_id'] ?? null, 'ie_bom_id' => $data['ie_bom_id'] ?? null, 'ie_installed_asset_id' => $data['ie_installed_asset_id'] ?? null, 'maintenance_equipment_id' => $data['maintenance_equipment_id'] ?? null, 'asset_id' => $data['asset_id'] ?? null, 'parent_listing_id' => $data['parent_listing_id'] ?? null, 'photos' => $data['photos'] ?? null, 'documents' => $data['documents'] ?? null, 'created_by' => $user?->id, ]); $this->pipelineService->advanceStage($business, $listing, 'draft_listing', null, null, null, $user); $this->pipelineService->logEvent( $business, Listing::class, $listing->id, 'listing_created', 'عرضه جدید ثبت شد: '.$listing->title, $user ); return $listing->fresh(); }); } public function update(Listing $listing, array $data, ?User $user = null): Listing { if (in_array($listing->status, ['sold', 'rejected', 'withdrawn'], true)) { throw new \RuntimeException('عرضه در وضعیت فعلی قابل ویرایش نیست.'); } $listing->update(collect($data)->only($listing->getFillable())->toArray()); $this->pipelineService->logEvent( $listing->business, Listing::class, $listing->id, 'listing_updated', 'عرضه به‌روزرسانی شد', $user ); return $listing->fresh(); } public function submit(Business $business, Listing $listing, ?User $user = null): Listing { if ($listing->status !== 'draft') { throw new \RuntimeException('فقط عرضه‌های پیش‌نویس قابل ارسال هستند.'); } if (empty($listing->title)) { throw new \RuntimeException('عنوان عرضه الزامی است.'); } return DB::transaction(function () use ($business, $listing, $user) { $listing->update([ 'status' => 'submitted', 'submitted_at' => now(), ]); $this->pipelineService->advanceStage($business, $listing, 'submitted', null, null, null, $user); $this->pipelineService->logEvent( $business, Listing::class, $listing->id, 'listing_submitted', 'عرضه برای بررسی ارسال شد', $user ); return $listing->fresh(); }); } public function assignBroker(Business $business, Listing $listing, int $brokerUserId, ?User $user = null): Listing { if (! in_array($listing->status, ['submitted', 'under_review'], true)) { throw new \RuntimeException('تخصیص کارشناس فقط برای عرضه‌های ارسال‌شده یا در حال بررسی مجاز است.'); } return DB::transaction(function () use ($business, $listing, $brokerUserId, $user) { $listing->update([ 'assigned_broker_user_id' => $brokerUserId, 'status' => 'under_review', ]); $this->pipelineService->advanceStage($business, $listing, 'under_review', null, null, null, $user); $this->pipelineService->logEvent( $business, Listing::class, $listing->id, 'broker_assigned', 'کارشناس/واسط اختصاص داده شد', $user, ['broker_user_id' => $brokerUserId] ); return $listing->fresh(); }); } public function reject(Business $business, Listing $listing, ?string $reason = null, ?User $user = null): Listing { return DB::transaction(function () use ($business, $listing, $reason, $user) { $listing->update(['status' => 'rejected']); $this->pipelineService->advanceStage($business, $listing, 'rejected', null, null, null, $user); $this->pipelineService->logEvent( $business, Listing::class, $listing->id, 'listing_rejected', $reason ?: 'عرضه رد شد', $user ); return $listing->fresh(); }); } public function markAppraised(Business $business, Listing $listing, ?User $user = null): Listing { return DB::transaction(function () use ($business, $listing, $user) { $listing->update([ 'status' => 'appraised', 'appraised_at' => now(), ]); $this->pipelineService->advanceStage($business, $listing, 'appraised', null, null, null, $user); $this->pipelineService->logEvent( $business, Listing::class, $listing->id, 'listing_appraised', 'کارشناسی عرضه تکمیل شد', $user ); return $listing->fresh(); }); } public function listQuery(int $businessId, array $filters = []): Builder { $query = Listing::query() ->where('business_id', $businessId) ->with(['seller', 'broker', 'appraisals', 'deals']); if (! empty($filters['status'])) { $query->where('status', $filters['status']); } if (! empty($filters['listing_type'])) { $query->where('listing_type', $filters['listing_type']); } if (! empty($filters['scope_type'])) { $query->where('scope_type', $filters['scope_type']); } if (! empty($filters['seller_contact_id'])) { $query->where('seller_contact_id', (int) $filters['seller_contact_id']); } if (! empty($filters['assigned_broker_user_id'])) { $query->where('assigned_broker_user_id', (int) $filters['assigned_broker_user_id']); } if (! empty($filters['visibility'])) { $query->where('visibility', $filters['visibility']); } if (! empty($filters['search'])) { $term = '%'.$filters['search'].'%'; $query->where(function ($q) use ($term) { $q->where('title', 'like', $term) ->orWhere('listing_code', 'like', $term) ->orWhere('location_text', 'like', $term); }); } if (! empty($filters['submitted_from'])) { $query->whereDate('submitted_at', '>=', $this->aexUtil->parseInputDate($filters['submitted_from'])); } if (! empty($filters['submitted_to'])) { $query->whereDate('submitted_at', '<=', $this->aexUtil->parseInputDate($filters['submitted_to'])); } if (! empty($filters['has_ie_bom'])) { $query->whereNotNull('ie_bom_id'); } if (! empty($filters['has_equipment'])) { $query->whereNotNull('maintenance_equipment_id'); } $sortBy = $filters['sort_by'] ?? 'created_at'; $sortDir = $filters['sort_dir'] ?? 'desc'; $allowedSort = ['created_at', 'submitted_at', 'asking_price', 'title', 'status']; if (in_array($sortBy, $allowedSort, true)) { $query->orderBy($sortBy, $sortDir === 'asc' ? 'asc' : 'desc'); } return $query; } }