mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-20 21:48:33 +02:00
Fournisseur : interroger la dernière entrée de chaque magasin
Le repli sur l'API FF n'a rien trouvé pour 503996 : /dernier-fournisseur appelé sans magasin ne renvoie pas l'entrée de Houdemont. Seul `?site=579` donne P024 PUCKATOR BV (entrée du 29/09/2026), et la fiche de l'article n'a aucun fournisseur. On interroge désormais l'appel sans magasin, `?site=292` et `?site=579`, et on retient l'entrée la plus récente. On choisit aussi, parmi les articles renvoyés par /api/articles?codein=, celui dont le codein est bien celui demandé. Le tableau de bord en profite (même fonction). Le test reproduit la réponse de l'API pour 503996 ; il échoue sur l'ancien code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhSZx8CCuqoKrDw7unFqGq
This commit is contained in:
2 files changed
+56
-23
No files matched your search
+43
-18
@@ -780,8 +780,11 @@ async fn enrichir_article_dashboard(base: &str, codein: &str, out: &mut Enrichis
|
||||
return Ok(());
|
||||
}
|
||||
let art_data: Value = art_res.json().await?;
|
||||
// L'article dont le codein est bien celui demandé, sinon le premier renvoyé.
|
||||
let first = match get(&art_data, "articles") {
|
||||
Some(Value::Array(a)) if !a.is_empty() => a[0].clone(),
|
||||
Some(Value::Array(a)) if !a.is_empty() => {
|
||||
a.iter().find(|x| str_field(x, "codein").trim() == codein.trim()).unwrap_or(&a[0]).clone()
|
||||
}
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let no_id = match get(&first, "no_id") {
|
||||
@@ -791,9 +794,8 @@ async fn enrichir_article_dashboard(base: &str, codein: &str, out: &mut Enrichis
|
||||
|
||||
let url_ref = format!("{base}/api/articles/{}/referentiel", encode(&no_id));
|
||||
let url_dernier = format!("{base}/api/articles/{}/dernier-fournisseur", encode(&no_id));
|
||||
let (ref_res, dernier_res) = tokio::join!(ff_get(&url_ref), ff_get(&url_dernier));
|
||||
let (ref_res, dernier) = tokio::join!(ff_get(&url_ref), fournisseur_derniere_entree(&url_dernier));
|
||||
let ref_res = ref_res?;
|
||||
let dernier_res = dernier_res?;
|
||||
|
||||
// Stock (toujours depuis referentiel)
|
||||
let mut ref_fournisseur_fallback = String::new();
|
||||
@@ -832,25 +834,48 @@ async fn enrichir_article_dashboard(base: &str, codein: &str, out: &mut Enrichis
|
||||
}
|
||||
}
|
||||
|
||||
// Fournisseur réel : /dernier-fournisseur (dernière entrée en stock par site)
|
||||
// Fallback : premier non-dépôt du référentiel
|
||||
if dernier_res.status().is_success() {
|
||||
let dernier_data: Value = dernier_res.json().await?;
|
||||
// Prendre le fournisseur du premier site disponible
|
||||
let first = arr(&dernier_data, "fournisseurs_par_site")
|
||||
.iter()
|
||||
.find(|e| get(e, "nom_fournisseur").map(truthy).unwrap_or(false));
|
||||
if let Some(e) = first {
|
||||
out.fournisseur = Some(str_field(e, "nom_fournisseur").trim().to_string());
|
||||
} else if !ref_fournisseur_fallback.is_empty() {
|
||||
out.fournisseur = Some(ref_fournisseur_fallback);
|
||||
}
|
||||
} else if !ref_fournisseur_fallback.is_empty() {
|
||||
out.fournisseur = Some(ref_fournisseur_fallback);
|
||||
// Fournisseur réel : /dernier-fournisseur (dernière entrée en stock),
|
||||
// sinon le fournisseur du référentiel.
|
||||
match dernier? {
|
||||
Some(f) => out.fournisseur = Some(f),
|
||||
None if !ref_fournisseur_fallback.is_empty() => out.fournisseur = Some(ref_fournisseur_fallback),
|
||||
None => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fournisseur de la dernière entrée en stock d'un article, tous magasins
|
||||
/// confondus : la plus récente (`date_derniere_entree`), sinon la première.
|
||||
///
|
||||
/// Sans `?site=`, l'API ne renvoie pas toujours tous les magasins : pour le
|
||||
/// codein 503996, seule `?site=579` donne l'entrée (P024 PUCKATOR BV, reçue
|
||||
/// à Houdemont). On interroge donc aussi chaque magasin.
|
||||
async fn fournisseur_derniere_entree(url: &str) -> anyhow::Result<Option<String>> {
|
||||
let urls = [url.to_string(), format!("{url}?site=292"), format!("{url}?site=579")];
|
||||
let reponses = futures::future::join_all(urls.iter().map(|u| ff_get(u))).await;
|
||||
let mut meilleur: Option<(String, String)> = None; // (date, fournisseur)
|
||||
for res in reponses {
|
||||
let res = res?;
|
||||
if !res.status().is_success() {
|
||||
continue;
|
||||
}
|
||||
let data: Value = res.json().await?;
|
||||
// Entrées sous `fournisseurs_par_site`, ou l'objet lui-même.
|
||||
let entrees = arr(&data, "fournisseurs_par_site").iter().chain(std::iter::once(&data));
|
||||
for e in entrees {
|
||||
let nom = str_field(e, "nom_fournisseur").trim().to_string();
|
||||
if nom.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let date = str_field(e, "date_derniere_entree");
|
||||
if meilleur.as_ref().is_none_or(|(d, _)| date > *d) {
|
||||
meilleur = Some((date, nom));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(meilleur.map(|(_, f)| f))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fournisseur manquant — repli sur l'API FF en direct
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -108,7 +108,7 @@ async fn article_sans_fournisseur_principal() {
|
||||
let mut hp = hp;
|
||||
completer_fournisseurs(&mut hp, |r| (r.codein.as_str(), &mut r.fournisseur)).await;
|
||||
for r in &hp {
|
||||
let voulu = if r.codein == "100003" { "FOURNISSEUR API" } else { attendu(&r.codein) };
|
||||
let voulu = if r.codein == "100003" { "PUCKATOR BV" } else { attendu(&r.codein) };
|
||||
assert_eq!(r.fournisseur, voulu, "hit parade complété {}", r.codein);
|
||||
}
|
||||
}
|
||||
@@ -126,13 +126,21 @@ async fn fausse_api() -> String {
|
||||
_ => Json(json!({ "articles": [] })),
|
||||
}
|
||||
}
|
||||
async fn referentiel(Path(_id): Path<String>) -> Json<Value> {
|
||||
Json(json!({ "stock": [], "fournisseurs": [{ "codefou": "F9", "nom_fou": " FOURNISSEUR API ", "preference": 0 }] }))
|
||||
// Comme pour le codein 503996 : fiche sans fournisseur, et seule la
|
||||
// dernière entrée de Houdemont (`?site=579`) donne le fournisseur.
|
||||
async fn dernier(Path(_id): Path<String>, Query(q): Query<HashMap<String, String>>) -> Json<Value> {
|
||||
match q.get("site").map(|s| s.as_str()) {
|
||||
Some("579") => Json(json!({ "fournisseurs_par_site": [{
|
||||
"site": "579", "codefou": "P024", "nom_fournisseur": "PUCKATOR BV ",
|
||||
"date_derniere_entree": "2026-09-29", "qte_entree": 240, "prmp": 1.16
|
||||
}] })),
|
||||
_ => Json(json!({ "fournisseurs_par_site": [] })),
|
||||
}
|
||||
}
|
||||
let app = axum::Router::new()
|
||||
.route("/api/articles", get(articles))
|
||||
.route("/api/articles/{id}/referentiel", get(referentiel))
|
||||
.route("/api/articles/{id}/dernier-fournisseur", get(|| async { Json(json!({ "fournisseurs_par_site": [] })) }));
|
||||
.route("/api/articles/{id}/referentiel", get(|| async { Json(json!({ "stock": [], "fournisseurs": [] })) }))
|
||||
.route("/api/articles/{id}/dernier-fournisseur", get(dernier));
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
|
||||
|
||||
Reference in new issue
Block a user