Files
GSB-Construction/Modules/BiddingManagement/app/Models/BidSubmission.php

90 lines
2.3 KiB
PHP

<?php
namespace Modules\BiddingManagement\Models;
use App\Traits\HasPublicIdentifier;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Modules\BiddingManagement\Enums\BidSubmissionStatus;
use Modules\DocumentManagement\Models\Document;
class BidSubmission extends Model
{
use HasPublicIdentifier;
protected $fillable = [
'bid_invitation_id', 'submitted_price', 'completion_days',
'validity_date', 'technical_notes', 'status', 'submitted_at',
];
protected function casts(): array
{
return [
'status' => BidSubmissionStatus::class,
'submitted_price' => 'decimal:2',
'completion_days' => 'integer',
'validity_date' => 'date',
'submitted_at' => 'datetime',
];
}
// --- Relationships ---
public function invitation(): BelongsTo
{
return $this->belongsTo(BidInvitation::class, 'bid_invitation_id');
}
public function scores(): HasMany
{
return $this->hasMany(BidScore::class);
}
public function documents(): MorphMany
{
return $this->morphMany(Document::class, 'documentable');
}
// --- State Machine ---
public function transitionTo(BidSubmissionStatus $newStatus): void
{
$current = $this->status;
if (!in_array($newStatus, $current->allowedTransitions())) {
throw new \InvalidArgumentException(
"Cannot transition from {$current->label()} to {$newStatus->label()}"
);
}
$this->update(['status' => $newStatus]);
}
// --- Computed ---
public function getComputedScoreAttribute(): float
{
$criteria = $this->invitation->package->criteria;
if ($criteria->isEmpty()) {
return 0.0;
}
$total = 0.0;
foreach ($this->scores as $score) {
$criterion = $criteria->firstWhere('id', $score->bid_criteria_id);
if ($criterion) {
$total += ($score->score / 100) * $criterion->weight;
}
}
return round($total, 2);
}
public function getContractorAttribute()
{
return $this->invitation->contractor;
}
}