generated from gilles/template-webapp
claude code
This commit is contained in:
247
backend/app/repositories/item.py
Normal file
247
backend/app/repositories/item.py
Normal file
@@ -0,0 +1,247 @@
|
||||
"""Repository pour les objets d'inventaire."""
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.models.item import Item, ItemStatus
|
||||
from app.repositories.base import BaseRepository
|
||||
|
||||
|
||||
class ItemRepository(BaseRepository[Item]):
|
||||
"""Repository pour les opérations sur les objets."""
|
||||
|
||||
def __init__(self, db: AsyncSession) -> None:
|
||||
"""Initialise le repository."""
|
||||
super().__init__(Item, db)
|
||||
|
||||
async def get_with_relations(self, id: int) -> Item | None:
|
||||
"""Récupère un objet avec ses relations (catégorie, emplacement, documents).
|
||||
|
||||
Args:
|
||||
id: ID de l'objet
|
||||
|
||||
Returns:
|
||||
L'objet avec ses relations ou None
|
||||
"""
|
||||
result = await self.db.execute(
|
||||
select(Item)
|
||||
.options(
|
||||
selectinload(Item.category),
|
||||
selectinload(Item.location),
|
||||
selectinload(Item.documents),
|
||||
)
|
||||
.where(Item.id == id)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def get_all_with_relations(
|
||||
self, skip: int = 0, limit: int = 100
|
||||
) -> list[Item]:
|
||||
"""Récupère tous les objets avec leurs relations.
|
||||
|
||||
Args:
|
||||
skip: Offset
|
||||
limit: Limite
|
||||
|
||||
Returns:
|
||||
Liste des objets avec relations
|
||||
"""
|
||||
result = await self.db.execute(
|
||||
select(Item)
|
||||
.options(
|
||||
selectinload(Item.category),
|
||||
selectinload(Item.location),
|
||||
)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
.order_by(Item.name)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def search(
|
||||
self,
|
||||
query: str,
|
||||
category_id: int | None = None,
|
||||
location_id: int | None = None,
|
||||
status: ItemStatus | None = None,
|
||||
min_price: Decimal | None = None,
|
||||
max_price: Decimal | None = None,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
) -> list[Item]:
|
||||
"""Recherche des objets avec filtres.
|
||||
|
||||
Args:
|
||||
query: Texte de recherche (nom, description, marque, modèle)
|
||||
category_id: Filtre par catégorie
|
||||
location_id: Filtre par emplacement
|
||||
status: Filtre par statut
|
||||
min_price: Prix minimum
|
||||
max_price: Prix maximum
|
||||
skip: Offset
|
||||
limit: Limite
|
||||
|
||||
Returns:
|
||||
Liste des objets correspondants
|
||||
"""
|
||||
stmt = select(Item).options(
|
||||
selectinload(Item.category),
|
||||
selectinload(Item.location),
|
||||
)
|
||||
|
||||
# Recherche textuelle
|
||||
if query:
|
||||
search_term = f"%{query}%"
|
||||
stmt = stmt.where(
|
||||
or_(
|
||||
Item.name.ilike(search_term),
|
||||
Item.description.ilike(search_term),
|
||||
Item.brand.ilike(search_term),
|
||||
Item.model.ilike(search_term),
|
||||
Item.notes.ilike(search_term),
|
||||
)
|
||||
)
|
||||
|
||||
# Filtres
|
||||
if category_id is not None:
|
||||
stmt = stmt.where(Item.category_id == category_id)
|
||||
if location_id is not None:
|
||||
stmt = stmt.where(Item.location_id == location_id)
|
||||
if status is not None:
|
||||
stmt = stmt.where(Item.status == status)
|
||||
if min_price is not None:
|
||||
stmt = stmt.where(Item.price >= min_price)
|
||||
if max_price is not None:
|
||||
stmt = stmt.where(Item.price <= max_price)
|
||||
|
||||
stmt = stmt.offset(skip).limit(limit).order_by(Item.name)
|
||||
result = await self.db.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def count_filtered(
|
||||
self,
|
||||
query: str | None = None,
|
||||
category_id: int | None = None,
|
||||
location_id: int | None = None,
|
||||
status: ItemStatus | None = None,
|
||||
min_price: Decimal | None = None,
|
||||
max_price: Decimal | None = None,
|
||||
) -> int:
|
||||
"""Compte les objets avec filtres.
|
||||
|
||||
Returns:
|
||||
Nombre d'objets correspondants
|
||||
"""
|
||||
from sqlalchemy import func
|
||||
|
||||
stmt = select(func.count(Item.id))
|
||||
|
||||
if query:
|
||||
search_term = f"%{query}%"
|
||||
stmt = stmt.where(
|
||||
or_(
|
||||
Item.name.ilike(search_term),
|
||||
Item.description.ilike(search_term),
|
||||
Item.brand.ilike(search_term),
|
||||
Item.model.ilike(search_term),
|
||||
Item.notes.ilike(search_term),
|
||||
)
|
||||
)
|
||||
|
||||
if category_id is not None:
|
||||
stmt = stmt.where(Item.category_id == category_id)
|
||||
if location_id is not None:
|
||||
stmt = stmt.where(Item.location_id == location_id)
|
||||
if status is not None:
|
||||
stmt = stmt.where(Item.status == status)
|
||||
if min_price is not None:
|
||||
stmt = stmt.where(Item.price >= min_price)
|
||||
if max_price is not None:
|
||||
stmt = stmt.where(Item.price <= max_price)
|
||||
|
||||
result = await self.db.execute(stmt)
|
||||
return result.scalar_one()
|
||||
|
||||
async def get_by_category(
|
||||
self, category_id: int, skip: int = 0, limit: int = 100
|
||||
) -> list[Item]:
|
||||
"""Récupère les objets d'une catégorie.
|
||||
|
||||
Args:
|
||||
category_id: ID de la catégorie
|
||||
skip: Offset
|
||||
limit: Limite
|
||||
|
||||
Returns:
|
||||
Liste des objets
|
||||
"""
|
||||
result = await self.db.execute(
|
||||
select(Item)
|
||||
.where(Item.category_id == category_id)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
.order_by(Item.name)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def get_by_location(
|
||||
self, location_id: int, skip: int = 0, limit: int = 100
|
||||
) -> list[Item]:
|
||||
"""Récupère les objets d'un emplacement.
|
||||
|
||||
Args:
|
||||
location_id: ID de l'emplacement
|
||||
skip: Offset
|
||||
limit: Limite
|
||||
|
||||
Returns:
|
||||
Liste des objets
|
||||
"""
|
||||
result = await self.db.execute(
|
||||
select(Item)
|
||||
.where(Item.location_id == location_id)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
.order_by(Item.name)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def get_by_status(
|
||||
self, status: ItemStatus, skip: int = 0, limit: int = 100
|
||||
) -> list[Item]:
|
||||
"""Récupère les objets par statut.
|
||||
|
||||
Args:
|
||||
status: Statut recherché
|
||||
skip: Offset
|
||||
limit: Limite
|
||||
|
||||
Returns:
|
||||
Liste des objets
|
||||
"""
|
||||
result = await self.db.execute(
|
||||
select(Item)
|
||||
.where(Item.status == status)
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
.order_by(Item.name)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def get_by_serial_number(self, serial_number: str) -> Item | None:
|
||||
"""Récupère un objet par son numéro de série.
|
||||
|
||||
Args:
|
||||
serial_number: Numéro de série
|
||||
|
||||
Returns:
|
||||
L'objet trouvé ou None
|
||||
"""
|
||||
result = await self.db.execute(
|
||||
select(Item).where(Item.serial_number == serial_number)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
Reference in New Issue
Block a user