{
  "name": "ETL · Bilan commercial",
  "nodes": [
    {
      "id": "depart",
      "type": "custom",
      "position": {
        "x": 0,
        "y": 260
      },
      "data": {
        "type": "manualTrigger",
        "label": "Départ",
        "config": {}
      }
    },
    {
      "id": "commandes",
      "type": "custom",
      "position": {
        "x": 200,
        "y": 120
      },
      "data": {
        "type": "code",
        "label": "Commandes",
        "config": {
          "language": "javascript",
          "mode": "runOnceForAllItems",
          "code": "return [{\"commande\": \"CMD-001\", \"clientId\": \"C001\", \"quantite\": \"2\", \"prixUnitaire\": \"125,50\", \"dateCommande\": \"13/09/2026\", \"livree\": \"true\"}, {\"commande\": \"CMD-002\", \"clientId\": \"C002\", \"quantite\": \"3\", \"prixUnitaire\": \"80,00\", \"dateCommande\": \"13/09/2026\", \"livree\": \"false\"}, {\"commande\": \"CMD-003\", \"clientId\": \"C001\", \"quantite\": \"1\", \"prixUnitaire\": \"1 200,00\", \"dateCommande\": \"13/09/2026\", \"livree\": \"true\"}, {\"commande\": \"CMD-004\", \"clientId\": \"C003\", \"quantite\": \"4\", \"prixUnitaire\": \"25,00\", \"dateCommande\": \"13/09/2026\", \"livree\": \"true\"}, {\"commande\": \"CMD-005\", \"clientId\": \"C002\", \"quantite\": \"-1\", \"prixUnitaire\": \"50,00\", \"dateCommande\": \"13/09/2026\", \"livree\": \"false\"}, {\"commande\": \"INVALIDE\", \"clientId\": \"C001\", \"quantite\": \"2\", \"prixUnitaire\": \"10,00\", \"dateCommande\": \"13/09/2026\", \"livree\": \"false\"}];",
          "schema": [
            {
              "name": "commande",
              "type": "string",
              "nullable": false
            },
            {
              "name": "clientId",
              "type": "string",
              "nullable": false
            },
            {
              "name": "quantite",
              "type": "string",
              "nullable": false
            },
            {
              "name": "prixUnitaire",
              "type": "string",
              "nullable": false
            },
            {
              "name": "dateCommande",
              "type": "string",
              "nullable": false
            },
            {
              "name": "livree",
              "type": "string",
              "nullable": false
            }
          ]
        }
      }
    },
    {
      "id": "conversion",
      "type": "custom",
      "position": {
        "x": 400,
        "y": 120
      },
      "data": {
        "type": "dataConversion",
        "label": "Conversion FR",
        "config": {
          "columns": [
            {
              "name": "quantite",
              "type": "number"
            },
            {
              "name": "prixUnitaire",
              "type": "number",
              "locale": "fr-FR"
            },
            {
              "name": "dateCommande",
              "type": "date",
              "format": "dd/MM/yyyy"
            },
            {
              "name": "livree",
              "type": "boolean"
            }
          ]
        }
      }
    },
    {
      "id": "qualite",
      "type": "custom",
      "position": {
        "x": 600,
        "y": 120
      },
      "data": {
        "type": "schemaCheck",
        "label": "Qualité",
        "config": {
          "schema": [
            {
              "name": "commande",
              "type": "string",
              "nullable": false,
              "pattern": "^CMD-[0-9]{3}$"
            },
            {
              "name": "clientId",
              "type": "string",
              "nullable": false,
              "pattern": "^C[0-9]{3}$"
            },
            {
              "name": "quantite",
              "type": "number",
              "nullable": false,
              "minimum": 1,
              "maximum": 20
            },
            {
              "name": "prixUnitaire",
              "type": "number",
              "nullable": false,
              "minimum": 0.01,
              "maximum": 10000
            },
            {
              "name": "dateCommande",
              "type": "date",
              "nullable": false
            },
            {
              "name": "livree",
              "type": "boolean",
              "nullable": false
            }
          ]
        }
      }
    },
    {
      "id": "clients",
      "type": "custom",
      "position": {
        "x": 400,
        "y": 440
      },
      "data": {
        "type": "code",
        "label": "Clients",
        "config": {
          "language": "javascript",
          "mode": "runOnceForAllItems",
          "code": "return [{\"clientId\": \"C001\", \"client\": \"Atelier Lumen\", \"region\": \"Bretagne\"}, {\"clientId\": \"C002\", \"client\": \"Maison Épure\", \"region\": \"Occitanie\"}, {\"clientId\": \"C003\", \"client\": \"Studio Rivage\", \"region\": \"Normandie\"}];",
          "schema": [
            {
              "name": "clientId",
              "type": "string",
              "nullable": false
            },
            {
              "name": "client",
              "type": "string",
              "nullable": false
            },
            {
              "name": "region",
              "type": "string",
              "nullable": false
            }
          ]
        }
      }
    },
    {
      "id": "jointure",
      "type": "custom",
      "position": {
        "x": 800,
        "y": 340
      },
      "data": {
        "type": "mergeJoin",
        "label": "Jointure",
        "config": {
          "mode": "left",
          "keys": [
            {
              "left": "clientId",
              "right": "clientId"
            }
          ],
          "rightPrefix": "ref_"
        }
      }
    },
    {
      "id": "calcul",
      "type": "custom",
      "position": {
        "x": 1000,
        "y": 340
      },
      "data": {
        "type": "derivedColumn",
        "label": "CA HT / TTC",
        "config": {
          "columns": [
            {
              "name": "montantHT",
              "type": "number",
              "expression": "{{ Math.round($json.quantite * $json.prixUnitaire * 100) / 100 }}"
            },
            {
              "name": "montantTTC",
              "type": "number",
              "expression": "{{ Math.round($json.quantite * $json.prixUnitaire * 120) / 100 }}"
            }
          ]
        }
      }
    },
    {
      "id": "profil",
      "type": "custom",
      "position": {
        "x": 1200,
        "y": 340
      },
      "data": {
        "type": "profile",
        "label": "Profil",
        "config": {
          "columns": [
            {
              "name": "quantite"
            },
            {
              "name": "prixUnitaire"
            },
            {
              "name": "montantHT"
            },
            {
              "name": "montantTTC"
            }
          ]
        }
      }
    },
    {
      "id": "bilan",
      "type": "custom",
      "position": {
        "x": 1000,
        "y": 600
      },
      "data": {
        "type": "aggregate",
        "label": "Agrégation",
        "config": {
          "aggregations": [
            {
              "field": "montantHT",
              "operation": "sum",
              "outputField": "caHT"
            },
            {
              "field": "montantTTC",
              "operation": "sum",
              "outputField": "caTTC"
            },
            {
              "field": "quantite",
              "operation": "sum",
              "outputField": "quantiteTotale"
            },
            {
              "field": "ref_client",
              "operation": "unique",
              "outputField": "clients"
            }
          ]
        }
      }
    },
    {
      "id": "verification",
      "type": "custom",
      "position": {
        "x": 1200,
        "y": 600
      },
      "data": {
        "type": "code",
        "label": "Vérification",
        "config": {
          "language": "javascript",
          "mode": "runOnceForAllItems",
          "code": "const b = $json;\nif (b.itemCount !== 4 || b.caHT !== 1791 || Math.abs(b.caTTC - 2149.2) > 0.000001 || b.quantiteTotale !== 10 || b.clients.length !== 3) throw new Error('ETL_RESULT_MISMATCH');\nreturn {...b, verdict:'VALIDÉ', donnees:'SYNTHÉTIQUES', devise:'EUR'};"
        }
      }
    },
    {
      "id": "rejets",
      "type": "custom",
      "position": {
        "x": 820,
        "y": 0
      },
      "data": {
        "type": "code",
        "label": "Rejets",
        "config": {
          "language": "javascript",
          "mode": "runOnceForAllItems",
          "code": "const lignes = $input.all().map(i => i.json ?? i);\nif (lignes.length !== 2) throw new Error('ETL_REJECT_COUNT_MISMATCH');\nreturn {nombreRejets:lignes.length, lignes:lignes.map(r => ({commande:r.item.commande, quantite:r.item.quantite, raisons:r.error.issues.map(e => e.column + ': ' + e.code)}))};"
        }
      }
    }
  ],
  "edges": [
    {
      "id": "depart-commandes",
      "source": "depart",
      "target": "commandes",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    },
    {
      "id": "depart-clients",
      "source": "depart",
      "target": "clients",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    },
    {
      "id": "commandes-conversion",
      "source": "commandes",
      "target": "conversion",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    },
    {
      "id": "conversion-qualite",
      "source": "conversion",
      "target": "qualite",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    },
    {
      "id": "qualite-jointure",
      "source": "qualite",
      "target": "jointure",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep",
      "label": "valides"
    },
    {
      "id": "clients-jointure",
      "source": "clients",
      "target": "jointure",
      "sourceHandle": "output_0",
      "targetHandle": "input-1",
      "type": "smoothstep",
      "label": "référentiel"
    },
    {
      "id": "qualite-rejets",
      "source": "qualite",
      "target": "rejets",
      "sourceHandle": "output_1",
      "targetHandle": "input-0",
      "type": "smoothstep",
      "label": "rejets"
    },
    {
      "id": "jointure-calcul",
      "source": "jointure",
      "target": "calcul",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    },
    {
      "id": "calcul-profil",
      "source": "calcul",
      "target": "profil",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    },
    {
      "id": "profil-bilan",
      "source": "profil",
      "target": "bilan",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    },
    {
      "id": "bilan-verification",
      "source": "bilan",
      "target": "verification",
      "sourceHandle": "output_0",
      "targetHandle": "input-0",
      "type": "smoothstep"
    }
  ],
  "settings": {
    "timeout": 30000
  }
}