'boolean', 'unit' => RawMaterialUnit::class, ]; } // 3. Scope (grouped by column, then alphabetical) // Column Group: is_active #[Scope] protected function active(Builder $query): void { $query->where('is_active', true); } #[Scope] protected function inactive(Builder $query): void { $query->where('is_active', false); } // 4. Attribute public function unitAbbreviation(): Attribute { return Attribute::make( get: fn () => $this->unit->abbreviation(), ); } public function unitLabel(): Attribute { return Attribute::make( get: fn () => $this->unit->label(), ); } public function totalInventoryValueFormatted(): Attribute { return Attribute::make( get: fn () => $this->relationLoaded('prices') ? 'Rp '.number_format( $this->prices->sum(fn (RawMaterialPrice $price) => (float) $price->stock * (int) $price->price), 0, ',', '.', ) : '-', ); } public function totalStockFormatted(): Attribute { return Attribute::make( get: function () { if (! $this->relationLoaded('prices')) { return '-'; } $total = $this->prices->sum(fn (RawMaterialPrice $price) => (float) $price->stock); $formatted = rtrim(rtrim(number_format($total, 4, ',', '.'), '0'), ','); return "{$formatted} {$this->unit->abbreviation()}"; }, ); } // 5. Relation public function ownerVerificationRequests(): MorphMany { return $this->morphMany(OwnerVerificationRequest::class, 'subject'); } public function pendingOwnerVerificationRequest(): MorphOne { return $this->morphOne(OwnerVerificationRequest::class, 'subject') ->where('status', OwnerVerificationStatus::PENDING) ->latestOfMany(); } public function prices(): HasMany { return $this->hasMany(RawMaterialPrice::class)->orderBy('variant'); } }