parfum/app/Services/StockActivityLogService.php
Yoga Pangestu dffc9f9e72 feat: add stock transfer management functionality
- Implemented stock transfer creation and management with Livewire components.
- Created StockTransfer and StockTransferItem models with necessary relationships.
- Added migration files for stock_transfers and stock_transfer_items tables.
- Developed traits for handling stock transfer items (add, delete, update).
- Integrated stock transfer logging in StockActivityLogService.
- Updated ViewServiceProvider to include stock transfer permissions and routes.
- Created frontend views for stock transfer form and index with appropriate UI components.
- Added role permissions for stock transfer actions in RolePermissionSeeder.
2026-01-09 21:41:49 +07:00

284 lines
8.9 KiB
PHP

<?php
namespace App\Services;
use App\Models\Order;
use App\Models\Outlet;
use App\Models\Purchase;
use App\Models\Restock;
use App\Models\StockOpname;
use App\Models\StockTransfer;
use App\Models\Warehouse;
use Illuminate\Database\Eloquent\Model;
class StockActivityLogService
{
public const LOG_PURCHASE = 'purchase';
public const LOG_DISTRIBUTION = 'distribution';
public const LOG_SALE = 'sale';
public const LOG_STOCK_OPNAME = 'stock_opname';
public const LOG_TRANSFER = 'transfer';
/**
* Log purchase activity (Incoming to Warehouse).
*/
public static function purchase(
Purchase $purchase,
Model $product,
int $quantity,
int $previousStock,
int $newStock,
?Model $causedBy = null
): void {
self::logWarehouse(
logName: self::LOG_PURCHASE,
event: 'increase',
description: "Purchase #{$purchase->hash_id}: Added {$quantity} stock to Warehouse.",
performedOn: $product,
warehouse: $purchase->warehouse,
quantityChange: $quantity,
previousStock: $previousStock,
newStock: $newStock,
extraProperties: [
'purchase_id' => $purchase->id,
'purchase_hash' => $purchase->hash_id,
],
causedBy: $causedBy
);
}
/**
* Log distribution activity (Warehouse to Outlet).
* This logs two events: Out from Warehouse, In to Outlet.
*/
public static function distribution(
Restock $restock,
Model $product,
int $quantity,
int $warehousePreviousStock,
int $warehouseNewStock,
int $outletPreviousStock,
int $outletNewStock,
?Model $causedBy = null
): void {
// Log Warehouse Out
self::logWarehouse(
logName: self::LOG_DISTRIBUTION,
event: 'decrease',
description: "Restock #{$restock->hash_id}: Moved {$quantity} stock from Warehouse to Outlet.",
performedOn: $product,
warehouse: $restock->warehouse,
quantityChange: -1 * abs($quantity),
previousStock: $warehousePreviousStock,
newStock: $warehouseNewStock,
extraProperties: [
'restock_id' => $restock->id,
'restock_hash' => $restock->hash_id,
'target_outlet_id' => $restock->outlet_id,
],
causedBy: $causedBy
);
// Log Outlet In
self::logOutlet(
logName: self::LOG_DISTRIBUTION,
event: 'increase',
description: "Restock #{$restock->hash_id}: Received {$quantity} stock from Warehouse.",
performedOn: $product,
outlet: $restock->outlet,
quantityChange: abs($quantity),
previousStock: $outletPreviousStock,
newStock: $outletNewStock,
extraProperties: [
'restock_id' => $restock->id,
'restock_hash' => $restock->hash_id,
'source_warehouse_id' => $restock->warehouse_id,
],
causedBy: $causedBy
);
}
/**
* Log sale activity (Outlet to Customer).
*/
public static function sale(
Order $order,
Model $product,
int $quantity,
int $previousStock,
int $newStock,
?Model $causedBy = null
): void {
self::logOutlet(
logName: self::LOG_SALE,
event: 'decrease',
description: "Order #{$order->hash_id}: Sold {$quantity} items.",
performedOn: $product,
outlet: $order->outlet,
quantityChange: -1 * abs($quantity),
previousStock: $previousStock,
newStock: $newStock,
extraProperties: [
'order_id' => $order->id,
'order_hash' => $order->hash_id,
],
causedBy: $causedBy ?? $order->user
);
}
/**
* Log stock opname activity (Adjustment in Outlet).
*/
public static function stockOpname(
StockOpname $stockOpname,
Model $product,
int $quantityChange,
int $previousStock,
int $newStock,
?string $note = null,
?Model $causedBy = null
): void {
$event = $quantityChange > 0 ? 'increase' : 'decrease';
self::logOutlet(
logName: self::LOG_STOCK_OPNAME,
event: $event,
description: "Stock Opname #{$stockOpname->hash_id}: Adjusted stock by {$quantityChange}.",
performedOn: $product,
outlet: $stockOpname->outlet,
quantityChange: $quantityChange,
previousStock: $previousStock,
newStock: $newStock,
extraProperties: [
'stock_opname_id' => $stockOpname->id,
'stock_opname_hash' => $stockOpname->hash_id,
'note' => $note,
],
causedBy: $causedBy
);
}
/**
* Log transfer activity (Outlet to Outlet).
* This logs two events: Out from Source Outlet, In to Destination Outlet.
*/
public static function transfer(
StockTransfer $stockTransfer,
Model $product,
int $quantity,
int $sourcePreviousStock,
int $sourceNewStock,
int $destinationPreviousStock,
int $destinationNewStock,
?Model $causedBy = null
): void {
// Log Source Outlet Out
self::logOutlet(
logName: self::LOG_TRANSFER,
event: 'decrease',
description: "Stock Transfer #{$stockTransfer->hash_id}: Moved {$quantity} stock from Source to Destination Outlet.",
performedOn: $product,
outlet: $stockTransfer->sourceOutlet,
quantityChange: -1 * abs($quantity),
previousStock: $sourcePreviousStock,
newStock: $sourceNewStock,
extraProperties: [
'stock_transfer_id' => $stockTransfer->id,
'stock_transfer_hash' => $stockTransfer->hash_id,
'destination_outlet_id' => $stockTransfer->destination_outlet_id,
],
causedBy: $causedBy
);
// Log Destination Outlet In
self::logOutlet(
logName: self::LOG_TRANSFER,
event: 'increase',
description: "Stock Transfer #{$stockTransfer->hash_id}: Received {$quantity} stock from Source Outlet.",
performedOn: $product,
outlet: $stockTransfer->destinationOutlet,
quantityChange: abs($quantity),
previousStock: $destinationPreviousStock,
newStock: $destinationNewStock,
extraProperties: [
'stock_transfer_id' => $stockTransfer->id,
'stock_transfer_hash' => $stockTransfer->hash_id,
'source_outlet_id' => $stockTransfer->source_outlet_id,
],
causedBy: $causedBy
);
}
/**
* Log stock related activity for Outlet.
*/
public static function logOutlet(
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,
'location_type' => 'outlet',
'location_name' => $outlet->name ?? 'Unknown Outlet',
], $extraProperties);
activity($logName)
->performedOn($performedOn)
->causedBy($causedBy)
->withProperties($properties)
->event($event)
->log($description);
}
/**
* Log stock related activity for Warehouse.
*/
public static function logWarehouse(
string $logName,
string $event,
string $description,
Model $performedOn,
Warehouse $warehouse,
int $quantityChange,
int $previousStock,
int $newStock,
array $extraProperties = [],
?Model $causedBy = null
): void {
$causedBy = $causedBy ?: auth()->user();
$properties = array_merge([
'warehouse_id' => $warehouse->getKey(),
'quantity_change' => $quantityChange,
'previous_stock' => $previousStock,
'new_stock' => $newStock,
'location_type' => 'warehouse',
'location_name' => $warehouse->name ?? 'Unknown Warehouse',
], $extraProperties);
activity($logName)
->performedOn($performedOn)
->causedBy($causedBy)
->withProperties($properties)
->event($event)
->log($description);
}
}