141 lines
5.1 KiB
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,
|
|
]);
|
|
}
|
|
}
|