202 lines
6.3 KiB
PHP
202 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Traits\Uuid;
|
|
use Illuminate\Database\Eloquent\Attributes\Scope;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
|
|
class Contract extends Model
|
|
{
|
|
/** @use HasFactory<\Database\Factories\ContractFactory> */
|
|
use HasFactory;
|
|
|
|
use SoftDeletes;
|
|
use Uuid;
|
|
|
|
protected $fillable = [
|
|
'reference',
|
|
'start_date',
|
|
'end_date',
|
|
'client_case_id',
|
|
'type_id',
|
|
'description',
|
|
'meta',
|
|
];
|
|
|
|
protected $hidden = [
|
|
'id',
|
|
'client_case_id',
|
|
'type_id',
|
|
];
|
|
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'meta' => 'array',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Normalize start_date inputs to Y-m-d (or null) on assignment.
|
|
*/
|
|
protected function startDate(): Attribute
|
|
{
|
|
return Attribute::make(
|
|
set: function ($value) {
|
|
if (is_null($value)) {
|
|
return null;
|
|
}
|
|
$str = is_string($value) ? $value : (string) $value;
|
|
|
|
return \App\Services\DateNormalizer::toDate($str);
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Normalize end_date inputs to Y-m-d (or null) on assignment.
|
|
*/
|
|
protected function endDate(): Attribute
|
|
{
|
|
return Attribute::make(
|
|
set: function ($value) {
|
|
if (is_null($value)) {
|
|
return null;
|
|
}
|
|
$str = is_string($value) ? $value : (string) $value;
|
|
|
|
return \App\Services\DateNormalizer::toDate($str);
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Scope contracts to those in a specific segment with active pivot.
|
|
*/
|
|
#[Scope]
|
|
public function scopeForSegment(Builder $query, int $segmentId): Builder
|
|
{
|
|
return $query->whereExists(function ($q) use ($segmentId) {
|
|
$q->from('contract_segment')
|
|
->whereColumn('contract_segment.contract_id', 'contracts.id')
|
|
->where('contract_segment.segment_id', $segmentId)
|
|
->where('contract_segment.active', true);
|
|
});
|
|
}
|
|
|
|
public function type(): BelongsTo
|
|
{
|
|
return $this->belongsTo(\App\Models\ContractType::class, 'type_id');
|
|
}
|
|
|
|
public function clientCase(): BelongsTo
|
|
{
|
|
return $this->belongsTo(\App\Models\ClientCase::class)
|
|
->with(['person']);
|
|
}
|
|
|
|
public function segments(): BelongsToMany
|
|
{
|
|
return $this->belongsToMany(\App\Models\Segment::class)
|
|
->withPivot('active', 'created_at')
|
|
->wherePivot('active', true);
|
|
}
|
|
|
|
public function account(): HasOne
|
|
{
|
|
// Use latestOfMany to always surface newest account snapshot if multiple exist.
|
|
return $this->hasOne(\App\Models\Account::class)
|
|
->latestOfMany()
|
|
->with('type');
|
|
}
|
|
|
|
public function objects(): HasMany
|
|
{
|
|
return $this->hasMany(\App\Models\CaseObject::class, 'contract_id');
|
|
}
|
|
|
|
public function documents(): MorphMany
|
|
{
|
|
return $this->morphMany(\App\Models\Document::class, 'documentable');
|
|
}
|
|
|
|
protected static function booted(): void
|
|
{
|
|
static::created(function (Contract $contract): void {
|
|
// Only apply configs when type and client case are set and no segments are already attached
|
|
if (empty($contract->type_id) || empty($contract->client_case_id)) {
|
|
return;
|
|
}
|
|
|
|
$existing = \DB::table('contract_segment')
|
|
->where('contract_id', $contract->id)
|
|
->count();
|
|
if ($existing > 0) {
|
|
// Respect pre-attached segments (e.g. custom import logic)
|
|
return;
|
|
}
|
|
|
|
$configs = ContractConfig::query()
|
|
->where('contract_type_id', $contract->type_id)
|
|
->where('active', true)
|
|
->get(['segment_id', 'is_initial']);
|
|
|
|
if ($configs->isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
foreach ($configs as $cfg) {
|
|
// Ensure the segment is attached to the client case and active
|
|
$attached = \DB::table('client_case_segment')
|
|
->where('client_case_id', $contract->client_case_id)
|
|
->where('segment_id', $cfg->segment_id)
|
|
->first();
|
|
if (! $attached) {
|
|
\DB::table('client_case_segment')->insert([
|
|
'client_case_id' => $contract->client_case_id,
|
|
'segment_id' => $cfg->segment_id,
|
|
'active' => true,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
} elseif (! $attached->active) {
|
|
\DB::table('client_case_segment')
|
|
->where('id', $attached->id)
|
|
->update(['active' => true, 'updated_at' => now()]);
|
|
}
|
|
|
|
// Attach to contract; mark active if initial, otherwise inactive
|
|
$pivot = \DB::table('contract_segment')
|
|
->where('contract_id', $contract->id)
|
|
->where('segment_id', $cfg->segment_id)
|
|
->first();
|
|
|
|
$activeFlag = $cfg->is_initial ? true : false;
|
|
if ($pivot) {
|
|
\DB::table('contract_segment')
|
|
->where('id', $pivot->id)
|
|
->update(['active' => $activeFlag, 'updated_at' => now()]);
|
|
} else {
|
|
\DB::table('contract_segment')->insert([
|
|
'contract_id' => $contract->id,
|
|
'segment_id' => $cfg->segment_id,
|
|
'active' => $activeFlag,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|