mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-21 14:08:08 +02:00
feat: implement API clients for Nancy FF data sync and add local configuration settings
This commit is contained in:
1 parent
a06d7cd7e8
commit
df1d059952
3 files changed
+51
-6
No files matched your search
@@ -10,7 +10,10 @@
|
|||||||
"Bash(rtk git *)",
|
"Bash(rtk git *)",
|
||||||
"Bash(rtk npx *)",
|
"Bash(rtk npx *)",
|
||||||
"Bash(git add *)",
|
"Bash(git add *)",
|
||||||
"Bash(git commit *)"
|
"Bash(git commit *)",
|
||||||
|
"Bash(python -c \"import sys,json; d=json.load\\(sys.stdin\\); items=d.get\\('top10_ca',[]\\); print\\('top10_ca fields on first item:', list\\(items[0].keys\\(\\)\\) if items else 'EMPTY'\\); print\\('first item:', json.dumps\\(items[0], ensure_ascii=False, indent=2\\) if items else 'EMPTY'\\)\")",
|
||||||
|
"Bash(node -e \"let d=''; process.stdin.on\\('data',c=>d+=c\\); process.stdin.on\\('end',\\(\\)=>{ const j=JSON.parse\\(d\\); const items=j.top10_ca||[]; if\\(items.length\\) { console.log\\('KEYS:', Object.keys\\(items[0]\\).join\\(', '\\)\\); console.log\\('FIRST:', JSON.stringify\\(items[0], null, 2\\)\\); } 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 items=Array.isArray\\(j\\)?j:\\(j.data||j.items||j.mouvements||j.list||[]\\); if\\(items.length\\) { console.log\\('KEYS:', Object.keys\\(items[0]\\).join\\(', '\\)\\); console.log\\('FIRST:', JSON.stringify\\(items[0], null, 2\\)\\); } else { console.log\\('STRUCTURE:', Object.keys\\(j\\)\\); console.log\\('RAW:', JSON.stringify\\(j\\).slice\\(0,500\\)\\); } }\\)\")"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,6 +293,35 @@ export async function getMouvementsByFournisseur(
|
|||||||
})).filter(m => m.codein && m.datemvt);
|
})).filter(m => m.codein && m.datemvt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retourne les mouvements pour une plage de dates (sans filtre fournisseur).
|
||||||
|
* Utilisé pour extraire codefou_reel / nom_fournisseur_reel par codein.
|
||||||
|
*/
|
||||||
|
export async function getMouvementsForDate(
|
||||||
|
dateDebut: string,
|
||||||
|
dateFin: string
|
||||||
|
): Promise<FfMouvement[]> {
|
||||||
|
const raw = await fetchAllPages<unknown>(
|
||||||
|
(page) =>
|
||||||
|
`${FF_API_BASE}/api/mouvements/articles?dateDebut=${dateDebut}&dateFin=${dateFin}&page=${page}&limit=1000`,
|
||||||
|
extractList,
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
return raw.map((r: any): FfMouvement => ({
|
||||||
|
codein: r.codein ?? r.Codein ?? r.code_article ?? "",
|
||||||
|
genremvt: Number(r.genremvt ?? r.GenreMvt ?? r.genre_mvt ?? r.type ?? 0),
|
||||||
|
datemvt: r.datmvt ?? r.datemvt ?? r.DateMvt ?? r.date_mvt ?? r.date ?? "",
|
||||||
|
qte: Number(r.qtemvt ?? r.qte ?? r.Qte ?? r.quantite ?? 0),
|
||||||
|
montant: Number(r.mntmvtht ?? r.montant ?? r.Montant ?? r.montant_mvt ?? r.ca ?? 0),
|
||||||
|
marge: Number(r.margemvt ?? r.marge ?? r.Marge ?? r.marge_mvt ?? 0),
|
||||||
|
qtestock: Number(r.qtestock ?? r.QteStock ?? r.qte_stock ?? r.stock ?? 0),
|
||||||
|
site: r.site ?? r.Site ?? r.magasin ?? r.code_magasin ?? r.codesite ?? "",
|
||||||
|
codefou_reel: r.codefou_reel ?? undefined,
|
||||||
|
nom_fournisseur_reel: r.nom_fournisseur_reel ?? undefined,
|
||||||
|
})).filter(m => m.codein && m.datemvt);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Mensuel — stock fin de mois + ventes + réceptions par article et par site
|
// Mensuel — stock fin de mois + ventes + réceptions par article et par site
|
||||||
// GET /api/articles/:no_id/mensuel?dateDebut=&dateFin=
|
// GET /api/articles/:no_id/mensuel?dateDebut=&dateFin=
|
||||||
|
|||||||
+18
-5
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import { db } from "@/db";
|
import { db } from "@/db";
|
||||||
import { sql, type SQL } from "drizzle-orm";
|
import { sql, type SQL } from "drizzle-orm";
|
||||||
|
import { getMouvementsForDate } from "@/lib/api-ff-client";
|
||||||
|
|
||||||
// Cache module-level pour éviter les requêtes information_schema répétées
|
// Cache module-level pour éviter les requêtes information_schema répétées
|
||||||
let _nomenclatureParentCol: string | null | undefined = undefined; // undefined = pas encore chargé
|
let _nomenclatureParentCol: string | null | undefined = undefined; // undefined = pas encore chargé
|
||||||
@@ -603,11 +604,25 @@ export async function pgGetDashboardData(): Promise<DashboardData> {
|
|||||||
];
|
];
|
||||||
const allCodeins = [...new Set(allItems.map(i => i.codein).filter(Boolean))];
|
const allCodeins = [...new Set(allItems.map(i => i.codein).filter(Boolean))];
|
||||||
|
|
||||||
// Enrichissement stock uniquement — le fournisseur réel vient maintenant directement
|
// Fournisseur réel : un seul appel sur les mouvements du jour (codefou_reel / nom_fournisseur_reel).
|
||||||
// des champs codefou_reel / nom_fournisseur_reel retournés par l'API mouvements.
|
// Stock : enrichissement via /referentiel par article (toujours nécessaire).
|
||||||
|
let fouMap = new Map<string, string>();
|
||||||
let stockMap = new Map<string, { stock292: number; stock579: number; stockTotal: number }>();
|
let stockMap = new Map<string, { stock292: number; stock579: number; stockTotal: number }>();
|
||||||
const FF_API_BASE = process.env.FF_API_BASE_URL ?? "https://api.ffnancy.fr";
|
const FF_API_BASE = process.env.FF_API_BASE_URL ?? "https://api.ffnancy.fr";
|
||||||
|
|
||||||
|
const [mouvements] = await Promise.allSettled([
|
||||||
|
getMouvementsForDate(dateHier, dateHier),
|
||||||
|
]);
|
||||||
|
if (mouvements.status === "fulfilled") {
|
||||||
|
for (const m of mouvements.value) {
|
||||||
|
if (!fouMap.has(m.codein) && (m.nom_fournisseur_reel || m.codefou_reel)) {
|
||||||
|
fouMap.set(m.codein, m.nom_fournisseur_reel?.trim() || m.codefou_reel!.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn("[Dashboard] getMouvementsForDate failed:", mouvements.reason);
|
||||||
|
}
|
||||||
|
|
||||||
if (allCodeins.length > 0) {
|
if (allCodeins.length > 0) {
|
||||||
try {
|
try {
|
||||||
for (let i = 0; i < allCodeins.length; i += 5) {
|
for (let i = 0; i < allCodeins.length; i += 5) {
|
||||||
@@ -655,9 +670,7 @@ export async function pgGetDashboardData(): Promise<DashboardData> {
|
|||||||
const stock = i.site === "292" ? (stockEntry?.stock292 ?? 0)
|
const stock = i.site === "292" ? (stockEntry?.stock292 ?? 0)
|
||||||
: i.site === "579" ? (stockEntry?.stock579 ?? 0)
|
: i.site === "579" ? (stockEntry?.stock579 ?? 0)
|
||||||
: (stockEntry?.stockTotal ?? 0);
|
: (stockEntry?.stockTotal ?? 0);
|
||||||
const fournisseur = i.nom_fournisseur_reel?.trim()
|
const fournisseur = fouMap.get(i.codein) || "—";
|
||||||
|| i.codefou_reel?.trim()
|
|
||||||
|| "—";
|
|
||||||
return {
|
return {
|
||||||
codein: i.codein,
|
codein: i.codein,
|
||||||
libelle1: i.libelle1,
|
libelle1: i.libelle1,
|
||||||
|
|||||||
Reference in new issue
Block a user