130 lines
3.9 KiB
PHP
130 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Models\PartHistory;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
class StockMovement extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
protected $fillable = [
|
|
'part_id',
|
|
'supplier_id',
|
|
'purchase_order_id',
|
|
'service_order_id',
|
|
'movement_type',
|
|
'quantity',
|
|
'type', // Add this for compatibility
|
|
'reference_type',
|
|
'reference_id',
|
|
'unit_cost',
|
|
'total_cost',
|
|
'notes',
|
|
'created_by',
|
|
'user_id', // Add this for compatibility
|
|
];
|
|
|
|
protected $casts = [
|
|
'quantity' => 'integer',
|
|
'unit_cost' => 'decimal:2',
|
|
'total_cost' => 'decimal:2',
|
|
];
|
|
|
|
protected static function boot()
|
|
{
|
|
parent::boot();
|
|
|
|
static::created(function ($stockMovement) {
|
|
$part = $stockMovement->part;
|
|
|
|
// Get the quantity before this movement
|
|
$quantityBefore = $part->quantity_on_hand;
|
|
|
|
// Calculate quantity after based on movement type
|
|
$quantityChange = $stockMovement->movement_type === 'in' ?
|
|
$stockMovement->quantity : -$stockMovement->quantity;
|
|
$quantityAfter = $quantityBefore + $quantityChange;
|
|
|
|
// Log the stock movement in part history
|
|
PartHistory::logEvent(
|
|
$stockMovement->part_id,
|
|
$stockMovement->movement_type === 'in' ? PartHistory::EVENT_STOCK_IN :
|
|
($stockMovement->movement_type === 'out' ? PartHistory::EVENT_STOCK_OUT : PartHistory::EVENT_ADJUSTMENT),
|
|
[
|
|
'quantity_change' => $quantityChange,
|
|
'quantity_before' => $quantityBefore,
|
|
'quantity_after' => $quantityAfter,
|
|
'reference_type' => $stockMovement->reference_type,
|
|
'reference_id' => $stockMovement->reference_id,
|
|
'notes' => $stockMovement->notes ?? "Stock {$stockMovement->movement_type}",
|
|
]
|
|
);
|
|
});
|
|
}
|
|
|
|
// Handle both user_id and created_by for compatibility
|
|
public function setUserIdAttribute($value)
|
|
{
|
|
$this->attributes['created_by'] = $value;
|
|
}
|
|
|
|
const TYPE_IN = 'in';
|
|
const TYPE_OUT = 'out';
|
|
const TYPE_ADJUSTMENT = 'adjustment';
|
|
const TYPE_TRANSFER = 'transfer';
|
|
const TYPE_RETURN = 'return';
|
|
|
|
const REFERENCE_PURCHASE = 'purchase';
|
|
const REFERENCE_SALE = 'sale';
|
|
const REFERENCE_ADJUSTMENT = 'adjustment';
|
|
const REFERENCE_TRANSFER = 'transfer';
|
|
const REFERENCE_RETURN = 'return';
|
|
|
|
public function part(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Part::class);
|
|
}
|
|
|
|
public function supplier(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Supplier::class);
|
|
}
|
|
|
|
public function purchaseOrder(): BelongsTo
|
|
{
|
|
return $this->belongsTo(PurchaseOrder::class);
|
|
}
|
|
|
|
public function serviceOrder(): BelongsTo
|
|
{
|
|
return $this->belongsTo(ServiceOrder::class);
|
|
}
|
|
|
|
public function createdBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'created_by');
|
|
}
|
|
|
|
public function getMovementTypeColorAttribute(): string
|
|
{
|
|
return match($this->movement_type) {
|
|
self::TYPE_IN => 'text-green-600 dark:text-green-400',
|
|
self::TYPE_OUT => 'text-red-600 dark:text-red-400',
|
|
self::TYPE_ADJUSTMENT => 'text-blue-600 dark:text-blue-400',
|
|
self::TYPE_TRANSFER => 'text-purple-600 dark:text-purple-400',
|
|
self::TYPE_RETURN => 'text-orange-600 dark:text-orange-400',
|
|
default => 'text-gray-600 dark:text-gray-400',
|
|
};
|
|
}
|
|
|
|
public function getFormattedQuantityAttribute(): string
|
|
{
|
|
$prefix = $this->movement_type === self::TYPE_IN ? '+' : '-';
|
|
return $prefix . number_format($this->quantity);
|
|
}
|
|
}
|