jeremy 4 months ago
parent
commit
ba0602171e
4 changed files with 12 additions and 34 deletions
  1. 0 4
      .env.example
  2. 9 22
      backend/src/routes/irl.js
  3. 0 2
      docker-compose.yml
  4. 3 6
      frontend/src/pages/LeaseDetail.jsx

+ 0 - 4
.env.example

@@ -16,7 +16,3 @@ OIDC_REDIRECT_URI=http://localhost:3001/api/auth/oidc/callback
 OIDC_SCOPE=openid email profile
 OIDC_SCOPE=openid email profile
 OIDC_BUTTON_LABEL=Se connecter via SSO
 OIDC_BUTTON_LABEL=Se connecter via SSO
 FRONTEND_URL=http://localhost:3000
 FRONTEND_URL=http://localhost:3000
-
-# INSEE API (optionnel — pour récupérer l'IRL automatiquement)
-# Obtenir un token sur https://api.insee.fr/catalogue/
-INSEE_API_TOKEN=

+ 9 - 22
backend/src/routes/irl.js

@@ -7,31 +7,22 @@ const router = express.Router();
 const IRL_SERIES = '001515333';
 const IRL_SERIES = '001515333';
 
 
 async function fetchIrlFromInsee(year, quarter) {
 async function fetchIrlFromInsee(year, quarter) {
-  const token = process.env.INSEE_API_TOKEN;
-  if (!token) return null;
-
   const period = `${year}-Q${quarter}`;
   const period = `${year}-Q${quarter}`;
   const url = `https://api.insee.fr/series/BDM/V1/data/SERIES_BDM/${IRL_SERIES}?startPeriod=${period}&endPeriod=${period}`;
   const url = `https://api.insee.fr/series/BDM/V1/data/SERIES_BDM/${IRL_SERIES}?startPeriod=${period}&endPeriod=${period}`;
 
 
   const resp = await fetch(url, {
   const resp = await fetch(url, {
-    headers: {
-      'Authorization': `Bearer ${token}`,
-      'Accept': 'application/json',
-    },
+    headers: { 'Accept': 'application/xml' },
   });
   });
 
 
-  if (!resp.ok) throw new Error(`INSEE API ${resp.status}`);
+  if (!resp.ok) throw new Error(`INSEE API HTTP ${resp.status}`);
 
 
-  const data = await resp.json();
+  const xml = await resp.text();
 
 
-  // SDMX-JSON parsing
-  const seriesKey = Object.keys(data.dataSets[0].series)[0];
-  const observations = data.dataSets[0].series[seriesKey].observations;
-  const obsKey = Object.keys(observations)[0];
-  const value = observations[obsKey][0];
+  // SDMX-XML : extraire <generic:ObsValue value="142.06"/>
+  const match = xml.match(/ObsValue[^/]*value="([\d.]+)"/);
+  if (!match) throw new Error('Valeur IRL introuvable dans la réponse XML');
 
 
-  if (value === undefined || value === null) throw new Error('Valeur IRL introuvable pour cette période');
-  return parseFloat(value);
+  return parseFloat(match[1]);
 }
 }
 
 
 // GET /api/irl?year=2024&quarter=2
 // GET /api/irl?year=2024&quarter=2
@@ -39,16 +30,12 @@ router.get('/', authMiddleware, async (req, res) => {
   const { year, quarter } = req.query;
   const { year, quarter } = req.query;
   if (!year || !quarter) return res.status(400).json({ error: 'year et quarter requis' });
   if (!year || !quarter) return res.status(400).json({ error: 'year et quarter requis' });
 
 
-  const configured = !!process.env.INSEE_API_TOKEN;
-
-  if (!configured) return res.json({ value: null, configured: false });
-
   try {
   try {
     const value = await fetchIrlFromInsee(parseInt(year), parseInt(quarter));
     const value = await fetchIrlFromInsee(parseInt(year), parseInt(quarter));
-    res.json({ value, configured: true, year: parseInt(year), quarter: parseInt(quarter) });
+    res.json({ value, year: parseInt(year), quarter: parseInt(quarter) });
   } catch (e) {
   } catch (e) {
     console.error('IRL fetch error:', e.message);
     console.error('IRL fetch error:', e.message);
-    res.json({ value: null, configured: true, error: e.message });
+    res.status(502).json({ value: null, error: e.message });
   }
   }
 });
 });
 
 

+ 0 - 2
docker-compose.yml

