diff --git a/backend/app/main.py b/backend/app/main.py
index 4893253..e6b04f3 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -61,6 +61,11 @@ async def lifespan(app: FastAPI):
await conn.execute(text("ALTER TABLE ventes_litterature ADD COLUMN IF NOT EXISTS reunion_id UUID REFERENCES reunions(id)"))
await conn.execute(text("ALTER TABLE invitations ADD COLUMN IF NOT EXISTS cible_type VARCHAR(40)"))
await conn.execute(text("ALTER TABLE invitations ADD COLUMN IF NOT EXISTS cible_id UUID"))
+ await conn.execute(text("ALTER TABLE rapports_rsg ADD COLUMN IF NOT EXISTS infos_district TEXT"))
+ await conn.execute(text("ALTER TABLE rapports_rsg ADD COLUMN IF NOT EXISTS positions_groupe TEXT"))
+ await conn.execute(text("ALTER TABLE rapports_rsg ADD COLUMN IF NOT EXISTS suivis TEXT"))
+ await conn.execute(text("ALTER TABLE rapports_rsg ADD COLUMN IF NOT EXISTS prochaine_echeance TEXT"))
+ await conn.execute(text("ALTER TABLE rapports_rsg ADD COLUMN IF NOT EXISTS liens_utiles TEXT"))
for table in (
"propositions",
"depenses",
diff --git a/backend/app/models/rapport_rsg.py b/backend/app/models/rapport_rsg.py
index 4961370..fff6b81 100644
--- a/backend/app/models/rapport_rsg.py
+++ b/backend/app/models/rapport_rsg.py
@@ -14,6 +14,11 @@ class RapportRsg(Base):
reunion_dist: Mapped[date] = mapped_column(Date, nullable=False)
redacteur_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("membres.id"), nullable=False)
decisions: Mapped[list | None] = mapped_column(JSON)
+ infos_district: Mapped[str | None] = mapped_column(Text)
infos_region: Mapped[str | None] = mapped_column(Text)
sujets_groupe: Mapped[str | None] = mapped_column(Text)
+ positions_groupe: Mapped[str | None] = mapped_column(Text)
+ suivis: Mapped[str | None] = mapped_column(Text)
+ prochaine_echeance: Mapped[str | None] = mapped_column(Text)
+ liens_utiles: Mapped[str | None] = mapped_column(Text)
cree_le: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
diff --git a/backend/app/routers/accueil.py b/backend/app/routers/accueil.py
index 319cb28..7a25da7 100644
--- a/backend/app/routers/accueil.py
+++ b/backend/app/routers/accueil.py
@@ -46,10 +46,10 @@ MODULES_SAISIE = {
ModuleTuile(code="M2", nom="Présences", description="Réunion d'affaires", icone="✅", couleur="green"),
],
"RSG": [
- ModuleTuile(code="M3", nom="Rapport RSG", description="Compte-rendu district", icone="📋", couleur="blue"),
+ ModuleTuile(code="M3", nom="Services", description="District, région, sujets et suivis", icone="📋", couleur="blue"),
],
"RSG Substitut(e)": [
- ModuleTuile(code="M3", nom="Rapport RSG", description="Compte-rendu district", icone="📋", couleur="blue"),
+ ModuleTuile(code="M3", nom="Services", description="District, région, sujets et suivis", icone="📋", couleur="blue"),
],
"Animateur(trice)": [],
"Responsable littérature": [
diff --git a/backend/app/routers/rapports_rsg.py b/backend/app/routers/rapports_rsg.py
index 411e180..aa10e41 100644
--- a/backend/app/routers/rapports_rsg.py
+++ b/backend/app/routers/rapports_rsg.py
@@ -1,4 +1,4 @@
-"""Routes M3 — Rapport du RSG."""
+"""Routes M3 — Services généraux."""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, desc
@@ -11,7 +11,7 @@ from app.models.affectation import Affectation
from app.models.poste import Poste
from app.schemas.phase4 import RapportRsgCreate, RapportRsgOut
-router = APIRouter(prefix="/rapports-rsg", tags=["M3 — Rapport RSG"])
+router = APIRouter(prefix="/rapports-rsg", tags=["M3 — Services"])
async def require_rsg(membre=Depends(get_current_membre), db=Depends(get_db)):
@@ -39,8 +39,13 @@ async def _to_out(r: RapportRsg, db: AsyncSession) -> RapportRsgOut:
reunion_dist=r.reunion_dist,
redacteur_prenom=redacteur.prenom if redacteur else "?",
decisions=r.decisions,
+ infos_district=r.infos_district,
infos_region=r.infos_region,
sujets_groupe=r.sujets_groupe,
+ positions_groupe=r.positions_groupe,
+ suivis=r.suivis,
+ prochaine_echeance=r.prochaine_echeance,
+ liens_utiles=r.liens_utiles,
cree_le=r.cree_le,
)
@@ -51,7 +56,7 @@ async def liste_rapports_rsg(
membre=Depends(get_current_membre),
db: AsyncSession = Depends(get_db),
):
- """Lister les rapports RSG du groupe."""
+ """Lister les renseignements de service du groupe."""
result = await db.execute(
select(RapportRsg)
.where(RapportRsg.groupe_id == membre.groupe_id)
@@ -67,14 +72,19 @@ async def creer_rapport_rsg(
membre=Depends(require_rsg),
db: AsyncSession = Depends(get_db),
):
- """M3 — Rédiger un rapport de réunion de district."""
+ """M3 — Renseigner les services généraux."""
rapport = RapportRsg(
groupe_id=membre.groupe_id,
reunion_dist=req.reunion_dist,
redacteur_id=membre.id,
decisions=req.decisions,
+ infos_district=req.infos_district,
infos_region=req.infos_region,
sujets_groupe=req.sujets_groupe,
+ positions_groupe=req.positions_groupe,
+ suivis=req.suivis,
+ prochaine_echeance=req.prochaine_echeance,
+ liens_utiles=req.liens_utiles,
)
db.add(rapport)
await db.flush()
diff --git a/backend/app/schemas/phase4.py b/backend/app/schemas/phase4.py
index e351bf8..2479ca8 100644
--- a/backend/app/schemas/phase4.py
+++ b/backend/app/schemas/phase4.py
@@ -1,4 +1,4 @@
-"""Schémas Phase 4 — PV, Présences, Rapport RSG."""
+"""Schémas Phase 4 — PV, Présences, Services."""
from pydantic import BaseModel
from datetime import date, datetime
from uuid import UUID
@@ -79,12 +79,17 @@ class PvAdoption(BaseModel):
contenu: Optional[str] = None
-# ── M3 Rapport RSG ──
+# ── M3 Services ──
class RapportRsgCreate(BaseModel):
reunion_dist: date
decisions: Optional[list[str]] = None
+ infos_district: Optional[str] = None
infos_region: Optional[str] = None
sujets_groupe: Optional[str] = None
+ positions_groupe: Optional[str] = None
+ suivis: Optional[str] = None
+ prochaine_echeance: Optional[str] = None
+ liens_utiles: Optional[str] = None
class RapportRsgOut(BaseModel):
@@ -92,8 +97,13 @@ class RapportRsgOut(BaseModel):
reunion_dist: date
redacteur_prenom: str
decisions: Optional[list[str]] = None
+ infos_district: Optional[str] = None
infos_region: Optional[str] = None
sujets_groupe: Optional[str] = None
+ positions_groupe: Optional[str] = None
+ suivis: Optional[str] = None
+ prochaine_echeance: Optional[str] = None
+ liens_utiles: Optional[str] = None
cree_le: datetime
model_config = {"from_attributes": True}
diff --git a/docs/10_MANUEL_UTILISATEUR.md b/docs/10_MANUEL_UTILISATEUR.md
index 5eeeed0..56cf80c 100644
--- a/docs/10_MANUEL_UTILISATEUR.md
+++ b/docs/10_MANUEL_UTILISATEUR.md
@@ -132,7 +132,24 @@ La page de l'assemblée sert d'ordre du jour. Le rapport PDF sert de procès-ver
Dans une assemblée, ouvrir `Présences`, cocher les membres présents et enregistrer. Les présences alimentent le rapport PDF.
-## 11. Procès-verbal et rapport du trésorier
+## 11. Services
+
+Le module `Services` est destiné au RSG, au RSG substitut et aux exécutifs. Il sert à garder la mémoire des informations de service à transmettre entre le district, la région et le groupe.
+
+On peut y renseigner:
+
+- les décisions du district;
+- les informations du district;
+- les informations de la région;
+- les sujets à rapporter au groupe;
+- les positions du groupe à transmettre;
+- les suivis à faire;
+- les prochaines échéances;
+- les liens ou références utiles.
+
+Les renseignements précédents restent consultables par date de réunion de district.
+
+## 12. Procès-verbal et rapport du trésorier
L'adoption d'un procès-verbal ou d'un rapport du trésorier est faite par un exécutif avec un bouton `Adopter`. L'adoption demande un proposeur et un secondeur.
@@ -140,7 +157,7 @@ Avant adoption, le rapport peut être corrigé. Après adoption, il est cristall
Le rapport du trésorier doit être adopté avant de décider du montant des contributions.
-## 12. Gouvernance
+## 13. Gouvernance
Le module `Gouvernance` rassemble les propositions, réflexions en cours, rejets et décisions.
@@ -169,7 +186,7 @@ Les types de décisions sont:
- `Nomination`;
- `Contribution`.
-## 13. Dépenses
+## 14. Dépenses
Un membre peut soumettre une dépense en son nom. Un exécutif peut soumettre pour un autre membre.
@@ -179,7 +196,7 @@ La dépense est ensuite traitée dans `Trésorerie`:
- `Chèque` ou `Virement`: crée un engagement jusqu'au débit bancaire;
- `Rejeter`: efface la demande.
-## 14. Trésorerie
+## 15. Trésorerie
Le module `Trésorerie` présente d'abord la position du groupe:
@@ -203,7 +220,7 @@ Les exécutifs peuvent:
La page `Résultats` présente l'état des résultats par mois.
-## 15. Donner au suivant
+## 16. Donner au suivant
Le module `Donner au suivant` sert à préparer une contribution.
@@ -217,7 +234,7 @@ Il faut choisir:
La contribution crée une décision de groupe. Elle doit ensuite être approuvée et traitée par la trésorerie avant de modifier les soldes.
-## 16. Postes à pourvoir
+## 17. Postes à pourvoir
Les postes disponibles sont affichés en tuiles. En ouvrant une tuile, le membre voit:
@@ -228,7 +245,7 @@ Les postes disponibles sont affichés en tuiles. En ouvrant une tuile, le membre
`Postuler` sert pour soi-même. `Proposer` permet à un exécutif de proposer un autre membre.
-## 17. Gestion des membres
+## 18. Gestion des membres
Les exécutifs peuvent:
@@ -240,7 +257,7 @@ Les exécutifs peuvent:
Sysadmin peut impersonifier un membre pour aider ou vérifier un problème.
-## 18. Gestion des postes et mandats
+## 19. Gestion des postes et mandats
`Gestion des postes` permet de définir les postes et leurs permissions.
@@ -248,15 +265,15 @@ Sysadmin peut impersonifier un membre pour aider ou vérifier un problème.
Une affectation produit une décision de type `Nomination` avec proposeur et secondeur.
-## 19. Littérature et jetons
+## 20. Littérature et jetons
Les membres peuvent consulter l'inventaire de littérature. Les responsables peuvent ajuster les stocks. Les ventes diminuent les stocks; les remises statistiques de jetons ne les diminuent pas.
-## 20. Événements
+## 21. Événements
Le module `Événements` affiche les événements passés et futurs. Les filtres peuvent être combinés.
-## 21. Rapports PDF et mode papier
+## 22. Rapports PDF et mode papier
Les rapports PDF servent à imprimer ou conserver:
@@ -268,11 +285,11 @@ Les rapports PDF servent à imprimer ou conserver:
`Mode papier` soutient une reprise en main si l'informatique n'est pas disponible.
-## 22. Journal
+## 23. Journal
Le journal affiche les changements enregistrés. Une ligne condensée montre date, heure et domaine; l'ouverture donne les détails. Il sert à comprendre ce qui a changé, pas à surveiller les membres.
-## 23. Gestion des groupes
+## 24. Gestion des groupes
Sur la page de sélection des groupes, le bouton discret `Sysadmin` demande le PIN sysadmin. Il ouvre la gestion des groupes.
@@ -285,4 +302,3 @@ Sysadmin peut:
- réinitialiser le groupe de démonstration.
`Groupe Démonstration` est toujours présent sur une installation vanille et ne peut pas être supprimé.
-
diff --git a/docs/50_MODELE_DONNEES.md b/docs/50_MODELE_DONNEES.md
index 31c9d47..d7a3e4b 100644
--- a/docs/50_MODELE_DONNEES.md
+++ b/docs/50_MODELE_DONNEES.md
@@ -42,7 +42,7 @@ Ce document décrit les tables SQLAlchemy de l'application et leur rôle. Il ne
| `reunions` | Rencontres hebdomadaires et assemblées d'affaires. |
| `presences` | Présences rattachées à une réunion. |
| `pv_reunions` | Procès-verbaux préparés ou adoptés. |
-| `rapport_rsg` / `rapports_rsg` | Rapports RSG. |
+| `rapport_rsg` / `rapports_rsg` | Renseignements de service portés par le RSG: district, région, sujets au groupe, positions transmises et suivis. |
| `rapports_adoptes` | Rapports cristallisés après adoption. |
## Tables de gouvernance
@@ -108,4 +108,3 @@ Le démarrage supprime les anciennes tables devenues obsolètes:
- Une relation 1:1 ne justifie pas à elle seule la fusion de deux tables. Il faut aussi vérifier le cycle de vie, le niveau d'accès, l'historisation, la fréquence d'usage et la signification métier.
- Les caches de montant ou de statut ne doivent pas remplacer les registres d'origine.
- Toute nouvelle table métier doit documenter son rattachement à un groupe.
-
diff --git a/frontend/src/pages/Accueil.jsx b/frontend/src/pages/Accueil.jsx
index 639f52d..6e7e84c 100644
--- a/frontend/src/pages/Accueil.jsx
+++ b/frontend/src/pages/Accueil.jsx
@@ -28,7 +28,7 @@ const CONTEXTES = {
description: "État général et consultations courantes.",
sections: [
{ titre: 'À faire maintenant', codes: ['S5', 'C5', 'C9'] },
- { titre: 'État', codes: ['S1T', 'C2', 'C1', 'C4'] },
+ { titre: 'État', codes: ['S1T', 'M3', 'C2', 'C1', 'C4'] },
{ titre: 'Mon espace', codes: ['C7', 'C11', 'C13', 'C12'] },
],
},
@@ -48,6 +48,7 @@ const CONTEXTES = {
sections: [
{ titre: 'Finances', codes: ['S1T', 'S5', 'M4'] },
{ titre: 'Membres et structure', codes: ['G14', 'G2', 'G11', 'G12', 'G9', 'G13'] },
+ { titre: 'Services', codes: ['M3'] },
{ titre: 'Inventaire', codes: ['M5'] },
],
},
diff --git a/frontend/src/pages/GererPostes.jsx b/frontend/src/pages/GererPostes.jsx
index 9a3b548..85e3983 100644
--- a/frontend/src/pages/GererPostes.jsx
+++ b/frontend/src/pages/GererPostes.jsx
@@ -12,7 +12,7 @@ const MODULES = [
['S0', 'Rencontres'], ['M0', "Assemblée d'affaires"],
['C5', 'Gouvernance'],
['S1T', 'Trésorerie'], ['M4', 'Donner au suivant'],
- ['M1', 'Rédiger PV'], ['M2', 'Présences'], ['M3', 'Rapport RSG'],
+ ['M1', 'Rédiger PV'], ['M2', 'Présences'], ['M3', 'Services'],
['S2', 'Remise jeton'], ['M6', 'Commander jetons'],
['S3', 'Vente littérature'], ['M5', 'Ajuster inventaire'],
['G2', 'Gestion membres'], ['G9', 'Paramètres'],
diff --git a/frontend/src/pages/RapportRSG.jsx b/frontend/src/pages/RapportRSG.jsx
index b214d73..c2504a9 100644
--- a/frontend/src/pages/RapportRSG.jsx
+++ b/frontend/src/pages/RapportRSG.jsx
@@ -3,151 +3,200 @@ import { api } from '../stores/api';
import PageHeader from '../components/PageHeader';
import SuccessCheck from '../components/SuccessCheck';
+const champsTexte = [
+ ['infos_district', 'Informations du district', 'Informations reçues au district, comités, besoins de service...'],
+ ['infos_region', 'Informations de la région', "Nouvelles de la région, ateliers, assemblées, documents utiles..."],
+ ['sujets_groupe', 'Sujets à rapporter au groupe', "Questions, consultations ou points à amener à la prochaine assemblée d'affaires..."],
+ ['positions_groupe', 'Positions du groupe à transmettre', 'Décisions, orientations ou messages que le RSG doit porter au district...'],
+ ['suivis', 'Suivis à faire', 'Actions à ne pas oublier, personnes à relancer, documents à apporter...'],
+ ['prochaine_echeance', 'Prochaine échéance', 'Date, rencontre ou action attendue...'],
+ ['liens_utiles', 'Liens ou références utiles', 'Liens, noms de documents, coordonnées ou références à conserver...'],
+];
+
+function formatDate(valeur) {
+ if (!valeur) return '';
+ return new Date(`${valeur}T12:00:00`).toLocaleDateString('fr-CA', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ });
+}
+
+function SectionLecture({ titre, children }) {
+ if (!children) return null;
+ return (
+ {titre}
+
Renseigné par
+{rapport.redacteur_prenom}
+{decision}
+ ))} +Réunion du {viewRapport.reunion_dist}
-Rédigé par {viewRapport.redacteur_prenom}
-• {d}
-{viewRapport.infos_region}
-{viewRapport.sujets_groupe}
-Chargement...
+ ) : rapports.length === 0 ? ( +Aucun renseignement de service enregistré.
+ ) : ( +{error}
} -