Encadrer corrections de PV adoptes

This commit is contained in:
Daniel Allaire 2026-06-01 20:50:22 -04:00
parent 14fa8b4174
commit e1b80793ee
3 changed files with 62 additions and 1 deletions

View file

@ -121,6 +121,13 @@ async def modifier_pv(
pv = result.scalar_one_or_none() pv = result.scalar_one_or_none()
if not pv: if not pv:
raise HTTPException(status_code=404, detail="PV introuvable") raise HTTPException(status_code=404, detail="PV introuvable")
if pv.approuve:
raise HTTPException(
status_code=400,
detail="Un PV adopté ne peut pas être modifié directement. Inscrivez la correction au PV d'une assemblée suivante.",
)
if req.approuve is not None:
raise HTTPException(status_code=400, detail="Utilisez le processus d'adoption du PV")
reunion = await db.get(Reunion, pv.reunion_id) reunion = await db.get(Reunion, pv.reunion_id)
if reunion: if reunion:
await verifier_reunion_non_cristallisee( await verifier_reunion_non_cristallisee(

View file

@ -165,6 +165,8 @@ Le rapport du trésorier doit être adopté avant de décider du montant des con
Les rapports cristallisés sont téléchargés depuis l'icône de rapport sur les tuiles concernées. Les rapports cristallisés sont téléchargés depuis l'icône de rapport sur les tuiles concernées.
Un PV adopté ne se modifie pas directement. Si une correction est demandée après adoption, elle est inscrite au PV d'une assemblée suivante; le PV original demeure inchangé.
## 13. Gouvernance ## 13. Gouvernance
Le module `Gouvernance` rassemble les propositions, réflexions en cours, rejets et décisions. Le module `Gouvernance` rassemble les propositions, réflexions en cours, rejets et décisions.

View file

@ -32,6 +32,7 @@ export default function AssembleeAffaires({ contexte = false }) {
const [contenu, setContenu] = useState(''); const [contenu, setContenu] = useState('');
const [pvPrecedent, setPvPrecedent] = useState(null); const [pvPrecedent, setPvPrecedent] = useState(null);
const [pvCorrection, setPvCorrection] = useState(''); const [pvCorrection, setPvCorrection] = useState('');
const [correctionPvAdopte, setCorrectionPvAdopte] = useState('');
const [adoption, setAdoption] = useState({ proposeur_id: '', secondeur_id: '' }); const [adoption, setAdoption] = useState({ proposeur_id: '', secondeur_id: '' });
const [adoptionAssemblee, setAdoptionAssemblee] = useState({ proposeur_id: '', secondeur_id: '' }); const [adoptionAssemblee, setAdoptionAssemblee] = useState({ proposeur_id: '', secondeur_id: '' });
const [don, setDon] = useState({ destination: 'District 87-16', montant: '', methode: 'VIREMENT', proposeur_id: '', secondeur_id: '' }); const [don, setDon] = useState({ destination: 'District 87-16', montant: '', methode: 'VIREMENT', proposeur_id: '', secondeur_id: '' });
@ -118,6 +119,7 @@ export default function AssembleeAffaires({ contexte = false }) {
const precedent = precedents[0] || null; const precedent = precedents[0] || null;
setPvPrecedent(precedent); setPvPrecedent(precedent);
setPvCorrection(precedent?.contenu || ''); setPvCorrection(precedent?.contenu || '');
setCorrectionPvAdopte('');
setContenu(pvJour?.contenu || textePvAuto(precedent, propositions, resolutionsJour, postesVacants)); setContenu(pvJour?.contenu || textePvAuto(precedent, propositions, resolutionsJour, postesVacants));
}, [ouverteId, pvs.length, propositions.length, resolutions.length, postesVacants.length]); }, [ouverteId, pvs.length, propositions.length, resolutions.length, postesVacants.length]);
@ -324,6 +326,33 @@ export default function AssembleeAffaires({ contexte = false }) {
} }
}; };
const inscrireCorrectionPvAdopte = async () => {
const texte = correctionPvAdopte.trim();
if (!reunionOuverte || !pvPrecedent || !texte) {
setError('Correction requise');
return;
}
const datePv = pvPrecedent.date_reunion?.slice(0, 10) || 'date inconnue';
const ajout = `Correction au procès-verbal du ${datePv}:\n${texte}`;
const nouveauContenu = [contenu.trim(), ajout].filter(Boolean).join('\n\n');
setLoading(true);
setError('');
try {
await api.post('/pv/', {
reunion_id: reunionOuverte.id,
contenu: nouveauContenu,
decisions: null,
});
setContenu(nouveauContenu);
setCorrectionPvAdopte('');
setMsg('Correction inscrite au PV courant');
await charger();
} catch (e) {
setError(e.message);
}
setLoading(false);
};
const adopterAssemblee = async () => { const adopterAssemblee = async () => {
if (!reunionOuverte) return; if (!reunionOuverte) return;
if (!adoptionAssemblee.proposeur_id || !adoptionAssemblee.secondeur_id) { if (!adoptionAssemblee.proposeur_id || !adoptionAssemblee.secondeur_id) {
@ -438,7 +467,30 @@ export default function AssembleeAffaires({ contexte = false }) {
<button onClick={adopterPvPrecedent} disabled={!adoption.proposeur_id || !adoption.secondeur_id} className="w-full p-3 bg-green-50 text-green-700 rounded-xl text-sm font-bold border border-green-100 disabled:opacity-40">Adopter</button> <button onClick={adopterPvPrecedent} disabled={!adoption.proposeur_id || !adoption.secondeur_id} className="w-full p-3 bg-green-50 text-green-700 rounded-xl text-sm font-bold border border-green-100 disabled:opacity-40">Adopter</button>
</div> </div>
) : ( ) : (
<p className="text-sm whitespace-pre-wrap text-gray-700">{pvPrecedent.contenu}</p> <div className="space-y-3">
<p className="text-sm whitespace-pre-wrap text-gray-700">{pvPrecedent.contenu}</p>
{estExecutif && pvPrecedent.approuve && (
<div className="space-y-2 border-t border-gray-100 pt-3">
<p className="text-xs text-gray-500">
Ce PV est adopté et ne peut pas être modifié. Une correction sera inscrite au PV courant.
</p>
<textarea
value={correctionPvAdopte}
onChange={(e) => setCorrectionPvAdopte(e.target.value)}
rows={3}
placeholder="Correction à inscrire..."
className={`${INPUT} resize-none text-sm`}
/>
<button
onClick={inscrireCorrectionPvAdopte}
disabled={loading || !correctionPvAdopte.trim()}
className="w-full p-3 bg-blue-50 text-blue-700 rounded-xl text-sm font-bold border border-blue-100 disabled:opacity-40"
>
Inscrire au PV courant
</button>
</div>
)}
</div>
)} )}
</Card> </Card>
)} )}