Feat: Stat stock+
This commit is contained in:
parent
050a38c6bd
commit
39c21d3d09
@ -6,6 +6,7 @@ namespace App\Repositories;
|
||||
|
||||
use App\Models\Product;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ProductRepository extends BaseRepository implements ProductRepositoryInterface
|
||||
{
|
||||
@ -132,15 +133,148 @@ class ProductRepository extends BaseRepository implements ProductRepositoryInter
|
||||
$expiringProducts = $this->model->where('date_expiration', '<=', now()->addDays(30)->toDateString())
|
||||
->where('date_expiration', '>=', now()->toDateString())
|
||||
->count();
|
||||
$totalValue = $this->model->sum(\DB::raw('stock_actuel * prix_unitaire'));
|
||||
|
||||
$stockItems = DB::table('stock_items')
|
||||
->join('products', 'stock_items.product_id', '=', 'products.id');
|
||||
|
||||
$totalValue = $stockItems
|
||||
->selectRaw('COALESCE(SUM(stock_items.qty_on_hand_base * products.prix_unitaire), 0) as total_value')
|
||||
->value('total_value');
|
||||
|
||||
$stockAlertCount = $stockItems
|
||||
->whereRaw('stock_items.qty_on_hand_base <= stock_items.safety_stock_base')
|
||||
->count();
|
||||
|
||||
$stockRotation = $this->getRotationByProduct();
|
||||
$warehouseMovements = $this->getWarehouseMovements();
|
||||
$stockByWarehouse = $this->getStockByWarehouse();
|
||||
|
||||
return [
|
||||
'total_products' => $totalProducts,
|
||||
'low_stock_products' => $lowStockProducts,
|
||||
'expiring_products' => $expiringProducts,
|
||||
'total_value' => $totalValue,
|
||||
'stock_alerts_count' => $stockAlertCount,
|
||||
'stock_rotation_by_product' => $stockRotation,
|
||||
'warehouse_movements' => $warehouseMovements,
|
||||
'stock_by_warehouse' => $stockByWarehouse,
|
||||
];
|
||||
}
|
||||
|
||||
private function getRotationByProduct(): array
|
||||
{
|
||||
$oneYearAgo = now()->subYear();
|
||||
|
||||
$movementSubquery = DB::table('stock_moves')
|
||||
->selectRaw('product_id, SUM(qty_base) as moved_qty')
|
||||
->where('moved_at', '>=', $oneYearAgo)
|
||||
->groupBy('product_id');
|
||||
|
||||
$stockOnHandSubquery = DB::table('stock_items')
|
||||
->selectRaw('product_id, SUM(qty_on_hand_base) as qty_on_hand')
|
||||
->groupBy('product_id');
|
||||
|
||||
$rotation = DB::table('products')
|
||||
->leftJoinSub($movementSubquery, 'movement', function ($join) {
|
||||
$join->on('products.id', '=', 'movement.product_id');
|
||||
})
|
||||
->leftJoinSub($stockOnHandSubquery, 'stock', function ($join) {
|
||||
$join->on('products.id', '=', 'stock.product_id');
|
||||
})
|
||||
->whereNotNull('movement.moved_qty')
|
||||
->selectRaw(
|
||||
'products.id as product_id, products.nom as product_name, ' .
|
||||
'movement.moved_qty, COALESCE(stock.qty_on_hand, 0) as qty_on_hand, ' .
|
||||
'CASE WHEN COALESCE(stock.qty_on_hand, 0) > 0 THEN ROUND(movement.moved_qty / stock.qty_on_hand, 2) ELSE null END as rotation_rate'
|
||||
)
|
||||
->orderByDesc('rotation_rate')
|
||||
->limit(10)
|
||||
->get()
|
||||
->map(function ($row) {
|
||||
return [
|
||||
'product_id' => (int) $row->product_id,
|
||||
'product_name' => $row->product_name,
|
||||
'moved_qty_last_12_months' => (float) $row->moved_qty,
|
||||
'qty_on_hand' => (float) $row->qty_on_hand,
|
||||
'rotation_rate' => $row->rotation_rate !== null ? (float) $row->rotation_rate : null,
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
return $rotation;
|
||||
}
|
||||
|
||||
private function getWarehouseMovements(): array
|
||||
{
|
||||
$incoming = DB::table('stock_moves')
|
||||
->selectRaw('to_warehouse_id as warehouse_id, COUNT(*) as incoming_moves, SUM(qty_base) as incoming_qty')
|
||||
->whereNotNull('to_warehouse_id')
|
||||
->groupBy('to_warehouse_id');
|
||||
|
||||
$outgoing = DB::table('stock_moves')
|
||||
->selectRaw('from_warehouse_id as warehouse_id, COUNT(*) as outgoing_moves, SUM(qty_base) as outgoing_qty')
|
||||
->whereNotNull('from_warehouse_id')
|
||||
->groupBy('from_warehouse_id');
|
||||
|
||||
$warehouses = DB::table('warehouses')
|
||||
->leftJoinSub($incoming, 'incoming', function ($join) {
|
||||
$join->on('warehouses.id', '=', 'incoming.warehouse_id');
|
||||
})
|
||||
->leftJoinSub($outgoing, 'outgoing', function ($join) {
|
||||
$join->on('warehouses.id', '=', 'outgoing.warehouse_id');
|
||||
})
|
||||
->select([
|
||||
'warehouses.id as warehouse_id',
|
||||
'warehouses.name as warehouse_name',
|
||||
DB::raw('COALESCE(incoming.incoming_moves, 0) as incoming_moves'),
|
||||
DB::raw('COALESCE(incoming.incoming_qty, 0) as incoming_qty'),
|
||||
DB::raw('COALESCE(outgoing.outgoing_moves, 0) as outgoing_moves'),
|
||||
DB::raw('COALESCE(outgoing.outgoing_qty, 0) as outgoing_qty'),
|
||||
DB::raw('COALESCE(COALESCE(incoming.incoming_moves, 0) + COALESCE(outgoing.outgoing_moves, 0), 0) as total_moves'),
|
||||
])
|
||||
->get()
|
||||
->map(function ($row) {
|
||||
return [
|
||||
'warehouse_id' => (int) $row->warehouse_id,
|
||||
'warehouse_name' => $row->warehouse_name,
|
||||
'incoming_moves' => (int) $row->incoming_moves,
|
||||
'incoming_qty' => (float) $row->incoming_qty,
|
||||
'outgoing_moves' => (int) $row->outgoing_moves,
|
||||
'outgoing_qty' => (float) $row->outgoing_qty,
|
||||
'total_moves' => (int) $row->total_moves,
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
return $warehouses;
|
||||
}
|
||||
|
||||
private function getStockByWarehouse(): array
|
||||
{
|
||||
$warehouses = DB::table('warehouses')
|
||||
->leftJoin('stock_items', 'warehouses.id', '=', 'stock_items.warehouse_id')
|
||||
->leftJoin('products', 'stock_items.product_id', '=', 'products.id')
|
||||
->selectRaw(
|
||||
'warehouses.id as warehouse_id, warehouses.name as warehouse_name, ' .
|
||||
'COALESCE(SUM(stock_items.qty_on_hand_base), 0) as qty_on_hand, ' .
|
||||
'COALESCE(SUM(stock_items.qty_on_hand_base * products.prix_unitaire), 0) as stock_value, ' .
|
||||
'SUM(CASE WHEN stock_items.qty_on_hand_base <= stock_items.safety_stock_base THEN 1 ELSE 0 END) as alert_count'
|
||||
)
|
||||
->groupBy('warehouses.id', 'warehouses.name')
|
||||
->get()
|
||||
->map(function ($row) {
|
||||
return [
|
||||
'warehouse_id' => (int) $row->warehouse_id,
|
||||
'warehouse_name' => $row->warehouse_name,
|
||||
'qty_on_hand' => (float) $row->qty_on_hand,
|
||||
'stock_value' => (float) $row->stock_value,
|
||||
'alert_count' => (int) $row->alert_count,
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
return $warehouses;
|
||||
}
|
||||
/**
|
||||
* Find a default intervention product (where category has intervention=true)
|
||||
*/
|
||||
|
||||
@ -18,5 +18,7 @@ interface ProductRepositoryInterface extends BaseRepositoryInterface
|
||||
|
||||
public function getProductsByFournisseur(int $fournisseurId): LengthAwarePaginator;
|
||||
|
||||
public function getStatistics(): array;
|
||||
|
||||
public function findInterventionProduct(): ?\App\Models\Product;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user