Feat: Stat stock+
This commit is contained in:
parent
050a38c6bd
commit
39c21d3d09
@ -6,6 +6,7 @@ namespace App\Repositories;
|
|||||||
|
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class ProductRepository extends BaseRepository implements ProductRepositoryInterface
|
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())
|
$expiringProducts = $this->model->where('date_expiration', '<=', now()->addDays(30)->toDateString())
|
||||||
->where('date_expiration', '>=', now()->toDateString())
|
->where('date_expiration', '>=', now()->toDateString())
|
||||||
->count();
|
->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 [
|
return [
|
||||||
'total_products' => $totalProducts,
|
'total_products' => $totalProducts,
|
||||||
'low_stock_products' => $lowStockProducts,
|
'low_stock_products' => $lowStockProducts,
|
||||||
'expiring_products' => $expiringProducts,
|
'expiring_products' => $expiringProducts,
|
||||||
'total_value' => $totalValue,
|
'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)
|
* 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 getProductsByFournisseur(int $fournisseurId): LengthAwarePaginator;
|
||||||
|
|
||||||
|
public function getStatistics(): array;
|
||||||
|
|
||||||
public function findInterventionProduct(): ?\App\Models\Product;
|
public function findInterventionProduct(): ?\App\Models\Product;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user