@@ -37,8 +37,6 @@ services:
       - OIDC_SCOPE=${OIDC_SCOPE:-openid email profile}
       - OIDC_SCOPE=${OIDC_SCOPE:-openid email profile}
       - OIDC_BUTTON_LABEL=${OIDC_BUTTON_LABEL:-Se connecter via SSO}
       - OIDC_BUTTON_LABEL=${OIDC_BUTTON_LABEL:-Se connecter via SSO}
       - FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
       - FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
-      # INSEE API (optionnel — pour récupérer l'IRL automatiquement)
-      - INSEE_API_TOKEN=${INSEE_API_TOKEN:-}
     depends_on:
     depends_on:
       - postgres
       - postgres
     restart: unless-stopped
     restart: unless-stopped

+ 3 - 6
frontend/src/pages/LeaseDetail.jsx

@@ -83,7 +83,7 @@ export default function LeaseDetail() {
       setIrlCurrent(s => ({ ...s, loading: true, quarter: q, year: y }))
       setIrlCurrent(s => ({ ...s, loading: true, quarter: q, year: y }))
       try {
       try {
         const { data } = await api.get(`/irl?year=${y}&quarter=${q}`)
         const { data } = await api.get(`/irl?year=${y}&quarter=${q}`)
-        setIrlCurrent({ value: data.value, loading: false, quarter: q, year: y, configured: data.configured })
+        setIrlCurrent({ value: data.value, loading: false, quarter: q, year: y })
         // Pré-calculer le loyer si IRL de référence connu
         // Pré-calculer le loyer si IRL de référence connu
         if (data.value && lease.irl_reference_value) {
         if (data.value && lease.irl_reference_value) {
           const ratio = data.value / lease.irl_reference_value
           const ratio = data.value / lease.irl_reference_value
@@ -798,7 +798,7 @@ export default function LeaseDetail() {
                       ? <p className="text-indigo-500 text-xs">⏳ Récupération…</p>
                       ? <p className="text-indigo-500 text-xs">⏳ Récupération…</p>
                       : irlCurrent.value !== null
                       : irlCurrent.value !== null
                         ? <p className="font-bold text-indigo-700">{irlCurrent.value}</p>
                         ? <p className="font-bold text-indigo-700">{irlCurrent.value}</p>
-                        : <p className="text-gray-400 text-xs">{irlCurrent.configured === false ? 'INSEE non configuré' : 'Indice non disponible'}</p>}
+                        : <p className="text-gray-400 text-xs">Indice non disponible pour cette période</p>}
                   </div>
                   </div>
                 </div>
                 </div>
                 {irlCurrent.value && lease.irl_reference_value && (
                 {irlCurrent.value && lease.irl_reference_value && (
@@ -807,9 +807,6 @@ export default function LeaseDetail() {
                     {' '}— loyer calculé : <strong>{fmt(lease.rent_amount * irlCurrent.value / lease.irl_reference_value)}</strong>
                     {' '}— loyer calculé : <strong>{fmt(lease.rent_amount * irlCurrent.value / lease.irl_reference_value)}</strong>
                   </p>
                   </p>
                 )}
                 )}
-                {!irlCurrent.configured && (
-                  <p className="text-xs text-gray-400">Configurez <code>INSEE_API_TOKEN</code> pour la récupération automatique.</p>
-                )}
               </div>
               </div>
             ) : (
             ) : (
               <div className="bg-gray-50 border border-gray-200 rounded-xl p-3 text-xs text-gray-400">
               <div className="bg-gray-50 border border-gray-200 rounded-xl p-3 text-xs text-gray-400">
@@ -941,7 +938,7 @@ export default function LeaseDetail() {
                           try {
                           try {
                             const { data } = await api.get(`/irl?year=${leaseForm.irl_reference_year}&quarter=${leaseForm.irl_reference_quarter}`)
                             const { data } = await api.get(`/irl?year=${leaseForm.irl_reference_year}&quarter=${leaseForm.irl_reference_quarter}`)
                             if (data.value) setLeaseForm(f => ({ ...f, irl_reference_value: String(data.value) }))
                             if (data.value) setLeaseForm(f => ({ ...f, irl_reference_value: String(data.value) }))
-                            else toast.error(data.configured === false ? 'INSEE non configuré (INSEE_API_TOKEN)' : 'IRL indisponible pour cette période')
+                            else toast.error('IRL indisponible pour cette période')
                           } catch { toast.error('Erreur récupération IRL') }
                           } catch { toast.error('Erreur récupération IRL') }
                         }}
                         }}
                         className="px-2 py-1 bg-indigo-100 hover:bg-indigo-200 text-indigo-700 rounded-lg text-sm transition-colors whitespace-nowrap">
                         className="px-2 py-1 bg-indigo-100 hover:bg-indigo-200 text-indigo-700 rounded-lg text-sm transition-colors whitespace-nowrap">