IT-Stock/app/templates/scan.html
maxsoch 5088182f20 Création rapide depuis le scan, ajustement de stock flexible, filtres matériels
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.
2026-07-14 19:17:27 +02:00

127 lines
5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block titre %}Scanner - Gestion de stock IT{% endblock %}
{% block contenu %}
<section class="scanner">
<header class="scanner-entete">
<p class="scanner-eyebrow">Poste de scan</p>
<h1>Identifier un matériel</h1>
</header>
{#
Un seul champ dans un <form> : la douchette tape le code puis Entrée,
ce qui soumet le formulaire nativement (aucun JavaScript nécessaire
pour capter le scan). La "ligne laser" sous le champ ne s'anime que
lorsqu'il a le focus (voir .scanner-champ:focus + .laser-ligne dans
style.css) : un clin d'œil au faisceau de la douchette physique.
#}
<form method="post" action="/scan" class="scanner-formulaire">
<label for="champ-scan" class="scanner-label">Code-barre</label>
<input
type="text"
name="code_barre"
id="champ-scan"
class="scanner-champ"
placeholder="En attente de scan…"
autocomplete="off"
autofocus
required
>
<div class="laser-ligne" aria-hidden="true"></div>
</form>
{% if code_barre_introuvable %}
{#
Code scanné mais aucun matériel ne le porte : au lieu d'un simple
message d'erreur, on propose le même formulaire de création rapide que
sur /materiels, avec le code-barre déjà rempli, pour ne pas avoir à
quitter l'écran de scan.
#}
<article class="etiquette-resultat etiquette-creation">
<div class="rayures-code-barre etiquette-rayures" aria-hidden="true"></div>
<p class="etiquette-fil">Code inconnu</p>
<h2 class="etiquette-nom">« {{ code_barre_introuvable }} »</h2>
{% if categories_aplaties %}
<form method="post" action="/scan/creer" class="formulaire-grille etiquette-formulaire">
<input type="hidden" name="code_barre" value="{{ code_barre_introuvable }}">
<label>Nom <input type="text" name="nom" required autofocus></label>
<label>Catégorie
<select name="categorie_id" required>
{% for profondeur, categorie in categories_aplaties %}
<option value="{{ categorie.id }}">{{ "— " * profondeur }}{{ categorie.nom }}</option>
{% endfor %}
</select>
</label>
<label>Quantité initiale <input type="number" name="quantite" value="0" min="0"></label>
<label>Seuil d'alerte <input type="number" name="seuil_alerte" min="0"></label>
<button type="submit">Créer et ajouter au stock</button>
</form>
{% else %}
<p class="etiquette-sous-titre">
Créez d'abord une <a href="/categories">catégorie</a> avant de pouvoir créer ce matériel.
</p>
{% endif %}
</article>
{% endif %}
{% if materiel %}
{% set sous_seuil = materiel.seuil_alerte is not none and materiel.quantite < materiel.seuil_alerte %}
<article class="etiquette-resultat">
<div class="rayures-code-barre etiquette-rayures" aria-hidden="true"></div>
<p class="etiquette-fil">{{ materiel.categorie.chemin() }}</p>
<h2 class="etiquette-nom">{{ materiel.nom }}</h2>
<div class="etiquette-quantite">
<span class="etiquette-nombre">{{ materiel.quantite }}</span>
<span class="etiquette-unite">en stock</span>
</div>
{% if materiel.seuil_alerte is not none %}
<p class="etiquette-statut">
<span class="statut-puce {{ 'statut-bas' if sous_seuil else 'statut-ok' }}"></span>
{% if sous_seuil %}
Sous le seuil d'alerte ({{ materiel.seuil_alerte }})
{% else %}
Seuil d'alerte : {{ materiel.seuil_alerte }}
{% endif %}
</p>
{% endif %}
{#
Deux façons distinctes d'agir sur le stock, pour ne pas mélanger les
deux gestes : "ajuster" applique un delta (une quantité reçue ou
sortie, pas forcément 1) ; "définir" corrige directement le total
(utile après un inventaire physique, sans calculer d'écart).
#}
<div class="etiquette-controles">
<form method="post" action="/scan/{{ materiel.id }}/ajuster" class="formulaire-ajustement">
<label class="etiquette-champ-quantite">
Quantité à ajuster
<input type="number" name="quantite" value="1" min="1" required>
</label>
<div class="etiquette-actions">
<button type="submit" name="sens" value="retirer" class="bouton-scan bouton-sortie"> Retirer</button>
<button type="submit" name="sens" value="ajouter" class="bouton-scan bouton-reception">+ Ajouter</button>
</div>
</form>
<form method="post" action="/scan/{{ materiel.id }}/definir" class="formulaire-definir">
<label class="etiquette-champ-quantite">
Définir le stock à
<input type="number" name="quantite" value="{{ materiel.quantite }}" min="0" required>
</label>
<button type="submit">Enregistrer</button>
</form>
</div>
</article>
{% endif %}
</section>
<script>
// Remet le focus sur le champ de scan à chaque affichage de la page,
// pour pouvoir enchaîner les scans sans avoir à cliquer dans le champ.
document.getElementById("champ-scan").focus();
</script>
{% endblock %}