https://relay.exoconnect.app/VOTRE_CHEMIN).| Table | Route | Liste à développer |
|---|---|---|
| Ventes par mois | GET /api/sales/summary?group_by=month | series |
| Meilleurs clients | GET /api/sales/top-customers | items |
| Ventes et marge par produit | GET /api/products/analytics?group_by=product | buckets, page par page |
| Âge des comptes clients | GET /api/customers/aging | items |
| Paramètre | Type | Valeur |
|---|---|---|
UrlRelais | Texte | https://relay.exoconnect.app/VOTRE_CHEMIN |
CleApi | Texte | votre clé API en lecture |
Appel (Nouvelle source › Requête vide › Éditeur avancé). C'est la fonction que toutes les autres requêtes utiliseront :// Appel : GET sur l'API ExoConnect, réponse JSON décodée.
(chemin as text, optional parametres as record) as any =>
Json.Document(
Web.Contents(
UrlRelais,
[
RelativePath = chemin,
Query = if parametres = null then [] else parametres,
Headers = [#"x-api-key" = CleApi, Accept = "application/json"],
Timeout = #duration(0, 0, 5, 0)
]
)
)RelativePath et Query gardent l'adresse de base fixe : c'est ce qui permet au service Power BI d'actualiser le rapport (étape 4). Les valeurs de Query sont toujours du texte.series.let
Reponse = Appel("api/sales/summary", [from = "2025-01-01", to = "2025-12-31", group_by = "month"]),
Table = Table.FromRecords(Reponse[series]),
Colonnes = Table.SelectColumns(Table, {"bucket", "from", "to", "revenue_net", "subtotal", "taxes", "invoices_count", "active_customers_count"}),
Types = Table.TransformColumnTypes(Colonnes, {
{"from", type date}, {"to", type date},
{"revenue_net", Currency.Type}, {"subtotal", Currency.Type}, {"taxes", Currency.Type},
{"invoices_count", Int64.Type}, {"active_customers_count", Int64.Type}})
in
Typesrevenue_net : ventes taxes comprises, nettes des crédits. subtotal : ventes avant taxes, nettes des crédits ; c'est le chiffre d'affaires à comparer à vos états financiers.group_by accepte aussi day, week, quarter et year.Ventes par mois, puis Fermer et appliquer (Power BI) ou Fermer et charger (Excel).let
Reponse = Appel("api/sales/top-customers", [from = "2025-01-01", to = "2025-12-31", limit = "50"]),
Table = Table.FromRecords(Reponse[items]),
Client = Table.ExpandRecordColumn(Table, "customer", {"code", "name"}, {"client_code", "client_nom"}),
Colonnes = Table.SelectColumns(Client, {"rank", "client_code", "client_nom", "revenue", "invoices_count", "share_pct", "last_invoice_date"}),
Types = Table.TransformColumnTypes(Colonnes, {
{"rank", Int64.Type}, {"revenue", Currency.Type}, {"invoices_count", Int64.Type},
{"share_pct", type number}, {"last_invoice_date", type date}})
in
Typesmetadata.next_offset tant que metadata.has_more est vrai.let
Page = (decalage as number) =>
Appel("api/products/analytics", [from = "2025-01-01", to = "2025-12-31", group_by = "product",
limit = "200", offset = Number.ToText(decalage)]),
Pages = List.Generate(
() => Page(0),
each _ <> null,
each if _[metadata][has_more] then Page(_[metadata][next_offset]) else null,
each _[buckets]),
Table = Table.FromRecords(List.Combine(Pages)),
Produit = Table.ExpandRecordColumn(Table, "ref", {"code", "name"}, {"produit_code", "produit_nom"}),
Colonnes = Table.SelectColumns(Produit, {"produit_code", "produit_nom", "revenue", "cost", "margin", "margin_pct", "qty", "abc_class"}),
Types = Table.TransformColumnTypes(Colonnes, {
{"revenue", Currency.Type}, {"cost", Currency.Type}, {"margin", Currency.Type},
{"margin_pct", type number}, {"qty", type number}})
in
Typeslet
Reponse = Appel("api/customers/aging"),
Table = Table.FromRecords(Reponse[items]),
Client = Table.ExpandRecordColumn(Table, "customer", {"code", "name"}, {"client_code", "client_nom"}),
Tranches = Table.ExpandRecordColumn(Client, "aging", {"days_0_30", "days_30_60", "days_60_90", "days_over_90"}),
Colonnes = Table.SelectColumns(Tranches, {"client_code", "client_nom", "balance_total", "days_most_overdue",
"days_0_30", "days_30_60", "days_60_90", "days_over_90"}),
Types = Table.TransformColumnTypes(Colonnes, {
{"balance_total", Currency.Type}, {"days_most_overdue", Int64.Type},
{"days_0_30", Currency.Type}, {"days_30_60", Currency.Type}, {"days_60_90", Currency.Type},
{"days_over_90", Currency.Type}})
in
Typesref vide) regroupe les ventes sans produit, par exemple des lignes de texte avec montant.abc_class classe les produits : A pour ceux qui font 80 % des ventes, puis B et C.summary.lines_without_cost compte les lignes sans coûtant.https://relay.exoconnect.app/…, choisissez l'authentification Anonyme : la clé passe par l'en-tête x-api-key de la fonction Appel. Niveau de confidentialité : Organisationnel..pbix ou .xlsx : utilisez une clé en lecture seule, propre au rapport, et ne partagez pas le fichier source.2026-10-11T09:12:40-04:00) ; typez-les en date ou en datetimezone selon le visuel.Table.Combine) plutôt qu'un seul intervalle de dix ans.