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; } }