status, [BidPackageStatus::Draft, BidPackageStatus::Open]), 403, 'Invitations can only be added to draft or open packages.' ); $validated = $request->validate([ 'contractor_ids' => 'required|array|min:1', 'contractor_ids.*' => 'required|string', ]); $added = 0; foreach ($validated['contractor_ids'] as $ulid) { $contractorId = Contractor::resolveUlidToId($ulid); if (!$contractorId) { continue; } // Skip duplicates $exists = $bid->invitations()->where('contractor_id', $contractorId)->exists(); if ($exists) { continue; } $bid->invitations()->create([ 'contractor_id' => $contractorId, 'status' => 'invited', 'invited_at' => now(), ]); $added++; } return back()->with('success', "{$added} contractor(s) invited."); } public function destroy(BidInvitation $invitation) { abort_unless( in_array($invitation->package->status, [BidPackageStatus::Draft, BidPackageStatus::Open]), 403, 'Cannot remove invitations at this stage.' ); abort_if($invitation->submission()->exists(), 422, 'Cannot remove an invitation that has a submission.'); $invitation->delete(); return back()->with('success', 'Invitation removed.'); } public function accept(BidInvitation $invitation) { abort_unless($invitation->package->status === BidPackageStatus::Open, 403, 'Bid package is not open.'); abort_if($invitation->status === 'declined', 422, 'You have already declined this invitation.'); $invitation->accept(); return back()->with('success', 'You have accepted the bid invitation.'); } public function decline(Request $request, BidInvitation $invitation) { abort_unless($invitation->package->status === BidPackageStatus::Open, 403, 'Bid package is not open.'); $validated = $request->validate([ 'reason' => 'nullable|string|max:500', ]); $invitation->decline($validated['reason'] ?? null); return back()->with('success', 'You have declined the bid invitation.'); } }