143 lines
3.8 KiB
PHP
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');
|
|
}
|
|
}
|