$business->id, 'listing_id' => $listing->id, 'appraisal_id' => $data['appraisal_id'] ?? null, 'inquiry_code' => $data['inquiry_code'] ?? $this->aexUtil->generateInquiryCode($business->id), 'title' => $data['title'] ?? ('استعلام قیمت — '.$listing->title), 'status' => 'draft', 'inquiry_type' => $data['inquiry_type'] ?? 'mixed', 'notes' => $data['notes'] ?? null, 'due_date' => ! empty($data['due_date']) ? $this->aexUtil->parseInputDate($data['due_date']) : null, 'created_by' => $user?->id, ]); if (! empty($data['lines']) && is_array($data['lines'])) { $this->syncLines($business, $inquiry, $data['lines']); } elseif (! empty($data['from_appraisal']) && $inquiry->appraisal_id) { $this->importLinesFromAppraisal($business, $inquiry); } else { $this->addDefaultMachineLine($business, $inquiry, $listing); } if (! empty($data['supplier_contact_ids'])) { foreach ((array) $data['supplier_contact_ids'] as $contactId) { $this->addRecipient($business, $inquiry, 'supplier', (int) $contactId, null); } } if (! empty($data['internal_user_ids'])) { foreach ((array) $data['internal_user_ids'] as $userId) { $this->addRecipient($business, $inquiry, 'internal_user', null, (int) $userId); } } $this->pipelineService->logEvent( $business, PriceInquiry::class, $inquiry->id, 'price_inquiry_created', 'استعلام قیمت ایجاد شد', $user ); return $inquiry->fresh(['lines', 'recipients']); }); } public function send(PriceInquiry $inquiry, ?User $user = null): PriceInquiry { $inquiry->loadMissing('business', 'recipients', 'listing'); $inquiry->update(['status' => 'sent']); PriceInquiryRecipient::where('price_inquiry_id', $inquiry->id) ->where('status', 'pending') ->update(['sent_at' => now(), 'notified_at' => now()]); try { $this->notificationService->notifyInquiryRecipients($inquiry->business, $inquiry); } catch (\Throwable) { } $this->pipelineService->logEvent( $inquiry->business, PriceInquiry::class, $inquiry->id, 'price_inquiry_sent', 'استعلام قیمت برای گیرندگان ارسال شد', $user ); return $inquiry->fresh(['recipients']); } public function recordQuote(PriceInquiryRecipient $recipient, array $data, ?User $user = null): PriceInquiryRecipient { $recipient->loadMissing('inquiry.listing', 'contact'); $recipient->update([ 'status' => 'responded', 'quoted_price' => (int) ($data['quoted_price'] ?? 0), 'currency' => $data['currency'] ?? 'IRR', 'quote_notes' => $data['quote_notes'] ?? null, 'responded_at' => now(), ]); $inquiry = $recipient->inquiry; if ($inquiry) { $pending = $inquiry->recipients()->where('status', 'pending')->count(); $inquiry->update(['status' => $pending > 0 ? 'partial' : 'completed']); try { $inquiry->loadMissing('creator', 'listing.broker'); $notifiedUserIds = []; $supplierName = $recipient->contact?->name ?: 'تأمین‌کننده'; $message = $supplierName.' برای استعلام '.$inquiry->inquiry_code.' قیمت ثبت کرد.'; $business = Business::find($inquiry->business_id); if ($business && $inquiry->creator) { $this->notificationService->notify( $business, 'aex_supplier_quote_submitted', 'ثبت قیمت از سمت تأمین‌کننده', $message, $inquiry->creator, null, ['inquiry_id' => $inquiry->id, 'url' => action([\Modules\AssetExchange\Http\Controllers\PriceInquiryController::class, 'show'], [$inquiry->id])], 'high' ); $notifiedUserIds[] = $inquiry->creator->id; } $broker = $inquiry->listing?->broker; if ($business && $broker && ! in_array($broker->id, $notifiedUserIds, true)) { $this->notificationService->notify( $business, 'aex_supplier_quote_submitted', 'ثبت قیمت از سمت تأمین‌کننده', $message, $broker, null, ['inquiry_id' => $inquiry->id, 'url' => action([\Modules\AssetExchange\Http\Controllers\PriceInquiryController::class, 'show'], [$inquiry->id])], 'high' ); } } catch (\Throwable) { } } return $recipient->fresh(); } public function markRecipientViewed(PriceInquiryRecipient $recipient): PriceInquiryRecipient { if (! $recipient->viewed_at) { $recipient->update(['viewed_at' => now()]); } return $recipient->fresh(); } public function addRecipient(Business $business, PriceInquiry $inquiry, string $type, ?int $contactId, ?int $userId): PriceInquiryRecipient { return PriceInquiryRecipient::create([ 'business_id' => $business->id, 'price_inquiry_id' => $inquiry->id, 'recipient_type' => $type, 'contact_id' => $contactId, 'user_id' => $userId, 'status' => 'pending', ]); } protected function syncLines(Business $business, PriceInquiry $inquiry, array $lines): void { $sort = 0; foreach ($lines as $line) { if (empty($line['part_name']) && empty($line['description'])) { continue; } PriceInquiryLine::create([ 'business_id' => $business->id, 'price_inquiry_id' => $inquiry->id, 'line_kind' => $line['line_kind'] ?? 'part', 'part_name' => $line['part_name'] ?? null, 'description' => $line['description'] ?? null, 'quantity' => (float) ($line['quantity'] ?? 1), 'target_condition' => $line['target_condition'] ?? 'either', 'ie_item_master_id' => $line['ie_item_master_id'] ?? null, 'appraisal_line_id' => $line['appraisal_line_id'] ?? null, 'reference_price' => isset($line['reference_price']) ? (int) $line['reference_price'] : null, 'notes' => $line['notes'] ?? null, 'sort_order' => $sort++, ]); } } protected function importLinesFromAppraisal(Business $business, PriceInquiry $inquiry): void { $lines = AppraisalLine::where('appraisal_id', $inquiry->appraisal_id) ->whereIn('renovation_need', ['minor', 'major', 'replace']) ->orderBy('sort_order') ->get(); $sort = 0; foreach ($lines as $line) { PriceInquiryLine::create([ 'business_id' => $business->id, 'price_inquiry_id' => $inquiry->id, 'line_kind' => 'part', 'part_name' => $line->name, 'description' => $line->condition_notes, 'quantity' => 1, 'target_condition' => $line->renovation_need === 'replace' ? 'new' : 'either', 'appraisal_line_id' => $line->id, 'ie_item_master_id' => $line->ie_item_master_id, 'reference_price' => $line->replacement_cost ?: null, 'sort_order' => $sort++, ]); } } protected function addDefaultMachineLine(Business $business, PriceInquiry $inquiry, Listing $listing): void { PriceInquiryLine::create([ 'business_id' => $business->id, 'price_inquiry_id' => $inquiry->id, 'line_kind' => 'machine', 'part_name' => $listing->title, 'description' => trim(($listing->manufacturer ?? '').' '.($listing->model ?? '')), 'quantity' => 1, 'target_condition' => str_contains($listing->listing_type, 'new') ? 'new' : 'used', 'reference_price' => $listing->asking_price ?: null, 'sort_order' => 0, ]); } }