feat: Implement StockActivityLogService and refactor stock activity logging across stock opname, order, and purchase modules to utilize it.

This commit is contained in:
Yoga Pangestu 2025-12-15 21:09:41 +07:00
parent 8ccca5b7cc
commit 96ed2c3ac4
4 changed files with 108 additions and 60 deletions

View File

@ -4,6 +4,7 @@
use App\Enums\StockOpnameStatus; use App\Enums\StockOpnameStatus;
use App\Models\StockOpname; use App\Models\StockOpname;
use App\Services\StockActivityLogService;
use App\Traits\Components\WithConfirmation; use App\Traits\Components\WithConfirmation;
use App\Traits\Components\WithToast; use App\Traits\Components\WithToast;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
@ -90,7 +91,7 @@ public function approveApproval(StockOpname $stockOpname): void
$relationName = $relationMap[$morphType]; $relationName = $relationMap[$morphType];
$currentStock = $outlet->$relationName() $currentStock = $outlet->$relationName()
->where($itemable->getTable().'.id', $itemable->id) ->where($itemable->getTable() . '.id', $itemable->id)
->first()?->pivot?->stock ?? 0; ->first()?->pivot?->stock ?? 0;
$quantity = $item->qty_physical; $quantity = $item->qty_physical;
@ -99,26 +100,27 @@ public function approveApproval(StockOpname $stockOpname): void
$itemable->id => ['stock' => $quantity], $itemable->id => ['stock' => $quantity],
]); ]);
activity('Stock Opname') StockActivityLogService::log(
->performedOn($itemable) logName: 'Stock Opname',
->causedBy(auth()->user()) event: 'Mengubah',
->withProperties([ description: sprintf(
'itemable_id' => $item->itemable_id,
'itemable_type' => $item->itemable_type,
'outlet_id' => $outlet->id,
'quantity_change' => $quantity,
'previous_stock' => $currentStock,
'new_stock' => $currentStock + $quantity,
'itemable_id' => $item->id ?? null,
])
->event('Mengubah')
->log(sprintf(
'Stok %s di %s diperbarui dari %s menjadi %s', 'Stok %s di %s diperbarui dari %s menjadi %s',
$itemable->name, $itemable->name,
$outlet->name, $outlet->name,
$currentStock, $currentStock,
$quantity $quantity
)); ),
performedOn: $itemable,
outlet: $outlet,
quantityChange: $quantity - $currentStock,
previousStock: $currentStock,
newStock: $quantity,
extraProperties: [
// Preserving 'itemable_id' pointing to StockOpnameItem id as per original code overwriting
'itemable_id' => $item->id ?? null,
'itemable_type' => $item->itemable_type,
]
);
} }
}); });

View File

@ -0,0 +1,40 @@
<?php
namespace App\Services;
use Illuminate\Database\Eloquent\Model;
class StockActivityLogService
{
/**
* Log stock related activity.
*/
public static function log(
string $logName,
string $event,
string $description,
Model $performedOn,
Model $outlet,
int $quantityChange,
int $previousStock,
int $newStock,
array $extraProperties = [],
?Model $causedBy = null
): void {
$causedBy = $causedBy ?: auth()->user();
$properties = array_merge([
'outlet_id' => $outlet->getKey(),
'quantity_change' => $quantityChange,
'previous_stock' => $previousStock,
'new_stock' => $newStock,
], $extraProperties);
activity($logName)
->performedOn($performedOn)
->causedBy($causedBy)
->withProperties($properties)
->event($event)
->log($description);
}
}

View File

