create stock and stock logs
This commit is contained in:
@@ -10,17 +10,108 @@ class Opname extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = ['dealer_id','opname_date','user_id','note'];
|
||||
protected $fillable = [
|
||||
'dealer_id',
|
||||
'opname_date',
|
||||
'user_id',
|
||||
'note',
|
||||
'status',
|
||||
'approved_by',
|
||||
'approved_at',
|
||||
'rejection_note'
|
||||
];
|
||||
|
||||
public function dealer(){
|
||||
protected $casts = [
|
||||
'approved_at' => 'datetime'
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::updated(function ($opname) {
|
||||
// Jika status berubah menjadi approved
|
||||
if ($opname->isDirty('status') && $opname->status === 'approved') {
|
||||
// Update stock untuk setiap detail opname
|
||||
foreach ($opname->details as $detail) {
|
||||
$stock = Stock::firstOrCreate(
|
||||
[
|
||||
'product_id' => $detail->product_id,
|
||||
'dealer_id' => $opname->dealer_id
|
||||
],
|
||||
['quantity' => 0]
|
||||
);
|
||||
|
||||
// Update stock dengan physical_stock dari opname
|
||||
$stock->updateStock(
|
||||
$detail->physical_stock,
|
||||
$opname,
|
||||
"Stock adjustment from approved opname #{$opname->id}"
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function dealer()
|
||||
{
|
||||
return $this->belongsTo(Dealer::class);
|
||||
}
|
||||
|
||||
public function details(){
|
||||
public function details()
|
||||
{
|
||||
return $this->hasMany(OpnameDetail::class);
|
||||
}
|
||||
|
||||
public function user(){
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function approver()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'approved_by');
|
||||
}
|
||||
|
||||
// Method untuk approve opname
|
||||
public function approve(User $approver)
|
||||
{
|
||||
if ($this->status !== 'pending') {
|
||||
throw new \Exception('Only pending opnames can be approved');
|
||||
}
|
||||
|
||||
$this->status = 'approved';
|
||||
$this->approved_by = $approver->id;
|
||||
$this->approved_at = now();
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Method untuk reject opname
|
||||
public function reject(User $rejector, string $note)
|
||||
{
|
||||
if ($this->status !== 'pending') {
|
||||
throw new \Exception('Only pending opnames can be rejected');
|
||||
}
|
||||
|
||||
$this->status = 'rejected';
|
||||
$this->approved_by = $rejector->id;
|
||||
$this->approved_at = now();
|
||||
$this->rejection_note = $note;
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Method untuk submit opname untuk approval
|
||||
public function submit()
|
||||
{
|
||||
if ($this->status !== 'draft') {
|
||||
throw new \Exception('Only draft opnames can be submitted');
|
||||
}
|
||||
|
||||
$this->status = 'pending';
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,14 @@ class Product extends Model
|
||||
public function opnameDetails(){
|
||||
return $this->hasMany(OpnameDetail::class);
|
||||
}
|
||||
|
||||
public function stocks(){
|
||||
return $this->hasMany(Stock::class);
|
||||
}
|
||||
|
||||
// Helper method untuk mendapatkan total stock saat ini
|
||||
public function getCurrentTotalStockAttribute()
|
||||
{
|
||||
return $this->stocks()->sum('quantity');
|
||||
}
|
||||
}
|
||||
|
||||
56
app/Models/Stock.php
Normal file
56
app/Models/Stock.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Stock extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'product_id',
|
||||
'dealer_id',
|
||||
'quantity'
|
||||
];
|
||||
|
||||
public function product()
|
||||
{
|
||||
return $this->belongsTo(Product::class);
|
||||
}
|
||||
|
||||
public function dealer()
|
||||
{
|
||||
return $this->belongsTo(Dealer::class);
|
||||
}
|
||||
|
||||
public function stockLogs()
|
||||
{
|
||||
return $this->hasMany(StockLog::class);
|
||||
}
|
||||
|
||||
// Method untuk mengupdate stock
|
||||
public function updateStock($newQuantity, $source, $description = null)
|
||||
{
|
||||
$previousQuantity = $this->quantity;
|
||||
$quantityChange = $newQuantity - $previousQuantity;
|
||||
|
||||
$this->quantity = $newQuantity;
|
||||
$this->save();
|
||||
|
||||
// Buat log perubahan
|
||||
StockLog::create([
|
||||
'stock_id' => $this->id,
|
||||
'source_type' => get_class($source),
|
||||
'source_id' => $source->id,
|
||||
'previous_quantity' => $previousQuantity,
|
||||
'new_quantity' => $newQuantity,
|
||||
'quantity_change' => $quantityChange,
|
||||
'description' => $description,
|
||||
'user_id' => auth()->id()
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
70
app/Models/StockLog.php
Normal file
70
app/Models/StockLog.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\StockChangeType;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class StockLog extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'stock_id',
|
||||
'source_type',
|
||||
'source_id',
|
||||
'previous_quantity',
|
||||
'new_quantity',
|
||||
'quantity_change',
|
||||
'change_type',
|
||||
'description',
|
||||
'user_id'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'change_type' => StockChangeType::class,
|
||||
'previous_quantity' => 'decimal:2',
|
||||
'new_quantity' => 'decimal:2',
|
||||
'quantity_change' => 'decimal:2'
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function ($stockLog) {
|
||||
// Hitung quantity_change
|
||||
$stockLog->quantity_change = $stockLog->new_quantity - $stockLog->previous_quantity;
|
||||
|
||||
// Tentukan change_type berdasarkan quantity_change
|
||||
if ($stockLog->quantity_change == 0) {
|
||||
// Jika quantity sama persis (tanpa toleransi)
|
||||
$stockLog->change_type = StockChangeType::NO_CHANGE;
|
||||
} else if ($stockLog->quantity_change > 0) {
|
||||
$stockLog->change_type = StockChangeType::INCREASE;
|
||||
} else {
|
||||
$stockLog->change_type = StockChangeType::DECREASE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function stock()
|
||||
{
|
||||
return $this->belongsTo(Stock::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function source()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
// Helper method untuk mendapatkan label change_type
|
||||
public function getChangeTypeLabelAttribute()
|
||||
{
|
||||
return $this->change_type->label();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user