Files
GSB-Construction/Modules/BiddingManagement/app/Http/Controllers/BidSubmissionController.php

141 lines
5.1 KiB
PHP

<?php
namespace Modules\BiddingManagement\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Modules\ContractorManagement\Models\Contractor;
use Inertia\Inertia;
use Modules\BiddingManagement\Enums\BidPackageStatus;
use Modules\BiddingManagement\Enums\BidSubmissionStatus;
use Modules\BiddingManagement\Events\BidSubmitted;
use Modules\BiddingManagement\Models\BidInvitation;
use Modules\BiddingManagement\Models\BidSubmission;
class BidSubmissionController extends Controller
{
public function create(BidInvitation $invitation)
{
abort_unless($invitation->package->status === BidPackageStatus::Open, 403, 'Bid package is not open.');
abort_if($invitation->submission()->exists(), 422, 'You have already submitted a bid.');
$invitation->load('package.project:id,name,code', 'package.criteria');
return Inertia::render('BiddingManagement::Bids/Contractor/Submit', [
'invitation' => $invitation,
]);
}
public function store(Request $request, BidInvitation $invitation)
{
abort_unless($invitation->package->status === BidPackageStatus::Open, 403, 'Bid package is not open.');
abort_if($invitation->submission()->exists(), 422, 'You have already submitted a bid.');
$validated = $request->validate([
'submitted_price' => 'required|numeric|min:0',
'completion_days' => 'required|integer|min:1',
'validity_date' => 'nullable|date|after_or_equal:today',
'technical_notes' => 'nullable|string',
'documents' => 'nullable|array',
'documents.*' => 'file|max:20480|mimes:pdf,doc,docx,xls,xlsx,png,jpg,jpeg',
]);
// Accept invitation if still invited
if ($invitation->status === 'invited') {
$invitation->accept();
}
$documents = $validated['documents'] ?? [];
unset($validated['documents']);
$validated['bid_invitation_id'] = $invitation->id;
$validated['submitted_at'] = now();
$submission = BidSubmission::create($validated);
// Handle document uploads via DocumentManagement pattern
foreach ($documents as $file) {
$path = $file->store('bid-submissions', 'public');
$document = $submission->documents()->create([
'title' => $file->getClientOriginalName(),
'category' => 'bid_document',
'documentable_type' => BidSubmission::class,
'documentable_id' => $submission->id,
'uploaded_by' => Auth::id(),
'current_file_path' => $path,
'current_file_name' => $file->getClientOriginalName(),
'mime_type' => $file->getMimeType(),
'file_size' => $file->getSize(),
'version_count' => 1,
]);
}
BidSubmitted::dispatch($submission);
return redirect()
->route('bids.my')
->with('success', 'Your bid has been submitted successfully.');
}
public function show(BidSubmission $submission)
{
$submission->load([
'invitation.contractor:id,ulid,company_name,contact_person,email,specialization',
'invitation.package:id,ulid,title,evaluation_mode',
'invitation.package.criteria',
'scores.criteria',
'scores.evaluator:id,name',
'documents',
]);
return Inertia::render('BiddingManagement::Bids/Submission/Show', [
'submission' => $submission,
]);
}
public function shortlist(BidSubmission $submission)
{
try {
$submission->transitionTo(BidSubmissionStatus::Shortlisted);
} catch (\InvalidArgumentException $e) {
return back()->with('error', $e->getMessage());
}
return back()->with('success', 'Submission shortlisted.');
}
public function reject(BidSubmission $submission)
{
try {
$submission->transitionTo(BidSubmissionStatus::Rejected);
} catch (\InvalidArgumentException $e) {
return back()->with('error', $e->getMessage());
}
return back()->with('success', 'Submission rejected.');
}
public function myBids()
{
$user = Auth::user();
$contractor = Contractor::where('id', $user->contractor_id ?? null)->first();
abort_unless($contractor, 403, 'No contractor profile found for your account.');
$invitations = $contractor->bidInvitations()
->with([
'package:id,ulid,title,submission_deadline,status,evaluation_mode',
'package.project:id,name,code',
'submission:id,ulid,bid_invitation_id,submitted_price,completion_days,status,submitted_at',
])
->latest()
->get();
return Inertia::render('BiddingManagement::Bids/Contractor/MyBids', [
'invitations' => $invitations,
'contractor' => $contractor,
]);
}
}