store/app/Models/Cutting.php
Yoga Pangestu 15e2b7ec5b
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (8.3) (push) Waiting to run
tests / ci (8.4) (push) Waiting to run
tests / ci (8.5) (push) Waiting to run
feat: add material_result field to CuttingMaterial and CuttingMaterialCombination; update validation rules and service logic to handle new material result calculations; enhance frontend components for displaying material results in cutting management
2026-07-04 23:14:37 +07:00

136 lines
3.4 KiB
PHP

<?php
namespace App\Models;
use App\Enums\CuttingStatus;
use App\Models\Concerns\HasModuleMedia;
use App\Models\Concerns\HasRejection;
use App\Models\Concerns\InteractsWithActivityLog;
use Illuminate\Database\Eloquent\Attributes\Appends;
use Illuminate\Database\Eloquent\Attributes\Guarded;
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\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Validation\ValidationException;
use Spatie\MediaLibrary\HasMedia;
#[Guarded(['id'])]
#[Appends([
'created_at_formatted',
'status_label',
])]
class Cutting extends Model implements HasMedia
{
// 1. Use Trait
use HasFactory, HasModuleMedia, HasRejection, InteractsWithActivityLog, SoftDeletes;
// 2. Casting
protected function casts(): array
{
return [
'cost_per_unit' => 'integer',
'other_cost' => 'integer',
'sewing_cost' => 'integer',
'status' => CuttingStatus::class,
'total_material_cost' => 'integer',
];
}
// 3. Scope (grouped by column, then alphabetical)
// Column Group: status
#[Scope]
protected function completed(Builder $query): void
{
$query->where('status', CuttingStatus::COMPLETED);
}
#[Scope]
protected function inProgress(Builder $query): void
{
$query->where('status', CuttingStatus::IN_PROGRESS);
}
#[Scope]
protected function pendingVerification(Builder $query): void
{
$query->where('status', CuttingStatus::PENDING_VERIFICATION);
}
#[Scope]
protected function verified(Builder $query): void
{
$query->where('status', CuttingStatus::VERIFIED);
}
// 4. Attribute
public function createdAtFormatted(): Attribute
{
return Attribute::make(
get: fn () => $this->created_at?->translatedFormat('l, d F Y H:i'),
);
}
public function statusLabel(): Attribute
{
return Attribute::make(
get: fn () => $this->status->label(),
);
}
// 5. Other Methods
public static function mediaModuleName(): string
{
return 'cutting';
}
public function registerMediaCollections(): void
{
$this->addMediaCollection('images');
}
public function ensureEditable(): void
{
if (! $this->status->isEditable()) {
throw ValidationException::withMessages([
'status' => 'Proses cutting tidak dapat diubah.',
]);
}
}
// 6. Relation
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by_id');
}
public function materials(): HasMany
{
return $this->hasMany(CuttingMaterial::class);
}
public function combinations(): HasMany
{
return $this->hasMany(CuttingMaterialCombination::class);
}
public function resultPrices(): HasMany
{
return $this->hasMany(CuttingResultPrice::class);
}
public function results(): HasMany
{
return $this->hasMany(CuttingResult::class);
}
public function submittedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'submitted_by_id');
}
}