90 lines
2.3 KiB
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;
|
|
}
|
|
}
|