{
  "name": "GLPI Bulk Asset Import (Form Upload)",
  "nodes": [
    {
      "parameters": {
        "formTitle": "GLPI Bulk Asset Import",
        "formDescription": "Upload a CSV of assets to create them in GLPI.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "CSV File",
              "fieldType": "file",
              "multipleFiles": false,
              "acceptFileTypes": ".csv",
              "requiredField": true
            }
          ]
        },
        "responseMode": "lastNode",
        "options": {
          "buttonLabel": "Import to GLPI"
        }
      },
      "id": "328755ae-b928-4b1a-8504-7f1d43fba4fc",
      "name": "On Form Submission",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [
        -220,
        300
      ],
      "webhookId": "76f4b60c-4133-437c-a13c-a95cd3ba188b"
    },
    {
      "parameters": {
        "operation": "csv",
        "binaryPropertyName": "CSV_File",
        "options": {}
      },
      "id": "43ee535e-59af-4f69-92a5-8267ce5e9351",
      "name": "Extract From File",
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        40,
        180
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "eb27bc39-1e2e-4fde-bceb-9fcd5f2ed157",
              "name": "glpiUrl",
              "value": "https://glpi.example.com/apirest.php",
              "type": "string"
            },
            {
              "id": "f6d093e3-ee6b-4ad3-86e3-340ff614be1f",
              "name": "appToken",
              "value": "REPLACE_WITH_APP_TOKEN",
              "type": "string"
            },
            {
              "id": "bc0fbf97-40de-4222-84a3-65ebf8176035",
              "name": "userToken",
              "value": "REPLACE_WITH_USER_TOKEN",
              "type": "string"
            },
            {
              "id": "bd8173a3-a7a1-4f2c-a7ab-8b53d43a3c5d",
              "name": "itemtype",
              "value": "Computer",
              "type": "string"
            },
            {
              "id": "7e03e8b1-c8fc-40b3-8bf3-95caaaa8e69e",
              "name": "entitiesId",
              "value": 0,
              "type": "number"
            }
          ]
        },
        "options": {}
      },
      "id": "510508a4-03e4-4801-a727-71a8fcbf57f6",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        40,
        440
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $('Config').first().json.glpiUrl }}/initSession",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "App-Token",
              "value": "={{ $('Config').first().json.appToken }}"
            },
            {
              "name": "Authorization",
              "value": "=user_token {{ $('Config').first().json.userToken }}"
            }
          ]
        },
        "options": {}
      },
      "id": "e7db11bd-23c0-404c-b411-70ef4ae4ac84",
      "name": "Init Session",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        260,
        440
      ]
    },
    {
      "parameters": {
        "mode": "append",
        "options": {}
      },
      "id": "6437ae52-af21-4da5-a38b-560def9ad79e",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        480,
        320
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const cfg = $('Config').first().json;\nconst base = String(cfg.glpiUrl).replace(/\\/+$/, '');\nconst sessionToken = $('Init Session').first().json.session_token;\nconst appToken = cfg.appToken || '';\nconst entitiesId = Number(cfg.entitiesId ?? 0);\n\nconst headers = {\n  'Content-Type': 'application/json',\n  'App-Token': appToken,\n  'Session-Token': sessionToken,\n};\n\nconst directMap = {\n  name: 'name',\n  serial: 'serial',\n  inventory_number: 'otherserial',\n  comment: 'comment',\n};\n\nconst dropdownMap = {\n  manufacturer: { field: 'manufacturers_id', dropdown: 'Manufacturer' },\n  model:        { field: 'computermodels_id', dropdown: 'ComputerModel' },\n  type:         { field: 'computertypes_id', dropdown: 'ComputerType' },\n  location:     { field: 'locations_id', dropdown: 'Location' },\n  status:       { field: 'states_id', dropdown: 'State' },\n};\n\nconst cache = new Map();\n\nconst resolveDropdown = async (dropdown, rawName) => {\n  const name = String(rawName).trim();\n  const key = dropdown + '::' + name.toLowerCase();\n  if (cache.has(key)) return cache.get(key);\n\n  let id = null;\n  try {\n    const found = await this.helpers.httpRequest({\n      method: 'GET',\n      url: base + '/' + dropdown,\n      headers,\n      qs: { 'searchText[name]': name, range: '0-99' },\n      json: true,\n    });\n    if (Array.isArray(found)) {\n      const m = found.find(r => String(r.name || '').toLowerCase() === name.toLowerCase());\n      if (m) id = m.id;\n    }\n  } catch (e) {}\n\n  if (id === null) {\n    const created = await this.helpers.httpRequest({\n      method: 'POST',\n      url: base + '/' + dropdown,\n      headers,\n      body: { input: { name, entities_id: entitiesId } },\n      json: true,\n    });\n    id = Array.isArray(created) ? created[0].id : created.id;\n  }\n\n  cache.set(key, id);\n  return id;\n};\n\nconst rows = $('Extract From File').all().map(i => i.json);\nconst inputs = [];\n\nfor (const row of rows) {\n  const obj = { entities_id: entitiesId };\n  for (const [col, field] of Object.entries(directMap)) {\n    if (row[col] !== undefined && String(row[col]).trim() !== '') {\n      obj[field] = row[col];\n    }\n  }\n  for (const [col, map] of Object.entries(dropdownMap)) {\n    if (row[col] !== undefined && String(row[col]).trim() !== '') {\n      obj[map.field] = await resolveDropdown(map.dropdown, row[col]);\n    }\n  }\n  inputs.push(obj);\n}\n\nreturn [{ json: { input: inputs } }];\n"
      },
      "id": "c62f9b7e-c428-406f-a8cc-06573aec61f7",
      "name": "Build Payload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        320
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Config').first().json.glpiUrl }}/{{ $('Config').first().json.itemtype }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "App-Token",
              "value": "={{ $('Config').first().json.appToken }}"
            },
            {
              "name": "Session-Token",
              "value": "={{ $('Init Session').first().json.session_token }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "a4b40e43-03ec-4348-9662-09e3f62a00b4",
      "name": "Create Assets",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        920,
        320
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ $('Config').first().json.glpiUrl }}/killSession",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "App-Token",
              "value": "={{ $('Config').first().json.appToken }}"
            },
            {
              "name": "Session-Token",
              "value": "={{ $('Init Session').first().json.session_token }}"
            }
          ]
        },
        "options": {}
      },
      "id": "b4147143-6128-46ba-a76e-a6b83c4a5a57",
      "name": "Kill Session",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1140,
        320
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const items = $('Create Assets').all();\nlet results = [];\nfor (const it of items) {\n  const j = it.json;\n  if (Array.isArray(j)) results.push(...j);\n  else if (j && Array.isArray(j.data)) results.push(...j.data);\n  else results.push(j);\n}\n\nconst created = results.filter(r => r && r.id && r.id !== false);\nconst failed  = results.filter(r => !r || r.id === false || r.id === undefined);\n\nreturn [{\n  json: {\n    message: 'Created ' + created.length + ' of ' + results.length + ' assets (' + failed.length + ' failed).',\n    total: results.length,\n    created: created.length,\n    failed: failed.length,\n    created_ids: created.map(r => r.id),\n    failures: failed,\n  }\n}];\n"
      },
      "id": "ded40d69-9d47-40da-a370-4b4eca2b550e",
      "name": "Summarize",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1360,
        320
      ]
    }
  ],
  "connections": {
    "On Form Submission": {
      "main": [
        [
          {
            "node": "Extract From File",
            "type": "main",
            "index": 0
          },
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract From File": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Init Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Init Session": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Build Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Payload": {
      "main": [
        [
          {
            "node": "Create Assets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Assets": {
      "main": [
        [
          {
            "node": "Kill Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kill Session": {
      "main": [
        [
          {
            "node": "Summarize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true
  }
}