Un code-barre inconnu scanné propose désormais le même formulaire de création que /materiels au lieu d'un simple message d'erreur. La quantité peut être ajustée d'un delta choisi librement (pas figé à ±1) ou définie directement à une valeur absolue après un inventaire. La racine "/" sert directement la liste des matériels sans redirection. La page Matériels gagne un filtre par catégorie, un filtre stock bas, et un tri incluant un regroupement par catégorie.
37 lines
1.4 KiB
Python
37 lines
1.4 KiB
Python
"""
|
|
Utilitaires de parcours de l'arborescence de catégories (profondeur
|
|
illimitée). Partagés entre le routeur categories (affichage de l'arbre) et
|
|
le routeur materiels (construction du <select> indenté).
|
|
"""
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from app.models import Categorie
|
|
|
|
|
|
def categories_racines(session: Session) -> list[Categorie]:
|
|
"""Catégories de premier niveau (sans parent)."""
|
|
return list(session.exec(select(Categorie).where(Categorie.parent_id.is_(None))).all())
|
|
|
|
|
|
def aplatir(categories: list[Categorie], profondeur: int = 0) -> list[tuple[int, Categorie]]:
|
|
"""Aplatit l'arborescence en une liste (profondeur, categorie), dans
|
|
l'ordre d'affichage (chaque nœud juste avant ses enfants). Sert à
|
|
construire un <select> indenté sans avoir besoin de JavaScript."""
|
|
resultat: list[tuple[int, Categorie]] = []
|
|
for categorie in categories:
|
|
resultat.append((profondeur, categorie))
|
|
resultat.extend(aplatir(categorie.enfants, profondeur + 1))
|
|
return resultat
|
|
|
|
|
|
def ids_sous_arbre(categorie: Categorie) -> set[int]:
|
|
"""L'id de cette catégorie et de tous ses descendants, à n'importe
|
|
quelle profondeur. Sert au filtre "cette catégorie" sur /materiels :
|
|
on veut aussi les matériels rattachés à ses sous-catégories, pas
|
|
seulement à elle-même."""
|
|
ids = {categorie.id}
|
|
for enfant in categorie.enfants:
|
|
ids |= ids_sous_arbre(enfant)
|
|
return ids
|