mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-21 22:18:08 +02:00
feat: implement bulk PostgreSQL client for FF Nancy data retrieval and configure local environment permissions
This commit is contained in:
1 parent
0ad403651d
commit
26ba285c0e
2 files changed
+43
-31
No files matched your search
@@ -18,7 +18,8 @@
|
|||||||
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ console.log\\(JSON.stringify\\(JSON.parse\\(d\\), null, 2\\)\\); }\\)\")",
|
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ console.log\\(JSON.stringify\\(JSON.parse\\(d\\), null, 2\\)\\); }\\)\")",
|
||||||
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ const j=JSON.parse\\(d\\); const rows=Array.isArray\\(j\\)?j:\\(j.propositions??j.data??j.items??[]\\); console.log\\('top-level keys:', Object.keys\\(j\\)\\); if\\(rows.length\\){ const s=rows[0]; console.log\\('row keys:', Object.keys\\(s\\).join\\(', '\\)\\); console.log\\('franco_ht:', s.franco_ht, '| franco:', s.franco, '| franco_atteint:', s.franco_atteint\\); } else console.log\\('empty'\\); }\\)\")",
|
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ const j=JSON.parse\\(d\\); const rows=Array.isArray\\(j\\)?j:\\(j.propositions??j.data??j.items??[]\\); console.log\\('top-level keys:', Object.keys\\(j\\)\\); if\\(rows.length\\){ const s=rows[0]; console.log\\('row keys:', Object.keys\\(s\\).join\\(', '\\)\\); console.log\\('franco_ht:', s.franco_ht, '| franco:', s.franco, '| franco_atteint:', s.franco_atteint\\); } else console.log\\('empty'\\); }\\)\")",
|
||||||
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ const j=JSON.parse\\(d\\); console.log\\('top-level keys:', Object.keys\\(j\\)\\); const resume=j.resume; console.log\\('resume type:', Array.isArray\\(resume\\)?'array':'object'\\); if\\(Array.isArray\\(resume\\)&&resume.length\\){ console.log\\('resume[0]:', JSON.stringify\\(resume[0],null,2\\)\\); } else { console.log\\('resume:', JSON.stringify\\(resume,null,2\\)\\); } }\\)\")",
|
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ const j=JSON.parse\\(d\\); console.log\\('top-level keys:', Object.keys\\(j\\)\\); const resume=j.resume; console.log\\('resume type:', Array.isArray\\(resume\\)?'array':'object'\\); if\\(Array.isArray\\(resume\\)&&resume.length\\){ console.log\\('resume[0]:', JSON.stringify\\(resume[0],null,2\\)\\); } else { console.log\\('resume:', JSON.stringify\\(resume,null,2\\)\\); } }\\)\")",
|
||||||
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ const j=JSON.parse\\(d\\); console.log\\('top-level keys:', Object.keys\\(j\\)\\); const resume=j.resume; console.log\\('resume type:', Array.isArray\\(resume\\)?'array':'object'\\); if\\(Array.isArray\\(resume\\)&&resume.length\\){ console.log\\('resume[0]:', JSON.stringify\\(resume[0]\\)\\); } else { console.log\\('resume:', JSON.stringify\\(resume\\)\\); } }\\)\")"
|
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ const j=JSON.parse\\(d\\); console.log\\('top-level keys:', Object.keys\\(j\\)\\); const resume=j.resume; console.log\\('resume type:', Array.isArray\\(resume\\)?'array':'object'\\); if\\(Array.isArray\\(resume\\)&&resume.length\\){ console.log\\('resume[0]:', JSON.stringify\\(resume[0]\\)\\); } else { console.log\\('resume:', JSON.stringify\\(resume\\)\\); } }\\)\")",
|
||||||
|
"Bash(node -e ' *)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+41
-30
@@ -1143,48 +1143,59 @@ export async function pgGetCommandesAuto(): Promise<PgCommandeAutoRow[]> {
|
|||||||
const mapped = (rows as any[]).map(mapCommandeAutoRow);
|
const mapped = (rows as any[]).map(mapCommandeAutoRow);
|
||||||
|
|
||||||
// Enrichissement : pour les fournisseurs sans franco dans la réponse globale,
|
// Enrichissement : pour les fournisseurs sans franco dans la réponse globale,
|
||||||
// on appelle le detail endpoint qui contient toujours franco_ht dans son résumé.
|
// on appelle GET /api/commandes-auto/{codefou} (sans filtre site — franco est une
|
||||||
const missingFranco = mapped.filter(r => r.franco === 0 && r.codefou && r.site);
|
// propriété du fournisseur, pas du site) et on lit resume[0].franco_ht.
|
||||||
|
const missingFranco = mapped.filter(r => r.franco === 0 && r.codefou);
|
||||||
if (missingFranco.length > 0) {
|
if (missingFranco.length > 0) {
|
||||||
console.log(`[pg-ff] pgGetCommandesAuto: ${missingFranco.length} lignes sans franco → enrichissement via detail endpoint`);
|
console.log(`[pg-ff] pgGetCommandesAuto: ${missingFranco.length} lignes sans franco → enrichissement`);
|
||||||
// Regrouper par (codefou, site) pour éviter les doublons
|
// Dédupliquer par codefou (franco identique quel que soit le site)
|
||||||
const pairs = Array.from(
|
const uniqueCodeFous = [...new Set(missingFranco.map(r => r.codefou))];
|
||||||
new Map(missingFranco.map(r => [`${r.codefou}|${r.site}`, r])).values()
|
|
||||||
);
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const extractFranco = (detail: any): number => {
|
||||||
|
if (!detail || detail.error) return 0;
|
||||||
|
const resumeRaw = detail?.resume ?? detail?.summary ?? detail;
|
||||||
|
const resumeItem = Array.isArray(resumeRaw) ? resumeRaw[0] : resumeRaw;
|
||||||
|
return Number(resumeItem?.franco_ht ?? resumeItem?.franco ?? 0);
|
||||||
|
};
|
||||||
|
|
||||||
const details = await Promise.allSettled(
|
const details = await Promise.allSettled(
|
||||||
pairs.map(r =>
|
uniqueCodeFous.map(async codefou => {
|
||||||
fetch(`${FF_API_BASE}/api/commandes-auto/${encodeURIComponent(r.codefou)}?site=${encodeURIComponent(r.site)}`, { cache: "no-store" })
|
// 1. Essai sans filtre site
|
||||||
.then(res2 => res2.ok ? res2.json() : null)
|
try {
|
||||||
.then(detail => {
|
const res2 = await fetch(`${FF_API_BASE}/api/commandes-auto/${encodeURIComponent(codefou)}`, { cache: "no-store" });
|
||||||
if (!detail) return null;
|
if (res2.ok) {
|
||||||
// Le detail endpoint retourne { resume: [...] } (tableau) ou { resume: {} }
|
const detail = await res2.json();
|
||||||
// ou directement les champs du résumé
|
const f = extractFranco(detail);
|
||||||
const resumeRaw = detail?.resume ?? detail?.summary ?? detail;
|
if (f > 0) return { codefou, franco: f };
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
}
|
||||||
const resumeItem: any = Array.isArray(resumeRaw) ? resumeRaw[0] : resumeRaw;
|
} catch { /* ignore */ }
|
||||||
const francoVal = Number(resumeItem?.franco_ht ?? resumeItem?.franco ?? 0);
|
// 2. Fallback site=000
|
||||||
return { codefou: r.codefou, site: r.site, franco: francoVal };
|
try {
|
||||||
})
|
const res3 = await fetch(`${FF_API_BASE}/api/commandes-auto/${encodeURIComponent(codefou)}?site=000`, { cache: "no-store" });
|
||||||
.catch(() => null)
|
if (res3.ok) {
|
||||||
)
|
const detail = await res3.json();
|
||||||
|
const f = extractFranco(detail);
|
||||||
|
if (f > 0) return { codefou, franco: f };
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
return { codefou, franco: 0 };
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Construire un map (codefou|site) → franco enrichi
|
// Map codefou → franco enrichi
|
||||||
const francoMap = new Map<string, number>();
|
const francoMap = new Map<string, number>();
|
||||||
for (const result of details) {
|
for (const result of details) {
|
||||||
if (result.status === "fulfilled" && result.value && result.value.franco > 0) {
|
if (result.status === "fulfilled" && result.value.franco > 0) {
|
||||||
francoMap.set(`${result.value.codefou}|${result.value.site}`, result.value.franco);
|
francoMap.set(result.value.codefou, result.value.franco);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(`[pg-ff] pgGetCommandesAuto: francos enrichis:`, Object.fromEntries(francoMap));
|
console.log(`[pg-ff] pgGetCommandesAuto: francos enrichis:`, Object.fromEntries(francoMap));
|
||||||
|
|
||||||
// Appliquer les francos enrichis
|
|
||||||
return mapped.map(r => {
|
return mapped.map(r => {
|
||||||
if (r.franco === 0) {
|
if (r.franco === 0) {
|
||||||
const enrichedFranco = francoMap.get(`${r.codefou}|${r.site}`);
|
const enriched = francoMap.get(r.codefou);
|
||||||
if (enrichedFranco && enrichedFranco > 0) {
|
if (enriched && enriched > 0) return { ...r, franco: enriched };
|
||||||
return { ...r, franco: enrichedFranco };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user