sackey e839d40a99
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
Initial commit
2025-07-30 17:15:50 +00:00

143 lines
3.8 KiB
PHP

<?php
namespace App\Models;
use App\Traits\LogsPartHistory;
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;
class Part extends Model
{
/** @use HasFactory<\Database\Factories\PartFactory> */
use HasFactory, LogsPartHistory;
protected $fillable = [
'part_number',
'name',
'description',
'manufacturer',
'category',
'cost_price',
'sell_price',
'quantity_on_hand',
'minimum_stock_level',
'maximum_stock_level',
'location',
'supplier_id',
'supplier_part_number',
'lead_time_days',
'status',
'barcode',
'weight',
'dimensions',
'warranty_period',
'image',
];
protected $casts = [
'cost_price' => 'decimal:2',
'sell_price' => 'decimal:2',
'quantity_on_hand' => 'integer',
'minimum_stock_level' => 'integer',
'maximum_stock_level' => 'integer',
'lead_time_days' => 'integer',
'weight' => 'decimal:2',
];
public function serviceOrders(): BelongsToMany
{
return $this->belongsToMany(ServiceOrder::class, 'service_items')
->withPivot(['quantity', 'price', 'total'])
->withTimestamps();
}
public function supplier(): BelongsTo
{
return $this->belongsTo(Supplier::class);
}
public function stockMovements(): HasMany
{
return $this->hasMany(StockMovement::class);
}
public function purchaseOrderItems(): HasMany
{
return $this->hasMany(PurchaseOrderItem::class);
}
public function histories(): HasMany
{
return $this->hasMany(PartHistory::class);
}
public function isLowStock(): bool
{
return $this->quantity_on_hand <= $this->minimum_stock_level;
}
public function getMarkupPercentageAttribute()
{
if ($this->cost_price > 0) {
return round((($this->sell_price - $this->cost_price) / $this->cost_price) * 100, 2);
}
return 0;
}
public function getStockStatusAttribute(): string
{
if ($this->quantity_on_hand <= 0) {
return 'out_of_stock';
} elseif ($this->quantity_on_hand <= $this->minimum_stock_level) {
return 'low_stock';
} elseif ($this->quantity_on_hand >= $this->maximum_stock_level) {
return 'overstock';
}
return 'in_stock';
}
public function getStockStatusColorAttribute(): string
{
return match($this->stock_status) {
'out_of_stock' => 'text-red-600 dark:text-red-400',
'low_stock' => 'text-orange-600 dark:text-orange-400',
'overstock' => 'text-purple-600 dark:text-purple-400',
'in_stock' => 'text-green-600 dark:text-green-400',
default => 'text-gray-600 dark:text-gray-400',
};
}
public function getStockValueAttribute(): float
{
return $this->quantity_on_hand * $this->cost_price;
}
public function needsReorder(): bool
{
return $this->quantity_on_hand <= $this->minimum_stock_level;
}
public function scopeLowStock($query)
{
return $query->whereColumn('quantity_on_hand', '<=', 'minimum_stock_level');
}
public function scopeOutOfStock($query)
{
return $query->where('quantity_on_hand', '<=', 0);
}
public function scopeByCategory($query, $category)
{
return $query->where('category', $category);
}
public function scopeActive($query)
{
return $query->where('status', 'active');
}
}