fix feature report stock product

This commit is contained in:
2025-07-08 14:24:01 +07:00
parent 956df5cfe6
commit cfef3775d7
11 changed files with 1196 additions and 16 deletions

View File

@@ -0,0 +1,178 @@
<?php
namespace App\Services;
use App\Models\Product;
use App\Models\Dealer;
use App\Models\Stock;
use App\Models\StockLog;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class StockReportService
{
/**
* Get stock report data for all products and dealers on a specific date
*/
public function getStockReportData($targetDate = null)
{
$targetDate = $targetDate ? Carbon::parse($targetDate) : now();
// Get all dealers
$dealers = Dealer::orderBy('name')->get();
// Get all active products
$products = Product::where('active', true)
->with(['category'])
->orderBy('name')
->get();
$data = [];
foreach ($products as $product) {
$row = [
'product_id' => $product->id,
'product_code' => $product->code,
'product_name' => $product->name,
'category_name' => $product->category ? $product->category->name : '-',
'unit' => $product->unit ?? '-',
'total_stock' => 0
];
// Calculate stock for each dealer on the target date
foreach ($dealers as $dealer) {
$stockOnDate = $this->getStockOnDate($product->id, $dealer->id, $targetDate);
$row["dealer_{$dealer->id}"] = $stockOnDate;
$row['total_stock'] += $stockOnDate;
}
$data[] = $row;
}
return [
'data' => $data,
'dealers' => $dealers
];
}
/**
* Get stock quantity for a specific product and dealer on a given date
*/
public function getStockOnDate($productId, $dealerId, $targetDate)
{
// Get the latest stock log entry before or on the target date
$latestStockLog = StockLog::whereHas('stock', function($query) use ($productId, $dealerId) {
$query->where('product_id', $productId)
->where('dealer_id', $dealerId);
})
->where('created_at', '<=', $targetDate->endOfDay())
->orderBy('created_at', 'desc')
->first();
if ($latestStockLog) {
// Return the new_quantity from the latest log entry
return $latestStockLog->new_quantity;
}
// If no stock log found, check if there's a current stock record
$currentStock = Stock::where('product_id', $productId)
->where('dealer_id', $dealerId)
->first();
if ($currentStock) {
// Check if the stock was created before or on the target date
if ($currentStock->created_at <= $targetDate) {
return $currentStock->quantity;
}
}
// No stock data available for this date
return 0;
}
/**
* Get optimized stock data using a single query approach
*/
public function getOptimizedStockReportData($targetDate = null)
{
$targetDate = $targetDate ? Carbon::parse($targetDate) : now();
// Get all dealers
$dealers = Dealer::orderBy('name')->get();
// Get all active products with their stock data
$products = Product::where('active', true)
->with(['category', 'stocks.dealer'])
->orderBy('name')
->get();
$data = [];
foreach ($products as $product) {
$row = [
'product_id' => $product->id,
'product_code' => $product->code,
'product_name' => $product->name,
'category_name' => $product->category ? $product->category->name : '-',
'unit' => $product->unit ?? '-',
'total_stock' => 0
];
// Calculate stock for each dealer on the target date
foreach ($dealers as $dealer) {
$stockOnDate = $this->getOptimizedStockOnDate($product->id, $dealer->id, $targetDate);
$row["dealer_{$dealer->id}"] = $stockOnDate;
$row['total_stock'] += $stockOnDate;
}
$data[] = $row;
}
return [
'data' => $data,
'dealers' => $dealers
];
}
/**
* Optimized method to get stock on date using subquery
*/
private function getOptimizedStockOnDate($productId, $dealerId, $targetDate)
{
try {
// Use a subquery to get the latest stock log entry efficiently
$latestStockLog = DB::table('stock_logs')
->join('stocks', 'stock_logs.stock_id', '=', 'stocks.id')
->where('stocks.product_id', $productId)
->where('stocks.dealer_id', $dealerId)
->where('stock_logs.created_at', '<=', $targetDate->endOfDay())
->orderBy('stock_logs.created_at', 'desc')
->select('stock_logs.new_quantity')
->first();
if ($latestStockLog) {
return $latestStockLog->new_quantity;
}
// If no stock log found, check current stock
$currentStock = Stock::where('product_id', $productId)
->where('dealer_id', $dealerId)
->first();
if ($currentStock && $currentStock->created_at <= $targetDate) {
return $currentStock->quantity;
}
return 0;
} catch (\Exception $e) {
// Log error and return 0
Log::error('Error getting stock on date: ' . $e->getMessage(), [
'product_id' => $productId,
'dealer_id' => $dealerId,
'target_date' => $targetDate
]);
return 0;
}
}
}