Ajouter scenario demo resilience
- fermer les tuiles d assemblees par defaut - unifier la gestion des inventaires litterature et jetons - enrichir les donnees demo sur 12 mois avec rencontres, assemblees, rotations et journalisation
This commit is contained in:
parent
1ffce6e92c
commit
1ee392e25d
6 changed files with 281 additions and 17 deletions
|
|
@ -53,11 +53,11 @@ MODULES_SAISIE = {
|
|||
"Animateur(trice)": [],
|
||||
"Responsable littérature": [
|
||||
ModuleTuile(code="S3", nom="Vente littérature", description="Enregistrer une vente", icone="📖", couleur="purple"),
|
||||
ModuleTuile(code="M5", nom="Ajuster inventaire", description="Mettre à jour le stock", icone="📦", couleur="orange"),
|
||||
ModuleTuile(code="M5", nom="Gestion des inventaires", description="Littérature et jetons", icone="📦", couleur="orange"),
|
||||
],
|
||||
"Responsable jetons": [
|
||||
ModuleTuile(code="S2", nom="Remise jeton", description="Enregistrer un jeton", icone="🏅", couleur="yellow"),
|
||||
ModuleTuile(code="M6", nom="Commander jetons", description="Commander à l'Intergroupe", icone="📦", couleur="orange"),
|
||||
ModuleTuile(code="M5", nom="Gestion des inventaires", description="Littérature et jetons", icone="📦", couleur="orange"),
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ from app.models.destinataire_contribution import DestinataireContribution
|
|||
from app.models.envoi_contribution import EnvoiContribution
|
||||
from app.models.evenement import Evenement
|
||||
from app.models.groupe import Groupe
|
||||
from app.models.historique_modification import HistoriqueModification
|
||||
from app.models.jeton import Jeton
|
||||
from app.models.journal_action import JournalAction
|
||||
from app.models.litterature import Litterature
|
||||
from app.models.membre import Membre
|
||||
from app.models.mouvement_reserve import MouvementReserve
|
||||
|
|
@ -33,6 +35,7 @@ from app.models.rapport_adopte import RapportAdopte
|
|||
from app.models.rapport_rsg import RapportRsg
|
||||
from app.models.reserve import Reserve
|
||||
from app.models.reunion import Reunion
|
||||
from app.models.rotation import Rotation
|
||||
from app.models.transaction_comptable import TransactionComptable
|
||||
from app.models.vente_litterature import VenteLitterature
|
||||
from app.models.vente_jeton import VenteJeton
|
||||
|
|
@ -46,8 +49,8 @@ POSTES = [
|
|||
("Animateur(trice)", "service", "Anime les rencontres hebdomadaires.", "Disponibilité régulière", 5, ["S0"]),
|
||||
("Responsable accueil", "physique", "Accueille les nouveaux et veille à l'atmosphère de la salle.", "Fiabilité", 6, ["C9"]),
|
||||
("Responsable café", "physique", "Prépare le café et remet les petites dépenses au trésorier.", "Aucun", 7, ["S5"]),
|
||||
("Responsable littérature", "service", "Maintient l'inventaire et remet les ventes au trésorier.", "Connaissance de la littérature", 8, ["S3", "C4", "M5"]),
|
||||
("Responsable jetons", "service", "Prépare les jetons et les anniversaires.", "Aucun", 9, ["S2", "M6"]),
|
||||
("Responsable littérature", "service", "Maintient l'inventaire et remet les ventes au trésorier.", "Connaissance de la littérature", 8, ["S3", "C4", "M5", "M6"]),
|
||||
("Responsable jetons", "service", "Prépare les jetons et les anniversaires.", "Aucun", 9, ["S2", "M5", "M6"]),
|
||||
("Responsable clés", "physique", "Ouvre et ferme le local.", "Fiabilité", 10, []),
|
||||
]
|
||||
|
||||
|
|
@ -119,15 +122,17 @@ async def initialiser_demo(db: AsyncSession) -> dict:
|
|||
"groupe_id": groupe.id,
|
||||
"membres": len(membres),
|
||||
"periode_mois": 12,
|
||||
"rencontres": 52,
|
||||
"assemblees": 12,
|
||||
}
|
||||
|
||||
|
||||
async def _creer_base(db: AsyncSession):
|
||||
groupe = Groupe(
|
||||
nom="Groupe Méditation",
|
||||
nom="Groupe Résilience",
|
||||
district="87-16",
|
||||
region="87",
|
||||
adresse_local="Salle communautaire, Montréal",
|
||||
adresse_local="Salle communautaire Saint-Louis, Montréal",
|
||||
jour_reunion="mardi",
|
||||
heure_reunion=time(19, 30),
|
||||
assemblee_affaires_ordre="premiere",
|
||||
|
|
@ -242,6 +247,7 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
banque = 0.0
|
||||
encaisse = 0.0
|
||||
compteurs = {"RES": 0, "NOM": 0, "DON": 0}
|
||||
rotations_postes = ["Animateur(trice)", "Responsable accueil", "Responsable café", "Responsable clés"]
|
||||
|
||||
for index, jour in enumerate(dates_reunions):
|
||||
type_reunion = "affaires" if jour.month not in mois_vus else "reguliere"
|
||||
|
|
@ -254,12 +260,52 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
theme=["Acceptation", "Service", "Gratitude", "Honnêteté"][index % 4],
|
||||
annonces={"district": index % 5 == 0, "anniversaire": index % 8 == 0},
|
||||
note="Réunion de démonstration réaliste.",
|
||||
cree_le=_moment(jour, 18, 30),
|
||||
modifie_le=_moment(jour, 18, 30),
|
||||
)
|
||||
db.add(reunion)
|
||||
await db.flush()
|
||||
await _trace(
|
||||
db, groupe, secretaire, jour, "reunions", "reunion.planifiee",
|
||||
f"{'Assemblée d affaires' if type_reunion == 'affaires' else 'Rencontre hebdomadaire'} planifiée.",
|
||||
reunion.id,
|
||||
apres={"date": jour.isoformat(), "type": type_reunion},
|
||||
chemin="/reunions" if type_reunion == "reguliere" else "/presences/reunion",
|
||||
)
|
||||
|
||||
for decalage, poste_nom in enumerate(rotations_postes):
|
||||
poste = postes.get(poste_nom)
|
||||
if not poste:
|
||||
continue
|
||||
serviteur = membres_actifs[(index + decalage) % len(membres_actifs)]
|
||||
rotation = Rotation(
|
||||
groupe_id=groupe.id,
|
||||
date_reunion=jour,
|
||||
poste_id=poste.id,
|
||||
membre_id=serviteur.id,
|
||||
note="Rotation planifiée automatiquement pour la démonstration.",
|
||||
cree_par=secretaire.id,
|
||||
cree_le=_moment(jour, 18, 35),
|
||||
)
|
||||
db.add(rotation)
|
||||
await db.flush()
|
||||
await _trace(
|
||||
db, groupe, secretaire, jour, "rotations", "rotations.planifiees",
|
||||
"Rotations hebdomadaires planifiées.",
|
||||
reunion.id,
|
||||
apres={"postes": rotations_postes},
|
||||
chemin="/rotations/batch",
|
||||
)
|
||||
|
||||
for membre in membres_actifs:
|
||||
db.add(Presence(reunion_id=reunion.id, membre_id=membre.id, present=(index + len(membre.prenom)) % 7 != 0))
|
||||
await _trace(
|
||||
db, groupe, secretaire, jour, "presences", "presences.enregistrees",
|
||||
"Présences de la rencontre enregistrées.",
|
||||
reunion.id,
|
||||
apres={"presents": sum(1 for m in membres_actifs if (index + len(m.prenom)) % 7 != 0)},
|
||||
chemin="/presences/",
|
||||
)
|
||||
|
||||
montant_collecte = round(82 + (index % 6) * 9 + (index % 3) * 4, 2)
|
||||
collecte = Collecte(
|
||||
|
|
@ -274,16 +320,32 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
date_confirmation=datetime.combine(jour, time(21, 15)).replace(tzinfo=timezone.utc),
|
||||
recu_par=tresorier.id,
|
||||
date_reception=jour,
|
||||
cree_le=_moment(jour, 20, 55),
|
||||
modifie_le=_moment(jour, 21, 5),
|
||||
)
|
||||
db.add(collecte)
|
||||
await db.flush()
|
||||
encaisse += montant_collecte
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="collecte", compte="encaisse", montant=montant_collecte, sens="debit", source_table="collectes", source_id=collecte.id, note="Collecte 7e Tradition", cree_par=tresorier.id))
|
||||
await _trace(
|
||||
db, groupe, tresorier, jour, "collectes", "collecte.recue",
|
||||
"Collecte saisie puis reçue par le trésorier.",
|
||||
collecte.id,
|
||||
apres={"montant": montant_collecte, "statut": "recu"},
|
||||
chemin="/collectes",
|
||||
)
|
||||
|
||||
if index % 4 == 0:
|
||||
don = 40 + (index % 5) * 10
|
||||
banque += don
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="don", compte="banque", montant=don, sens="debit", note="Don direct au compte bancaire", cree_par=tresorier.id))
|
||||
await _trace(
|
||||
db, groupe, tresorier, jour, "transactions_comptables", "don.direct",
|
||||
"Don reçu directement au compte bancaire.",
|
||||
reunion.id,
|
||||
apres={"montant": don, "compte": "banque"},
|
||||
chemin="/operations-bancaires/don",
|
||||
)
|
||||
|
||||
if index % 4 == 3:
|
||||
depot = round(encaisse - 25, 2) if encaisse > 50 else round(encaisse, 2)
|
||||
|
|
@ -293,6 +355,14 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
db.add(OperationBancaire(groupe_id=groupe.id, type="depot", montant=depot, date_operation=jour, effectue_par=tresorier.id, note="Dépôt mensuel de l'encaisse"))
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="banque_depot", compte="banque", montant=depot, sens="debit", note="Dépôt de l'encaisse", cree_par=tresorier.id))
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="banque_depot", compte="encaisse", montant=depot, sens="credit", note="Dépôt de l'encaisse", cree_par=tresorier.id))
|
||||
await _trace(
|
||||
db, groupe, tresorier, jour, "operations_bancaires", "encaisse.deposee",
|
||||
"Dépôt de l'encaisse au compte bancaire.",
|
||||
collecte.id,
|
||||
avant={"encaisse": round(encaisse + depot, 2), "banque": round(banque - depot, 2)},
|
||||
apres={"encaisse": round(encaisse, 2), "banque": round(banque, 2)},
|
||||
chemin="/operations-bancaires",
|
||||
)
|
||||
|
||||
if type_reunion == "affaires":
|
||||
await _decision(db, groupe, reunion, compteurs, "RES", jour, secretaire, rsg, "Adopter l'ordre du jour", "L'assemblée doit traiter les affaires courantes.", "L'ordre du jour est adopté tel que présenté.")
|
||||
|
|
@ -314,7 +384,22 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
mois=jour.month,
|
||||
contenu_html=f"<html><body><h1>Rapport du trésorier</h1><p>Rapport adopté pour {jour.strftime('%Y-%m')}.</p><p>Banque: {banque:.2f} $</p><p>Encaisse: {encaisse:.2f} $</p></body></html>",
|
||||
donnees={"banque": round(banque, 2), "encaisse": round(encaisse, 2)},
|
||||
adopte_le=_moment(jour, 21, 35),
|
||||
))
|
||||
await _trace(
|
||||
db, groupe, secretaire, jour, "pv_reunions", "pv.redige",
|
||||
"Procès-verbal d'assemblée rédigé et classé.",
|
||||
pv.id,
|
||||
apres={"approuve": pv.approuve},
|
||||
chemin="/pv/",
|
||||
)
|
||||
await _trace(
|
||||
db, groupe, tresorier, jour, "rapports_adoptes", "rapport_tresorier.adopte",
|
||||
"Rapport du trésorier adopté par la conscience de groupe.",
|
||||
reunion.id,
|
||||
apres={"annee": jour.year, "mois": jour.month, "banque": round(banque, 2), "encaisse": round(encaisse, 2)},
|
||||
chemin="/rapports/etat-resultats/adopter",
|
||||
)
|
||||
|
||||
if index % 6 == 1:
|
||||
montant = 32 + (index % 4) * 6
|
||||
|
|
@ -329,6 +414,13 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
db.add(depense)
|
||||
await db.flush()
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="depense", compte=compte, montant=montant, sens="credit", source_table="depenses", source_id=depense.id, note=depense.quoi, cree_par=tresorier.id))
|
||||
await _trace(
|
||||
db, groupe, tresorier, jour, "depenses", "depense.confirmee",
|
||||
"Dépense soumise puis confirmée par le trésorier.",
|
||||
depense.id,
|
||||
apres={"montant": montant, "methode": methode, "quoi": depense.quoi},
|
||||
chemin="/depenses",
|
||||
)
|
||||
|
||||
if index % 8 == 2 and litterature:
|
||||
item = litterature[index % len(litterature)]
|
||||
|
|
@ -339,6 +431,13 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
await db.flush()
|
||||
encaisse += montant
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="vente_litterature", compte="encaisse", montant=montant, sens="debit", source_table="ventes_litterature", source_id=vente.id, note="Vente de littérature", cree_par=tresorier.id))
|
||||
await _trace(
|
||||
db, groupe, membres["karim.demo@example.test"], jour, "ventes_litterature", "vente_litterature.recue",
|
||||
"Vente de littérature enregistrée et remise au trésorier.",
|
||||
vente.id,
|
||||
apres={"titre": item.titre, "quantite": 2, "montant": montant},
|
||||
chemin="/ventes-litterature",
|
||||
)
|
||||
|
||||
if index % 12 == 6:
|
||||
montant = 6
|
||||
|
|
@ -358,6 +457,13 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
await db.flush()
|
||||
encaisse += montant
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="vente_jeton", compte="encaisse", montant=montant, sens="debit", source_table="ventes_jetons", source_id=vente_jeton.id, note="Vente de jetons", cree_par=tresorier.id))
|
||||
await _trace(
|
||||
db, groupe, membres["melanie.demo@example.test"], jour, "ventes_jetons", "vente_jeton.recue",
|
||||
"Vente de jetons enregistrée et remise au trésorier.",
|
||||
vente_jeton.id,
|
||||
apres={"type_jeton": vente_jeton.type_jeton, "quantite": 2, "montant": montant},
|
||||
chemin="/jetons/ventes",
|
||||
)
|
||||
|
||||
if index % 13 == 4:
|
||||
montant = 125
|
||||
|
|
@ -368,6 +474,13 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
db.add(contribution)
|
||||
await db.flush()
|
||||
db.add(TransactionComptable(groupe_id=groupe.id, date_transaction=jour, type="contribution", compte="banque", montant=montant, sens="credit", source_table="envois_contributions", source_id=contribution.id, note="Contribution au district", cree_par=tresorier.id))
|
||||
await _trace(
|
||||
db, groupe, tresorier, jour, "envois_contributions", "contribution.debitee",
|
||||
"Contribution décidée, engagée et débitée.",
|
||||
contribution.id,
|
||||
apres={"destination": contribution.destination, "montant": montant},
|
||||
chemin="/contributions",
|
||||
)
|
||||
|
||||
if index in {9, 22, 35}:
|
||||
candidat = membres_actifs[index % len(membres_actifs)]
|
||||
|
|
@ -378,9 +491,26 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
decision = await _decision(db, groupe, reunion, compteurs, "NOM", jour, rsg, secretaire, f"Nomination de {candidat.prenom}", "Un poste doit être pourvu pour soutenir le groupe.", f"Le groupe nomme {candidat.prenom} au poste de {poste.nom}.")
|
||||
decision.candidature_id = candidature.id
|
||||
candidature.proposition_id = decision.id
|
||||
db.add(Affectation(groupe_id=groupe.id, membre_id=candidat.id, poste_id=poste.id, date_debut=jour, statut="actif"))
|
||||
await _trace(
|
||||
db, groupe, rsg, jour, "candidatures", "nomination.adoptee",
|
||||
"Nomination proposée, secondée et adoptée.",
|
||||
candidature.id,
|
||||
apres={"membre": candidat.prenom, "poste": poste.nom, "resolution": decision.numero_resolution},
|
||||
chemin="/postes/candidatures",
|
||||
)
|
||||
|
||||
if index % 10 == 5:
|
||||
db.add(Jeton(groupe_id=groupe.id, reunion_id=reunion.id, membre_id=membres_actifs[index % len(membres_actifs)].id, type_jeton="multiple", date_remise=jour, annees=1 + index % 5, remis_par=membres["melanie.demo@example.test"].id))
|
||||
jeton = Jeton(groupe_id=groupe.id, reunion_id=reunion.id, membre_id=membres_actifs[index % len(membres_actifs)].id, type_jeton="multiple", date_remise=jour, annees=1 + index % 5, remis_par=membres["melanie.demo@example.test"].id, cree_le=_moment(jour, 20, 5))
|
||||
db.add(jeton)
|
||||
await db.flush()
|
||||
await _trace(
|
||||
db, groupe, membres["melanie.demo@example.test"], jour, "jetons", "jeton.remis",
|
||||
"Remise de jeton enregistrée pour statistiques.",
|
||||
jeton.id,
|
||||
apres={"annees": jeton.annees},
|
||||
chemin="/jetons",
|
||||
)
|
||||
|
||||
if index % 17 == 7:
|
||||
reserve = reserves[["Loyer", "Littérature", "Prudentielle"][index % 3]]
|
||||
|
|
@ -388,6 +518,14 @@ async def _ajouter_historique_demo(db: AsyncSession, groupe: Groupe, membres: di
|
|||
if banque >= montant:
|
||||
reserve.montant = float(reserve.montant or 0) + montant
|
||||
db.add(MouvementReserve(groupe_id=groupe.id, source_id=None, destination_id=reserve.id, montant=montant, type="reserve_virement", note="Affectation au solde réservé", cree_par=tresorier.id, cree_le=datetime.combine(jour, time(21, 0)).replace(tzinfo=timezone.utc)))
|
||||
await _trace(
|
||||
db, groupe, tresorier, jour, "mouvements_reserves", "reserve.transfert",
|
||||
"Montant transféré du disponible vers une réserve.",
|
||||
reserve.id,
|
||||
avant={"reserve": round(float(reserve.montant or 0) - montant, 2)},
|
||||
apres={"reserve": round(float(reserve.montant or 0), 2), "montant": montant},
|
||||
chemin="/operations-bancaires/reserves/transfert",
|
||||
)
|
||||
|
||||
db.add(Collecte(groupe_id=groupe.id, date_collecte=dates_reunions[-1] + timedelta(days=7), montant=96, detenteur_id=membres["luc.demo@example.test"].id, statut="en_attente", saisi_par=membres["luc.demo@example.test"].id))
|
||||
db.add(Depense(groupe_id=groupe.id, date_depense=date.today(), montant=48, qui=membres["anne.demo@example.test"].id, quoi="Fournitures à confirmer", paye=False, methode=None, saisi_par=membres["anne.demo@example.test"].id))
|
||||
|
|
@ -414,12 +552,65 @@ async def _decision(db, groupe, reunion, compteurs, prefixe, jour, proposeur, se
|
|||
date_adoption=jour,
|
||||
etant_donne_que=attendu,
|
||||
groupe_a_decide_de=decide,
|
||||
cree_le=_moment(jour, 20, 30),
|
||||
modifie_le=_moment(jour, 20, 45),
|
||||
)
|
||||
db.add(proposition)
|
||||
await db.flush()
|
||||
await _trace(
|
||||
db, groupe, proposeur, jour, "propositions", f"decision.{prefixe.lower()}",
|
||||
"Décision adoptée par la conscience de groupe.",
|
||||
proposition.id,
|
||||
apres={"numero": numero, "sujet": sujet, "decision": decide},
|
||||
chemin="/propositions/voter",
|
||||
)
|
||||
return proposition
|
||||
|
||||
|
||||
def _moment(jour: date, heure: int = 20, minute: int = 0) -> datetime:
|
||||
return datetime.combine(jour, time(heure, minute)).replace(tzinfo=timezone.utc)
|
||||
|
||||
|
||||
async def _trace(
|
||||
db: AsyncSession,
|
||||
groupe: Groupe,
|
||||
membre: Membre,
|
||||
jour: date,
|
||||
table: str,
|
||||
action: str,
|
||||
raison: str,
|
||||
objet_id=None,
|
||||
avant: dict | None = None,
|
||||
apres: dict | None = None,
|
||||
chemin: str | None = None,
|
||||
):
|
||||
moment = _moment(jour, 21, 50)
|
||||
cible = objet_id or groupe.id
|
||||
db.add(HistoriqueModification(
|
||||
groupe_id=groupe.id,
|
||||
table_cible=table,
|
||||
objet_id=cible,
|
||||
action=action,
|
||||
raison=raison,
|
||||
membre_id=membre.id if membre else None,
|
||||
chemin_api=chemin,
|
||||
avant=avant,
|
||||
apres=apres,
|
||||
cree_le=moment,
|
||||
))
|
||||
db.add(JournalAction(
|
||||
groupe_id=groupe.id,
|
||||
membre_id=membre.id if membre else None,
|
||||
methode="POST",
|
||||
chemin=chemin or f"/demo/{table}",
|
||||
statut_http=200,
|
||||
adresse_ip="127.0.0.1",
|
||||
user_agent="DemoSeeder/1.0",
|
||||
details={"action": action, "raison": raison},
|
||||
cree_le=moment,
|
||||
))
|
||||
|
||||
|
||||
def _premier_mardi(jour: date) -> date:
|
||||
delta = (1 - jour.weekday()) % 7
|
||||
return jour + timedelta(days=delta)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Script d'initialisation — Groupe Méditation
|
||||
Script d'initialisation — Groupe Résilience
|
||||
Crée les postes, le groupe initial et le premier membre.
|
||||
Usage: cd /opt/groupe-meditation/backend && venv/bin/python ../deploy/seed.py
|
||||
"""
|
||||
|
|
@ -92,14 +92,14 @@ async def seed():
|
|||
|
||||
async with async_session() as db:
|
||||
# ── Créer le groupe ──
|
||||
print("\n═══ Initialisation du Groupe Méditation ═══\n")
|
||||
print("\n═══ Initialisation du Groupe Résilience ═══\n")
|
||||
groupe = Groupe(
|
||||
nom="Groupe Méditation",
|
||||
nom="Groupe Résilience",
|
||||
district="87-16",
|
||||
region="87",
|
||||
adresse_local="",
|
||||
adresse_local="Salle communautaire Saint-Louis, Montréal",
|
||||
jour_reunion="mardi",
|
||||
heure_reunion=None,
|
||||
heure_reunion=datetime.strptime("19:30", "%H:%M").time(),
|
||||
format_reunion="Discussion ouverte",
|
||||
lien_aa87="https://aa87.org",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@ const COULEURS = {
|
|||
|
||||
const NAVIGABLE = ['C7', 'S1', 'S1T', 'S2', 'S3', 'S4', 'S5', 'C1', 'C2', 'C4', 'C5', 'C9', 'C11', 'C12', 'G2', 'G9', 'G11', 'G12', 'G13', 'M0', 'M1', 'M2', 'M3', 'M4', 'M5', 'M6'];
|
||||
const MODULES_CONTEXTE = new Set(['S0', 'M0', 'M1', 'M2']);
|
||||
const MODULE_INVENTAIRES = {
|
||||
code: 'M5',
|
||||
nom: 'Gestion des inventaires',
|
||||
description: 'Littérature et jetons',
|
||||
icone: '📦',
|
||||
couleur: 'orange',
|
||||
};
|
||||
|
||||
const CONTEXTES = {
|
||||
accueil: {
|
||||
|
|
@ -41,7 +48,7 @@ const CONTEXTES = {
|
|||
sections: [
|
||||
{ titre: 'Finances', codes: ['S1T', 'S5', 'M4'] },
|
||||
{ titre: 'Membres et structure', codes: ['G2', 'G11', 'G12', 'G9', 'G13'] },
|
||||
{ titre: 'Inventaire', codes: ['M5', 'M6'] },
|
||||
{ titre: 'Inventaire', codes: ['M5'] },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -96,6 +103,7 @@ export default function Accueil() {
|
|||
const moduleParCode = useMemo(() => {
|
||||
const map = new Map();
|
||||
tousModules.forEach((m) => map.set(m.code, m));
|
||||
if (map.has('M5') || map.has('M6')) map.set('M5', MODULE_INVENTAIRES);
|
||||
return map;
|
||||
}, [tousModules]);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const CATEGORIES = [
|
|||
|
||||
export default function AjusterInventaire() {
|
||||
const [items, setItems] = useState([]);
|
||||
const [jetons, setJetons] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [success, setSuccess] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
|
|
@ -19,9 +20,16 @@ export default function AjusterInventaire() {
|
|||
|
||||
const charger = async () => {
|
||||
try {
|
||||
const data = await api.get('/litterature/');
|
||||
const [data, stockJetons] = await Promise.all([
|
||||
api.get('/litterature/'),
|
||||
api.get('/commandes-jetons/stock'),
|
||||
]);
|
||||
setItems(data);
|
||||
} catch { setItems([]); }
|
||||
setJetons(stockJetons);
|
||||
} catch {
|
||||
setItems([]);
|
||||
setJetons([]);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
|
@ -43,9 +51,16 @@ export default function AjusterInventaire() {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-100">
|
||||
<PageHeader titre="Ajuster inventaire" sousTitre="M5 — Stock littérature" />
|
||||
<PageHeader titre="Gestion des inventaires" sousTitre="Littérature et jetons" />
|
||||
|
||||
<div className="p-4 space-y-4">
|
||||
<section className="space-y-2">
|
||||
<h2 className="text-xs font-bold text-gray-400 uppercase tracking-wider px-1">Jetons</h2>
|
||||
<StockJetons stock={jetons} loading={loading} />
|
||||
</section>
|
||||
|
||||
<section className="space-y-2">
|
||||
<h2 className="text-xs font-bold text-gray-400 uppercase tracking-wider px-1">Littérature</h2>
|
||||
{/* Bouton ajouter un titre */}
|
||||
<button
|
||||
onClick={() => setShowAjout(!showAjout)}
|
||||
|
|
@ -71,11 +86,62 @@ export default function AjusterInventaire() {
|
|||
<p className="text-gray-400">Aucun titre dans le catalogue</p>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StockJetons({ stock, loading }) {
|
||||
const aCommander = stock.filter((s) => s.stock_suggestion !== 'OK');
|
||||
if (loading) return null;
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{aCommander.length > 0 && (
|
||||
<div className="bg-orange-50 border-2 border-orange-300 rounded-2xl p-4">
|
||||
<p className="font-bold text-orange-700 text-sm mb-2">
|
||||
Jetons à commander ({aCommander.length})
|
||||
</p>
|
||||
<div className="space-y-1">
|
||||
{aCommander.map((s) => (
|
||||
<p key={s.type_jeton} className="text-sm text-orange-600">
|
||||
{s.label} — {s.remis_dernier_mois} remis ce mois
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{stock.map((s) => (
|
||||
<div
|
||||
key={s.type_jeton}
|
||||
className={`bg-white rounded-2xl p-4 shadow-sm flex items-center gap-3 border-2 ${
|
||||
s.stock_suggestion !== 'OK' ? 'border-orange-300' : 'border-gray-100'
|
||||
}`}
|
||||
>
|
||||
<span className="text-2xl">{s.label.split(' ')[0]}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-bold text-sm">{s.label.split(' ').slice(1).join(' ')}</p>
|
||||
<p className="text-xs text-gray-400">{s.remis_dernier_mois} remis ou vendu récemment</p>
|
||||
</div>
|
||||
<span className={`text-xs font-bold px-3 py-1 rounded-full ${
|
||||
s.stock_suggestion === 'OK'
|
||||
? 'bg-green-100 text-green-700'
|
||||
: 'bg-orange-100 text-orange-700'
|
||||
}`}>
|
||||
{s.stock_suggestion}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
{stock.length === 0 && (
|
||||
<div className="text-center py-8 bg-white rounded-2xl shadow-sm">
|
||||
<span className="text-4xl block mb-2">🏅</span>
|
||||
<p className="text-gray-400">Aucune donnée de jetons pour le moment</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ItemStock({ item, onAjuster }) {
|
||||
const [ajustement, setAjustement] = useState('');
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ export default function AssembleeAffaires({ contexte = false }) {
|
|||
secondeur_id: f.secondeur_id || m[1]?.id || '',
|
||||
}));
|
||||
if (!membreProposeId && m[0]) setMembreProposeId(m[0].id);
|
||||
if (!ouverteId && r.length) setOuverteId(r[0].id);
|
||||
} catch (e) {
|
||||
setError(e.message);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue