197 lines
6.0 KiB
PHP
197 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Stock;
|
|
use App\Models\Work;
|
|
use App\Models\Transaction;
|
|
use App\Models\StockLog;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Exception;
|
|
|
|
class StockService
|
|
{
|
|
/**
|
|
* Check if dealer has sufficient stock for work
|
|
*
|
|
* @param int $workId
|
|
* @param int $dealerId
|
|
* @param int $workQuantity
|
|
* @return array
|
|
*/
|
|
public function checkStockAvailability($workId, $dealerId, $workQuantity = 1)
|
|
{
|
|
$work = Work::with('products')->find($workId);
|
|
|
|
if (!$work) {
|
|
return [
|
|
'available' => false,
|
|
'message' => 'Pekerjaan tidak ditemukan',
|
|
'details' => []
|
|
];
|
|
}
|
|
|
|
$stockDetails = [];
|
|
$allAvailable = true;
|
|
|
|
foreach ($work->products as $product) {
|
|
$requiredQuantity = $product->pivot->quantity_required * $workQuantity;
|
|
$availableStock = $product->getStockByDealer($dealerId);
|
|
|
|
$isAvailable = $availableStock >= $requiredQuantity;
|
|
if (!$isAvailable) {
|
|
$allAvailable = false;
|
|
}
|
|
|
|
$stockDetails[] = [
|
|
'product_id' => $product->id,
|
|
'product_name' => $product->name,
|
|
'required_quantity' => $requiredQuantity,
|
|
'available_stock' => $availableStock,
|
|
'is_available' => $isAvailable
|
|
];
|
|
}
|
|
|
|
return [
|
|
'available' => $allAvailable,
|
|
'message' => $allAvailable ? 'Stock tersedia' : 'Stock tidak mencukupi',
|
|
'details' => $stockDetails
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Reduce stock when work transaction is completed
|
|
*
|
|
* @param Transaction $transaction
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function reduceStockForTransaction(Transaction $transaction)
|
|
{
|
|
return DB::transaction(function () use ($transaction) {
|
|
$work = $transaction->work;
|
|
|
|
if (!$work) {
|
|
throw new Exception('Work not found for transaction');
|
|
}
|
|
|
|
$work->load('products');
|
|
|
|
if ($work->products->isEmpty()) {
|
|
// No products required for this work, return true
|
|
return true;
|
|
}
|
|
|
|
foreach ($work->products as $product) {
|
|
$requiredQuantity = $product->pivot->quantity_required * $transaction->qty;
|
|
|
|
$stock = Stock::where('product_id', $product->id)
|
|
->where('dealer_id', $transaction->dealer_id)
|
|
->first();
|
|
|
|
if (!$stock) {
|
|
throw new Exception("Stock not found for product {$product->name} at dealer");
|
|
}
|
|
|
|
if ($stock->quantity < $requiredQuantity) {
|
|
throw new Exception("Insufficient stock for product {$product->name}. Required: {$requiredQuantity}, Available: {$stock->quantity}");
|
|
}
|
|
|
|
// Reduce stock
|
|
$newQuantity = $stock->quantity - $requiredQuantity;
|
|
$stock->updateStock(
|
|
$newQuantity,
|
|
$transaction,
|
|
"Stock reduced for work: {$work->name} (Transaction #{$transaction->id})"
|
|
);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Restore stock when work transaction is cancelled/reversed
|
|
*
|
|
* @param Transaction $transaction
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function restoreStockForTransaction(Transaction $transaction)
|
|
{
|
|
return DB::transaction(function () use ($transaction) {
|
|
$work = $transaction->work;
|
|
|
|
if (!$work) {
|
|
throw new Exception('Work not found for transaction');
|
|
}
|
|
|
|
$work->load('products');
|
|
|
|
if ($work->products->isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
foreach ($work->products as $product) {
|
|
$restoreQuantity = $product->pivot->quantity_required * $transaction->qty;
|
|
|
|
$stock = Stock::where('product_id', $product->id)
|
|
->where('dealer_id', $transaction->dealer_id)
|
|
->first();
|
|
|
|
if (!$stock) {
|
|
// Create new stock record if doesn't exist
|
|
$stock = Stock::create([
|
|
'product_id' => $product->id,
|
|
'dealer_id' => $transaction->dealer_id,
|
|
'quantity' => 0
|
|
]);
|
|
}
|
|
|
|
// Restore stock
|
|
$newQuantity = $stock->quantity + $restoreQuantity;
|
|
$stock->updateStock(
|
|
$newQuantity,
|
|
$transaction,
|
|
"Stock restored from cancelled work: {$work->name} (Transaction #{$transaction->id})"
|
|
);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get stock usage prediction for a work
|
|
*
|
|
* @param int $workId
|
|
* @param int $quantity
|
|
* @return array
|
|
*/
|
|
public function getStockUsagePrediction($workId, $quantity = 1)
|
|
{
|
|
$work = Work::with('products')->find($workId);
|
|
|
|
if (!$work) {
|
|
return [];
|
|
}
|
|
|
|
$predictions = [];
|
|
|
|
foreach ($work->products as $product) {
|
|
$totalRequired = $product->pivot->quantity_required * $quantity;
|
|
|
|
$predictions[] = [
|
|
'product_id' => $product->id,
|
|
'product_name' => $product->name,
|
|
'product_code' => $product->code,
|
|
'unit' => $product->unit,
|
|
'quantity_per_work' => $product->pivot->quantity_required,
|
|
'total_quantity_needed' => $totalRequired,
|
|
'notes' => $product->pivot->notes
|
|
];
|
|
}
|
|
|
|
return $predictions;
|
|
}
|
|
}
|