chore: update document approval workflow and bug fixes
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\BiddingManagement\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Inertia\Inertia;
|
||||
use Modules\BiddingManagement\Enums\BidPackageStatus;
|
||||
use Modules\BiddingManagement\Events\BidPackageOpened;
|
||||
use Modules\BiddingManagement\Models\BidPackage;
|
||||
use Modules\ContractorManagement\Models\Contractor;
|
||||
use Modules\ProjectManagement\Models\Project;
|
||||
|
||||
class BidPackageController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = BidPackage::with(['project:id,name,code', 'creator:id,name'])
|
||||
->withCount(['invitations', 'submissions']);
|
||||
|
||||
if ($search = $request->search) {
|
||||
$query->where('title', 'like', "%{$search}%");
|
||||
}
|
||||
|
||||
if ($status = $request->status) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
if ($mode = $request->evaluation_mode) {
|
||||
$query->where('evaluation_mode', $mode);
|
||||
}
|
||||
|
||||
if ($projectUlid = $request->project) {
|
||||
$projectId = Project::resolveUlidToId($projectUlid);
|
||||
$query->where('project_id', $projectId);
|
||||
}
|
||||
|
||||
$packages = $query->latest()->paginate(15)->withQueryString();
|
||||
|
||||
return Inertia::render('BiddingManagement::Bids/Index', [
|
||||
'packages' => $packages,
|
||||
'filters' => $request->only(['search', 'status', 'evaluation_mode', 'project']),
|
||||
'projects' => Project::select('id', 'ulid', 'name', 'code')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return Inertia::render('BiddingManagement::Bids/Create', [
|
||||
'projects' => Project::select('id', 'ulid', 'name', 'code')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'project_id' => 'required|string',
|
||||
'title' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'evaluation_mode' => 'required|in:simple,scored',
|
||||
'submission_deadline' => 'nullable|date',
|
||||
'validity_period' => 'nullable|date',
|
||||
'instructions' => 'nullable|string',
|
||||
'criteria' => 'nullable|array',
|
||||
'criteria.*.name' => 'required_with:criteria|string|max:100',
|
||||
'criteria.*.weight' => 'required_with:criteria|numeric|min:0|max:100',
|
||||
'criteria.*.description' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$validated['project_id'] = Project::resolveUlidToId($validated['project_id']);
|
||||
$validated['created_by'] = Auth::id();
|
||||
|
||||
$criteria = $validated['criteria'] ?? [];
|
||||
unset($validated['criteria']);
|
||||
|
||||
if ($validated['evaluation_mode'] === 'scored' && count($criteria) > 0) {
|
||||
$totalWeight = array_sum(array_column($criteria, 'weight'));
|
||||
if ($totalWeight > 100) {
|
||||
return back()->withErrors(['criteria' => "Total criteria weight ({$totalWeight}%) exceeds 100%."]);
|
||||
}
|
||||
}
|
||||
|
||||
$package = BidPackage::create($validated);
|
||||
|
||||
foreach ($criteria as $index => $criterion) {
|
||||
$package->criteria()->create([
|
||||
'name' => $criterion['name'],
|
||||
'weight' => $criterion['weight'],
|
||||
'description' => $criterion['description'] ?? null,
|
||||
'sort_order' => $index,
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('bids.show', $package)->with('success', 'Bid package created.');
|
||||
}
|
||||
|
||||
public function show(BidPackage $bid)
|
||||
{
|
||||
$bid->load([
|
||||
'project:id,ulid,name,code',
|
||||
'creator:id,name',
|
||||
'criteria',
|
||||
'invitations.contractor:id,ulid,company_name,specialization,rating',
|
||||
'invitations.submission.scores',
|
||||
'invitations.submission.documents',
|
||||
'award.submission.invitation.contractor:id,company_name',
|
||||
'award.awardedBy:id,name',
|
||||
'documents',
|
||||
]);
|
||||
|
||||
$contractors = Contractor::where('status', 'active')
|
||||
->select('id', 'ulid', 'company_name', 'specialization', 'rating')
|
||||
->get();
|
||||
|
||||
return Inertia::render('BiddingManagement::Bids/Show', [
|
||||
'package' => $bid,
|
||||
'contractors' => $contractors,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(BidPackage $bid)
|
||||
{
|
||||
abort_unless($bid->status === BidPackageStatus::Draft, 403, 'Only draft packages can be edited.');
|
||||
|
||||
$bid->load('criteria');
|
||||
|
||||
return Inertia::render('BiddingManagement::Bids/Edit', [
|
||||
'package' => $bid,
|
||||
'projects' => Project::select('id', 'ulid', 'name', 'code')->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request, BidPackage $bid)
|
||||
{
|
||||
abort_unless($bid->status === BidPackageStatus::Draft, 403, 'Only draft packages can be edited.');
|
||||
|
||||
$validated = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'description' => 'nullable|string',
|
||||
'evaluation_mode' => 'required|in:simple,scored',
|
||||
'submission_deadline' => 'nullable|date',
|
||||
'validity_period' => 'nullable|date',
|
||||
'instructions' => 'nullable|string',
|
||||
'criteria' => 'nullable|array',
|
||||
'criteria.*.name' => 'required_with:criteria|string|max:100',
|
||||
'criteria.*.weight' => 'required_with:criteria|numeric|min:0|max:100',
|
||||
'criteria.*.description' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$criteria = $validated['criteria'] ?? [];
|
||||
unset($validated['criteria']);
|
||||
|
||||
$bid->update($validated);
|
||||
|
||||
// Replace criteria wholesale
|
||||
$bid->criteria()->delete();
|
||||
foreach ($criteria as $index => $criterion) {
|
||||
$bid->criteria()->create([
|
||||
'name' => $criterion['name'],
|
||||
'weight' => $criterion['weight'],
|
||||
'description' => $criterion['description'] ?? null,
|
||||
'sort_order' => $index,
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->route('bids.show', $bid)->with('success', 'Bid package updated.');
|
||||
}
|
||||
|
||||
public function destroy(BidPackage $bid)
|
||||
{
|
||||
abort_unless($bid->status === BidPackageStatus::Draft, 403, 'Only draft packages can be deleted.');
|
||||
|
||||
$bid->delete();
|
||||
|
||||
return redirect()->route('bids.index')->with('success', 'Bid package deleted.');
|
||||
}
|
||||
|
||||
public function publish(BidPackage $bid)
|
||||
{
|
||||
try {
|
||||
$bid->transitionTo(BidPackageStatus::Open);
|
||||
BidPackageOpened::dispatch($bid);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return back()->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
return back()->with('success', 'Bid package is now open for submissions.');
|
||||
}
|
||||
|
||||
public function startEvaluation(BidPackage $bid)
|
||||
{
|
||||
try {
|
||||
$bid->transitionTo(BidPackageStatus::Evaluating);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return back()->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
return back()->with('success', 'Evaluation phase started.');
|
||||
}
|
||||
|
||||
public function cancel(BidPackage $bid)
|
||||
{
|
||||
try {
|
||||
$bid->transitionTo(BidPackageStatus::Cancelled);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return back()->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
return back()->with('success', 'Bid package cancelled.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user