@ -5,6 +5,7 @@
use App\Models\Bottle; use App\Models\Bottle;
use App\Models\Perfume; use App\Models\Perfume;
use App\Models\Product; use App\Models\Product;
use App\Services\StockActivityLogService;
trait WithUpdateStock trait WithUpdateStock
{ {
@ -39,20 +40,21 @@ public function increaseOutletStock($outlet, $item): void
]); ]);
} }
activity('Order') StockActivityLogService::log(
->performedOn($item->orderable) logName: 'Order',
->causedBy(auth()->user()) event: 'Menambah',
->withProperties([ description: 'Stok ' . $item->orderable->name . ' di ' . $outlet->name . ' bertambah ' . formatCurrencyNumber($quantity),
performedOn: $item->orderable,
outlet: $outlet,
quantityChange: $quantity,
previousStock: $currentStock,
newStock: $currentStock + $quantity,
extraProperties: [
'orderable_id' => $item->orderable_id, 'orderable_id' => $item->orderable_id,
'orderable_type' => $item->orderable_type, 'orderable_type' => $item->orderable_type,
'outlet_id' => $outlet->id,
'quantity_change' => $quantity,
'previous_stock' => $currentStock,
'new_stock' => $currentStock + $quantity,
'purchase_id' => $item->purchase_id ?? null, 'purchase_id' => $item->purchase_id ?? null,
]) ]
->event('Menambah') );
->log('Stok '.$item->orderable->name.' di '.$outlet->name.' bertambah '.formatCurrencyNumber($quantity));
} }
public function decreaseOutletStock($outlet, $item) public function decreaseOutletStock($outlet, $item)
@ -95,19 +97,20 @@ public function decreaseOutletStock($outlet, $item)
'stock' => $newStock, 'stock' => $newStock,
]); ]);
activity('Order') StockActivityLogService::log(
->performedOn($item->orderable) logName: 'Order',
->causedBy(auth()->user()) event: 'Mengurangi',
->withProperties([ description: 'Stok ' . $item->orderable->name . ' di ' . $outlet->name . ' berkurang ' . formatCurrencyNumber($quantity),
performedOn: $item->orderable,
outlet: $outlet,
quantityChange: $quantity,
previousStock: $currentStock,
newStock: $currentStock - $quantity,
extraProperties: [
'orderable_id' => $item->orderable_id, 'orderable_id' => $item->orderable_id,
'orderable_type' => $item->orderable_type, 'orderable_type' => $item->orderable_type,
'outlet_id' => $outlet->id,
'quantity_change' => $quantity,
'previous_stock' => $currentStock,
'new_stock' => $currentStock - $quantity,
'purchase_id' => $item->purchase_id ?? null, 'purchase_id' => $item->purchase_id ?? null,
]) ]
->event('Mengurangi') );
->log('Stok '.$item->orderable->name.' di '.$outlet->name.' berkurang '.formatCurrencyNumber($quantity));
} }
} }

View File

@ -5,6 +5,7 @@
use App\Models\Bottle; use App\Models\Bottle;
use App\Models\Perfume; use App\Models\Perfume;
use App\Models\Product; use App\Models\Product;
use App\Services\StockActivityLogService;
trait WithUpdateStock trait WithUpdateStock
{ {
@ -40,20 +41,21 @@ public function increaseOutletStock($outlet, $item): void
]); ]);
} }
activity('Belanja') StockActivityLogService::log(
->performedOn($item->purchasable) logName: 'Belanja',
->causedBy(auth()->user()) event: 'Menambah',
->withProperties([ description: 'Stok ' . $item->purchasable->name . ' di ' . $outlet->name . ' bertambah ' . formatCurrencyNumber($quantity),
performedOn: $item->purchasable,
outlet: $outlet,
quantityChange: $quantity,
previousStock: $previousStock,
newStock: $previousStock + $quantity,
extraProperties: [
'purchasable_id' => $item->purchasable_id, 'purchasable_id' => $item->purchasable_id,
'purchasable_type' => $item->purchasable_type, 'purchasable_type' => $item->purchasable_type,
'outlet_id' => $outlet->id,
'quantity_change' => $quantity,
'previous_stock' => $previousStock,
'new_stock' => $previousStock + $quantity,
'purchase_id' => $item->purchase_id ?? null, 'purchase_id' => $item->purchase_id ?? null,
]) ]
->event('Menambah') );
->log('Stok '.$item->purchasable->name.' di '.$outlet->name.' bertambah '.formatCurrencyNumber($quantity));
} }
public function decreaseOutletStock($outlet, $item): void public function decreaseOutletStock($outlet, $item): void
@ -84,19 +86,20 @@ public function decreaseOutletStock($outlet, $item): void
]); ]);
} }
activity('Belanja') StockActivityLogService::log(
->performedOn($item->purchasable) logName: 'Belanja',
->causedBy(auth()->user()) event: 'Mengurangi',
->withProperties([ description: 'Stok ' . $item->purchasable->name . ' di ' . $outlet->name . ' berkurang ' . $quantity,
performedOn: $item->purchasable,
outlet: $outlet,
quantityChange: $quantity,
previousStock: $currentStock,
newStock: $newStock,
extraProperties: [
'purchasable_id' => $item->purchasable_id, 'purchasable_id' => $item->purchasable_id,
'purchasable_type' => $item->purchasable_type, 'purchasable_type' => $item->purchasable_type,
'outlet_id' => $outlet->id,
'quantity_change' => $quantity,
'previous_stock' => $currentStock,
'new_stock' => $newStock,
'purchase_id' => $item->purchase_id, 'purchase_id' => $item->purchase_id,
]) ]
->event('Mengurangi') );
->log('Stok '.$item->purchasable->name.' di '.$outlet->name.' berkurang '.$quantity);
} }
} }