{
  "id": "k2YnknQUWqF88QOLuH41o",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Automate Odoo Accounting with Telegram and ChatGPT",
  "tags": [],
  "nodes": [
    {
      "id": "26bf9399-e7c0-4104-aaf0-b1fe1ad975a6",
      "name": "Bot Data Receiver",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2000,
        1328
      ],
      "parameters": {
        "path": "YOUR_WEBHOOK_UUID",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "0e948798-59d4-4061-8640-9bbf3e26eb3c",
      "name": "Find Expense Account ID",
      "type": "n8n-nodes-base.odoo",
      "position": [
        32,
        560
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $json.entities.debit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "c55ccf3f-64ca-4952-8eef-209eb4676301",
      "name": "Find Cash Account ID",
      "type": "n8n-nodes-base.odoo",
      "position": [
        256,
        560
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.credit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "1d8fa225-a34c-467c-aa54-59d397b046e7",
      "name": "Confirm & Post Entry",
      "type": "n8n-nodes-base.odoo",
      "position": [
        928,
        560
      ],
      "parameters": {
        "resource": "custom",
        "operation": "update",
        "customResource": "account.move",
        "customResourceId": "={{ $json.id }}",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "state",
              "fieldValue": "posted"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "afe2f67f-92dd-41b6-9cff-47edf75e584e",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -1776,
        1552
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "cc3c17dd-e763-4816-a6f6-48e608cae9ff",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        -1648,
        1552
      ],
      "parameters": {
        "sessionKey": "chat_history",
        "sessionIdType": "customKey",
        "contextWindowLength": 50
      },
      "typeVersion": 1.3
    },
    {
      "id": "68be6e21-c635-4d98-8395-bfbeb6d62586",
      "name": "AI Financial Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "onError": "continueRegularOutput",
      "position": [
        -1776,
        1328
      ],
      "parameters": {
        "text": "={{ JSON.stringify($json.body) }}",
        "options": {
          "systemMessage": "=You are an expert Financial Intent Classifier for Odoo ERP. Output ONLY a valid JSON object.\n\n# CORE DECISION LOGIC (CRITICAL):\n1. IF the user mentions an AMOUNT and an EXPENSE CATEGORY:\n   -> route_path MUST BE \"save_expense\"\n   -> action_type MUST BE \"save\"\n2. IF the user asks for a REPORT or BALANCE (No amount):\n   -> route_path MUST BE \"fetch_expense_report\" or \"fetch_safe_balance\"\n   -> action_type MUST BE \"fetch\"\n\n# ROUTE PATHS:\n- \"save_expense\": Recording an expense.\n- \"save_supplier_payment\": Paying a supplier.\n- \"fetch_safe_balance\": Inquiries about balances/checks.\n- \"fetch_expense_report\": Inquiries about expense totals.\n- \"fetch_supplier_statement\": Supplier statement.\n\n# ACCOUNT MAPPING (TEMPLATE - UPDATE THESE CODES):\n- Safes/Checks: [SAFE_1_NAME]=[SAFE_1_CODE] | [SAFE_2_NAME]=[SAFE_2_CODE] | [CHECKS_NAME]=[CHECKS_CODE] | [WALLET_NAME]=[WALLET_CODE]\n- Expenses: 1. [EXPENSE_1_NAME]=[EXPENSE_1_CODE] | 2. [EXPENSE_2_NAME]=[EXPENSE_2_CODE] \n\n# SPECIAL LOGIC FOR CHECKS (\u0634\u064a\u0643\u0627\u062a):\n- IF recording an expense paid by \"\u0634\u064a\u0643\": You MUST set source=\"\u0634\u064a\u0643\u0627\u062a\", credit_code=\"[CHECKS_CODE]\", and debit_code to the expense account. You MUST extract \"check_number\" and \"due_date\".\n\n# JSON SCHEMA:\n{\n  \"route_path\": \"string\",\n  \"company\": \"[COMPANY_A]\" | \"[COMPANY_B]\" | null,\n  \"action_type\": \"save|fetch\", \n  \"entities\": {\n    \"amount\": number,\n    \"source\": \"string|null\",\n    \"target_name\": \"string|null\",\n    \"credit_code\": \"string|null\",\n    \"debit_code\": \"string|null\",\n    \"category_or_type\": \"string|null\",\n    \"date_filter\": \"today|yesterday|null\",\n    \"check_details\": {\"check_number\": \"string\", \"due_date\": \"string\"}\n  },\n  \"is_complete\": true\n}"
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "7e93fd70-aaef-40a3-a4da-a06fe4e3f81e",
      "name": "Clean AI Output",
      "type": "n8n-nodes-base.code",
      "position": [
        -1424,
        1328
      ],
      "parameters": {
        "jsCode": "// 1. Extract response\nlet inputData = $input.first().json;\nlet rawString = inputData.text || inputData.output || inputData.message || \"\";\n\n// 2. Clean markdown code fences and parse JSON\nlet cleanString = rawString.replace(/```json\\n?/gi, '').replace(/```\\n?/gi, '').trim();\n\n// 3. Try to extract JSON object\nlet jsonMatch = cleanString.match(/\\{[\\s\\S]*\\}/);\nif (jsonMatch) {\n  cleanString = jsonMatch[0];\n}\n\n// 4. Parse and return\nlet parsed = JSON.parse(cleanString);\nreturn parsed;"
      },
      "typeVersion": 2
    },
    {
      "id": "1bcda57f-cf6a-49d6-a016-05153da77f2e",
      "name": "Route to Expense Path",
      "type": "n8n-nodes-base.switch",
      "position": [
        -1120,
        1248
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Expense Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "40764b6c-4e80-4fcb-8e3d-24132ddeb919",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.route_path }}",
                    "rightValue": "save_expense"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Discount Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "d3cabdbe-6667-4d7f-aba6-2c3a4e9739df",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.route_path }}",
                    "rightValue": "save_supplier_discount"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Supplier Payment Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "1da844d2-6c17-4156-acb2-0b2d340c81af",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.route_path }}",
                    "rightValue": "save_supplier_payment"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Supplier Invoice Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "a44e6a18-e047-41bc-abc6-b9f112bf835e",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.route_path }}",
                    "rightValue": "save_supplier_invoice"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Advance Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "0c0db7ec-12c0-4dac-a18f-ff70080137e2",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.route_path }}",
                    "rightValue": "save_advance"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Supplier Statement Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "835c54cc-ad8a-4bb9-967f-c6fad1c8383c",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.route_path }}",
                    "rightValue": "=fetch_supplier_statement"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Fetch Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "13f0fbf0-8ecd-4399-9a9c-c1717ba90eab",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.route_path }}",
                    "rightValue": "fetch"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {},
        "looseTypeValidation": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "7d57fcf3-71e8-4762-8af2-b42e2b18f258",
      "name": "Build Odoo Journal Entry",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        560
      ],
      "parameters": {
        "jsCode": "const botData = $('Clean AI Output').item.json;\nconst amount = parseFloat(botData.entities.amount);\nconst category = botData.entities.category_or_type;\nconst company = botData.company || \"[DEFAULT_COMPANY_NAME]\"; \n\nconst debitAccountId = $('Find Expense Account ID').item.json.id;\nconst creditAccountId = $('Find Cash Account ID').item.json.id;\n\nconst fullRef = `${category} ${company}`;\nconst debitLabel = category;\nconst creditLabel = `\u0633\u062f\u0627\u062f \u0645\u0635\u0631\u0648\u0641: ${category}`;\n\nreturn {\n  \"ref\": fullRef,\n  \"move_type\": \"entry\",\n  \"journal_id\": 2, // [TEMPLATE: REPLACE WITH YOUR ODOO JOURNAL ID]\n  \"line_ids\": [\n    [0, 0, { \"account_id\": debitAccountId, \"name\": debitLabel, \"debit\": amount, \"credit\": 0.0 }],\n    [0, 0, { \"account_id\": creditAccountId, \"name\": creditLabel, \"debit\": 0.0, \"credit\": amount }]\n  ]\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "f953e928-ba2f-40d4-9f20-0d6f085c4ba0",
      "name": "Create Odoo Entry",
      "type": "n8n-nodes-base.odoo",
      "position": [
        704,
        560
      ],
      "parameters": {
        "resource": "custom",
        "customResource": "account.move",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "journal_id",
              "fieldValue": "={{ $json.journal_id }}"
            },
            {
              "fieldName": "move_type",
              "fieldValue": "={{ $json.move_type }}"
            },
            {
              "fieldName": "line_ids",
              "fieldValue": "={{ $json.line_ids }}"
            },
            {
              "fieldName": "ref",
              "fieldValue": "={{ $json.ref }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7035e0e0-32f4-4617-a02a-2ceab249f883",
      "name": "Find Partner ID",
      "type": "n8n-nodes-base.odoo",
      "position": [
        -192,
        752
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $json.entities.target_name }}",
              "operator": "like",
              "fieldName": "name"
            }
          ]
        },
        "customResource": "res.partner"
      },
      "typeVersion": 1
    },
    {
      "id": "572ba219-c428-46fc-bdf1-43d5e94b6144",
      "name": "Build Discount Entry",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        752
      ],
      "parameters": {
        "jsCode": "const partnerId = $('Find Partner ID').item.json.id;\nconst debitAccountId = $('Find Debit Account ID').item.json.id;\nconst creditAccountId = $('Find Credit Account ID').item.json.id;\n\nconst currencyId = 1; // [TEMPLATE: REPLACE WITH YOUR ODOO CURRENCY ID]\n\nconst aiData = $('Clean AI Output').item.json.entities;\nconst amount = parseFloat(aiData.amount);\nconst label = aiData.label;\n\nconst lines = [\n    [0, 0, { \"account_id\": debitAccountId, \"partner_id\": partnerId, \"name\": label, \"debit\": amount, \"credit\": 0.0, \"currency_id\": currencyId }],\n    [0, 0, { \"account_id\": creditAccountId, \"name\": label, \"debit\": 0.0, \"credit\": amount, \"currency_id\": currencyId }]\n];\nreturn { lines_to_odoo: lines };"
      },
      "typeVersion": 2
    },
    {
      "id": "cc798d9e-dbd0-45eb-ab82-ccb9631e3f10",
      "name": "Confirm & Post Entry1",
      "type": "n8n-nodes-base.odoo",
      "position": [
        928,
        752
      ],
      "parameters": {
        "resource": "custom",
        "operation": "update",
        "customResource": "account.move",
        "customResourceId": "={{ $json.id }}",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "state",
              "fieldValue": "posted"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7954d235-8f6b-4bcc-b540-de2f55de95f8",
      "name": "Create Discount Entry",
      "type": "n8n-nodes-base.odoo",
      "position": [
        704,
        752
      ],
      "parameters": {
        "resource": "custom",
        "customResource": "account.move",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "line_ids",
              "fieldValue": "={{ $json.lines_to_odoo }}"
            },
            {
              "fieldName": "ref",
              "fieldValue": "={{ $('Clean AI Output').item.json.entities.label }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e2a96501-4101-45ab-9a59-5731c767b4e5",
      "name": "Find Debit Account ID",
      "type": "n8n-nodes-base.odoo",
      "position": [
        32,
        752
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.debit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "0c98bcf0-c0c4-49ad-9d4d-4038e6fb08d9",
      "name": "Find Credit Account ID",
      "type": "n8n-nodes-base.odoo",
      "position": [
        256,
        752
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.credit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "da4ae748-de3f-482f-b6a3-7e9e95da5623",
      "name": "Filter Payment Source",
      "type": "n8n-nodes-base.switch",
      "position": [
        -864,
        1040
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Checks Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "49b2f04f-1c50-4783-b932-ffefeaf4cba7",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.entities.source }}",
                    "rightValue": "\u0634\u064a\u0643\u0627\u062a"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Instant Payment Path",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "1028f762-a9bc-4766-b9b8-f2e161fed176",
                    "operator": {
                      "type": "string",
                      "operation": "notEquals"
                    },
                    "leftValue": "={{ $json.entities.source }}",
                    "rightValue": "\u0634\u064a\u0643\u0627\u062a"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "7ac9d563-4b8d-4c5d-bc7f-9cfae4960ac1",
      "name": "Format Check Date",
      "type": "n8n-nodes-base.code",
      "position": [
        -640,
        944
      ],
      "parameters": {
        "jsCode": "for (const item of $input.all()) {\n  if (item.json.entities && item.json.entities.check_details && item.json.entities.check_details.due_date) {\n    const rawDate = item.json.entities.check_details.due_date; \n    const [day, month, year] = rawDate.split('/');\n    item.json.entities.check_details.formatted_due_date = `${year}-${month}-${day}`; \n  }\n}\nreturn $input.all();"
      },
      "typeVersion": 2
    },
    {
      "id": "a7b55785-8077-49f2-a854-149c9854081b",
      "name": "Create an event",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        -416,
        944
      ],
      "parameters": {
        "end": "={{ $json.entities.check_details.formatted_due_date }}T10:00:00",
        "start": "={{ $json.entities.check_details.formatted_due_date }}T09:00:00",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "user@example.com"
        },
        "additionalFields": {
          "summary": "=\u0627\u0633\u062a\u062d\u0642\u0627\u0642 \u0634\u064a\u0643 \u0644\u0645\u0648\u0631\u062f: {{ $json.entities.target_name }}",
          "description": "=\u0645\u0628\u0644\u063a \u0627\u0644\u0634\u064a\u0643: {{ $json.entities.amount }} | \u0631\u0642\u0645 \u0627\u0644\u0634\u064a\u0643: {{ $json.entities.check_details.check_number }}"
        },
        "useDefaultReminders": "={{ true }}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "d746453e-191f-4ae7-b83b-4b058460631f",
      "name": "Find Partner ID2",
      "type": "n8n-nodes-base.odoo",
      "position": [
        -192,
        944
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Format Check Date').item.json.entities.target_name }}",
              "operator": "like",
              "fieldName": "name"
            }
          ]
        },
        "customResource": "res.partner"
      },
      "typeVersion": 1
    },
    {
      "id": "3d8fbdb6-e61f-40bc-9888-f04560651138",
      "name": "Find Debit Account ID1",
      "type": "n8n-nodes-base.odoo",
      "position": [
        32,
        944
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Format Check Date').item.json.entities.debit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "72ea3603-d788-4105-b6fd-edc51a3d2a3d",
      "name": "Find Credit Account ID1",
      "type": "n8n-nodes-base.odoo",
      "position": [
        256,
        944
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Format Check Date').item.json.entities.credit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "f1af3626-24f7-4d28-b5e5-ddacdad84b4a",
      "name": "Build Journal Lines",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        944
      ],
      "parameters": {
        "jsCode": "const payload = $('Format Check Date').item.json.entities;\nconst checkDetails = payload.check_details;\nconst partnerId = $('Find Partner ID2').item.json.id;\nconst debitAccountId = $('Find Debit Account ID1').item.json.id;\nconst creditAccountId = $('Find Credit Account ID1').item.json.id;\n\nconst amount = payload.amount;\nconst checkNumber = checkDetails.check_number;\nconst dueDate = checkDetails.formatted_due_date;\nconst supplierName = payload.target_name;\n\nconst lineLabel = `\u062f\u0641\u0639\u0629 \u0628\u0634\u064a\u0643 \u0631\u0642\u0645 ${checkNumber} \u062a\u0633\u062a\u062d\u0642 \u0641\u064a ${dueDate}`;\nconst currencyId = 74; // [TEMPLATE: REPLACE WITH YOUR ODOO CURRENCY ID]\n\nconst lines = [\n  [0, 0, { \"account_id\": debitAccountId, \"partner_id\": partnerId, \"name\": lineLabel, \"debit\": amount, \"credit\": 0, \"currency_id\": currencyId }],\n  [0, 0, { \"account_id\": creditAccountId, \"name\": lineLabel, \"debit\": 0, \"credit\": amount, \"currency_id\": currencyId }]\n];\n\nreturn { json: { journal_ref: `\u0633\u062f\u0627\u062f \u0645\u0648\u0631\u062f: ${supplierName} - \u0634\u064a\u0643 ${checkNumber}`, lines_to_odoo: lines } };"
      },
      "typeVersion": 2
    },
    {
      "id": "06ee9e6b-9f04-4f4e-a244-6a7ac8937028",
      "name": "Create Discount Entry1",
      "type": "n8n-nodes-base.odoo",
      "position": [
        704,
        944
      ],
      "parameters": {
        "resource": "custom",
        "customResource": "account.move",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "line_ids",
              "fieldValue": "={{ $json.lines_to_odoo }}"
            },
            {
              "fieldName": "ref",
              "fieldValue": "={{ $json.journal_ref }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "280fbca2-9609-4c81-ab3a-0ebb214c7ae1",
      "name": "Confirm & Post Entry2",
      "type": "n8n-nodes-base.odoo",
      "position": [
        928,
        944
      ],
      "parameters": {
        "resource": "custom",
        "operation": "update",
        "customResource": "account.move",
        "customResourceId": "={{ $json.id }}",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "state",
              "fieldValue": "posted"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8581cc65-762f-4b68-a9e5-a36f286b2810",
      "name": "Find Partner ID3",
      "type": "n8n-nodes-base.odoo",
      "position": [
        -192,
        1136
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $json.entities.target_name }}",
              "operator": "like",
              "fieldName": "name"
            }
          ]
        },
        "customResource": "res.partner"
      },
      "typeVersion": 1
    },
    {
      "id": "310fb4dc-b00a-4889-97a3-0a493ed4dd7d",
      "name": "Find Debit Account ID2",
      "type": "n8n-nodes-base.odoo",
      "position": [
        32,
        1136
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Filter Payment Source').item.json.entities.debit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "019c7dd5-20ce-46ce-ac99-5abe20103bb5",
      "name": "Find Credit Account ID2",
      "type": "n8n-nodes-base.odoo",
      "position": [
        256,
        1136
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Filter Payment Source').item.json.entities.credit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "4cd9c072-8710-48be-872b-fc578eabcd9b",
      "name": "Build Instant Entry Lines",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        1136
      ],
      "parameters": {
        "jsCode": "const payload = $('Clean AI Output').first().json;\nconst partnerId = $('Find Partner ID3').first().json.id;\nconst debitAccountId = $('Find Debit Account ID2').first().json.id;\nconst creditAccountId = $('Find Credit Account ID2').first().json.id;\n\nconst today = new Date().toISOString().split('T')[0];\nconst lineLabel = `\u0633\u062f\u0627\u062f \u0641\u0648\u0631\u064a \u0644\u0644\u0645\u0648\u0631\u062f: ${payload.entities.target_name} \u0628\u062a\u0627\u0631\u064a\u062e ${today}`;\nconst currencyId = 74; // [TEMPLATE: REPLACE WITH YOUR ODOO CURRENCY ID]\n\nconst lines = [\n  [0, 0, { \"account_id\": debitAccountId, \"partner_id\": partnerId, \"name\": lineLabel, \"debit\": payload.entities.amount, \"credit\": 0, \"currency_id\": currencyId }],\n  [0, 0, { \"account_id\": creditAccountId, \"name\": lineLabel, \"debit\": 0, \"credit\": payload.entities.amount, \"currency_id\": currencyId }]\n];\n\nreturn { json: { journal_ref: `\u0633\u062f\u0627\u062f \u0641\u0648\u0631\u064a: ${payload.entities.target_name}`, entry_date: today, lines_to_odoo: lines } };"
      },
      "typeVersion": 2
    },
    {
      "id": "b57f1f78-7da6-4976-b2ba-93fc40a0b561",
      "name": "Create Discount Entry2",
      "type": "n8n-nodes-base.odoo",
      "position": [
        704,
        1136
      ],
      "parameters": {
        "resource": "custom",
        "customResource": "account.move",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "line_ids",
              "fieldValue": "={{ $json.lines_to_odoo }}"
            },
            {
              "fieldName": "ref",
              "fieldValue": "={{ $json.journal_ref }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "58839b88-ab53-4a88-bc22-ea4ab96fe5fa",
      "name": "Confirm & Post Entry3",
      "type": "n8n-nodes-base.odoo",
      "position": [
        928,
        1136
      ],
      "parameters": {
        "resource": "custom",
        "operation": "update",
        "customResource": "account.move",
        "customResourceId": "={{ $json.id }}",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "state",
              "fieldValue": "posted"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b3446b1f-8a7b-49a6-90f9-4f2f9f89861c",
      "name": "Find Partner ID4",
      "type": "n8n-nodes-base.odoo",
      "position": [
        -192,
        1328
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $json.entities.target_name.replace(/[\u0623\u0625\u0622]/g, '\u0627').replace(/[\u0649]/g, '\u064a').replace(/[\u0629]/g, '\u0647').trim() }}",
              "operator": "like",
              "fieldName": "name"
            }
          ]
        },
        "customResource": "res.partner"
      },
      "typeVersion": 1
    },
    {
      "id": "48504344-8f4d-43f1-b2db-5d1f20d18154",
      "name": "Find Debit Account ID3",
      "type": "n8n-nodes-base.odoo",
      "position": [
        32,
        1328
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.debit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "77181831-cdfa-40ac-8a55-bf4391d07581",
      "name": "Find Credit Account ID3",
      "type": "n8n-nodes-base.odoo",
      "position": [
        256,
        1328
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.credit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "7f98d204-8c65-4a4e-a643-e86352a276db",
      "name": "Build Invoice Entry Lines",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        1328
      ],
      "parameters": {
        "jsCode": "const payload = $('Clean AI Output').first().json;\nconst partnerId = $('Find Partner ID4').first().json.id; \nconst debitAccountId = $('Find Debit Account ID3').first().json.id; \nconst creditAccountId = $('Find Credit Account ID3').first().json.id; \n\nconst today = new Date().toISOString().split('T')[0];\nconst lineLabel = `\u0625\u062b\u0628\u0627\u062a \u0641\u0627\u062a\u0648\u0631\u0629 \u0645\u0648\u0631\u062f: ${payload.entities.target_name} \u0628\u062a\u0627\u0631\u064a\u062e ${today}`;\nconst currencyId = 74; // [TEMPLATE: REPLACE WITH YOUR ODOO CURRENCY ID]\n\nconst lines = [\n  [0, 0, { \"account_id\": debitAccountId, \"name\": lineLabel, \"debit\": payload.entities.amount, \"credit\": 0, \"currency_id\": currencyId }],\n  [0, 0, { \"account_id\": creditAccountId, \"partner_id\": partnerId, \"name\": lineLabel, \"debit\": 0, \"credit\": payload.entities.amount, \"currency_id\": currencyId }]\n];\n\nreturn { json: { journal_ref: `\u0641\u0627\u062a\u0648\u0631\u0629: ${payload.entities.target_name}`, entry_date: today, lines_to_odoo: lines } };"
      },
      "typeVersion": 2
    },
    {
      "id": "a168aa30-2e4e-4c84-a9ab-2f9d9a6efe62",
      "name": "Create Discount Entry3",
      "type": "n8n-nodes-base.odoo",
      "position": [
        704,
        1328
      ],
      "parameters": {
        "resource": "custom",
        "customResource": "account.move",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "line_ids",
              "fieldValue": "={{ $json.lines_to_odoo }}"
            },
            {
              "fieldName": "ref",
              "fieldValue": "={{ $json.journal_ref }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "45fb5210-c6b2-420c-9f5c-71535305762d",
      "name": "Confirm & Post Entry4",
      "type": "n8n-nodes-base.odoo",
      "position": [
        928,
        1328
      ],
      "parameters": {
        "resource": "custom",
        "operation": "update",
        "customResource": "account.move",
        "customResourceId": "={{ $json.id }}",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "state",
              "fieldValue": "posted"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "19aa1c72-87f3-4759-a164-999f3a04ac77",
      "name": "Find Partner ID5",
      "type": "n8n-nodes-base.odoo",
      "position": [
        -192,
        1520
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $json.entities.target_name.replace(/[\u0623\u0625\u0622]/g, '\u0627').replace(/[\u0649]/g, '\u064a').replace(/[\u0629]/g, '\u0647').trim() }}",
              "operator": "like",
              "fieldName": "name"
            }
          ]
        },
        "customResource": "res.partner"
      },
      "typeVersion": 1
    },
    {
      "id": "3a4379cd-752d-4ea7-bffb-5806237c36ca",
      "name": "Find Debit Account ID4",
      "type": "n8n-nodes-base.odoo",
      "position": [
        32,
        1520
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.debit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "2148de06-b201-496f-b1b7-062b422c486b",
      "name": "Find Credit Account ID4",
      "type": "n8n-nodes-base.odoo",
      "position": [
        256,
        1520
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.credit_code }}",
              "fieldName": "code"
            }
          ]
        },
        "customResource": "account.account"
      },
      "typeVersion": 1
    },
    {
      "id": "8925d278-7e4c-4602-9c1b-2092a91dbd83",
      "name": "Build Advance Entry Lines",
      "type": "n8n-nodes-base.code",
      "position": [
        480,
        1520
      ],
      "parameters": {
        "jsCode": "const payload = $('Clean AI Output').first().json;\nconst partnerId = $('Find Partner ID5').first().json.id; \nconst debitAccountId = $('Find Debit Account ID4').first().json.id; \nconst creditAccountId = $('Find Credit Account ID4').first().json.id; \n\nconst today = new Date().toISOString().split('T')[0];\nconst lineLabel = `\u0633\u0644\u0641\u0629 \u0644\u0644\u0645\u0648\u0638\u0641: ${payload.entities.target_name} \u0628\u062a\u0627\u0631\u064a\u062e ${today}`;\nconst currencyId = 74; // [TEMPLATE: REPLACE WITH YOUR ODOO CURRENCY ID]\n\nconst lines = [\n  [0, 0, { \"account_id\": debitAccountId, \"partner_id\": partnerId, \"name\": lineLabel, \"debit\": payload.entities.amount, \"credit\": 0, \"currency_id\": currencyId }],\n  [0, 0, { \"account_id\": creditAccountId, \"name\": lineLabel, \"debit\": 0, \"credit\": payload.entities.amount, \"currency_id\": currencyId }]\n];\n\nreturn { json: { journal_ref: `\u0633\u0644\u0641\u0629: ${payload.entities.target_name}`, entry_date: today, lines_to_odoo: lines } };"
      },
      "typeVersion": 2
    },
    {
      "id": "3417ebbb-7ada-437f-b934-e0740f393daa",
      "name": "Create Discount Entry4",
      "type": "n8n-nodes-base.odoo",
      "position": [
        704,
        1520
      ],
      "parameters": {
        "resource": "custom",
        "customResource": "account.move",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "line_ids",
              "fieldValue": "={{ $json.lines_to_odoo }}"
            },
            {
              "fieldName": "ref",
              "fieldValue": "={{ $json.journal_ref }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "15e17889-5553-47e6-a40c-44a4a42614a1",
      "name": "Confirm & Post Entry5",
      "type": "n8n-nodes-base.odoo",
      "position": [
        928,
        1520
      ],
      "parameters": {
        "resource": "custom",
        "operation": "update",
        "customResource": "account.move",
        "customResourceId": "={{ $json.id }}",
        "fieldsToCreateOrUpdate": {
          "fields": [
            {
              "fieldName": "state",
              "fieldValue": "posted"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "6199887a-1680-429a-8744-69a927c06d90",
      "name": "Fetch Safe/Bank Move Lines",
      "type": "n8n-nodes-base.odoo",
      "position": [
        960,
        1840
      ],
      "parameters": {
        "limit": "={{ 0 }}",
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $('Route to Expense Path').item.json.entities.credit_code }}",
              "fieldName": "account_code"
            },
            {
              "value": "={{ $json.startDate }}",
              "fieldName": "date"
            }
          ]
        },
        "customResource": "account.move.line"
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "caed4b1b-6606-407e-834f-cdab1a9cdc63",
      "name": "Date Resolver",
      "type": "n8n-nodes-base.code",
      "position": [
        736,
        1840
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst userMessage = input.user_message || \"\";\nconst now = new Date();\nlet startDate, endDate, displayName;\n\nif (userMessage.includes(\"\u0634\u0647\u0631\") || userMessage.includes(\"\u0627\u0644\u0634\u0647\u0631\")) {\n  startDate = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().split('T')[0];\n  endDate = now.toISOString().split('T')[0];\n  displayName = \"\u062e\u0644\u0627\u0644 \u0634\u0647\u0631 \" + (now.getMonth() + 1);\n} else if (userMessage.includes(\"\u0627\u0645\u0628\u0627\u0631\u062d\")) {\n  const yesterday = new Date(now);\n  yesterday.setDate(now.getDate() - 1);\n  startDate = yesterday.toISOString().split('T')[0];\n  endDate = startDate;\n  displayName = \"\u0627\u0645\u0628\u0627\u0631\u062d\";\n} else {\n  startDate = now.toISOString().split('T')[0];\n  endDate = startDate;\n  displayName = \"\u0627\u0644\u0646\u0647\u0627\u0631\u062f\u0629\";\n}\n\nreturn { startDate, endDate, display_name: displayName };"
      },
      "typeVersion": 2
    },
    {
      "id": "e35ee0d5-cf8c-4d9b-954a-e46c8fe222fc",
      "name": "Build Final Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        1840
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst aiData = $('Clean AI Output').first().json.entities || {};\nconst dateData = $('Date Resolver').first().json || {};\n\nlet totalDebit = 0; \nlet totalCredit = 0; \nlet details = \"\";\n\nlet accountName = aiData.source || aiData.category_or_type || \"\u0627\u0644\u062d\u0633\u0627\u0628\";\nlet displayDate = dateData.display_name || \"\u0627\u0644\u0641\u062a\u0631\u0629 \u0627\u0644\u0645\u062d\u062f\u062f\u0629\";\nlet actualDate = dateData.startDate || dateData.resolved_date || \"\";\n\nif (items.length > 0 && items[0].json && items[0].json.account_id) {\n  accountName = items[0].json.account_id[1]; \n  for (const item of items) {\n    if (!item.json.id) continue;\n    const debit = item.json.debit || 0;\n    const credit = item.json.credit || 0;\n    const ref = item.json.ref || item.json.name || \"\u0628\u062f\u0648\u0646 \u0648\u0635\u0641\";\n    \n    totalDebit += debit;\n    totalCredit += credit;\n\n    if (debit > 0) details += `\u2022 \u0645\u062f\u064a\u0646/\u0648\u0627\u0631\u062f \ud83d\udfe2: ${debit.toLocaleString()} \u062c \u2b05\ufe0f (${ref})\\n`;\n    if (credit > 0) details += `\u2022 \u062f\u0627\u0626\u0646/\u0645\u0646\u0635\u0631\u0641 \ud83d\udd34: ${credit.toLocaleString()} \u062c \u2b05\ufe0f (${ref})\\n`;\n  }\n}\n\nlet report = `\ud83d\udcca \u062a\u0642\u0631\u064a\u0631 \u062d\u0633\u0627\u0628: ${accountName}\\n\ud83d\udcc5 \u0644\u0640 (${displayDate} - ${actualDate}):\\n`;\nreport += `--------------------------\\n`;\n\nif (details) {\n    report += details;\n    report += `--------------------------\\n`;\n    if (accountName.includes(\"\u0634\u064a\u0643\u0627\u062a\") || accountName.includes(\"\u062e\u0632\u064a\u0646\") || accountName.includes(\"\u0643\u0627\u0634\")) {\n        report += `\ud83d\udcb0 \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0648\u0627\u0631\u062f (\u062f\u062e\u0644): ${totalDebit.toLocaleString()} \u062c\u0646\u064a\u0647\\n`;\n        report += `\ud83d\udcb8 \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0646\u0635\u0631\u0641 (\u062e\u0631\u062c): ${totalCredit.toLocaleString()} \u062c\u0646\u064a\u0647\\n`;\n        report += `\u2696\ufe0f \u0635\u0627\u0641\u064a \u0627\u0644\u062d\u0631\u0643\u0629: ${(totalDebit - totalCredit).toLocaleString()} \u062c\u0646\u064a\u0647\\n`;\n    } else {\n        report += `\ud83d\udcb8 \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0635\u0631\u0648\u0641: ${totalDebit.toLocaleString()} \u062c\u0646\u064a\u0647\\n`;\n        if (totalCredit > 0) report += `\ud83d\udd04 \u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0645\u0631\u062a\u062c\u0639\u0627\u062a: ${totalCredit.toLocaleString()} \u062c\u0646\u064a\u0647\\n`;\n    }\n} else {\n    report += `\u26a0\ufe0f \u0645\u0641\u064a\u0634 \u0623\u064a \u062d\u0631\u0643\u0627\u062a \u0627\u062a\u0633\u062c\u0644\u062a \u0641\u064a \u0627\u0644\u0641\u062a\u0631\u0629 \u062f\u064a.\\n`;\n}\n\nreport += `--------------------------\\n`;\nreport += `\ud83d\udc81\u200d\u2640\ufe0f \u0627\u0644\u0646\u0638\u0627\u0645: \u0627\u0644\u062d\u0633\u0628\u0629 \u0638\u0628\u0637\u062a\u060c \u0627\u0644\u062a\u0642\u0631\u064a\u0631 \u0645\u0639\u0627\u0643!`;\n\nreturn { json: { report_text: report } };"
      },
      "typeVersion": 2
    },
    {
      "id": "37d2638c-d712-46cf-af4d-a4c7a765fee0",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1392,
        1840
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { \"reply\": $json.report_text } }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "b8abee07-052b-4cd6-b2d1-1b5cd7968f62",
      "name": "Find Supplier ID",
      "type": "n8n-nodes-base.odoo",
      "position": [
        736,
        2032
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $json.entities.target_name.replace(/[\u0623\u0625\u0622]/g, '\u0627').replace(/[\u0649]/g, '\u064a').replace(/[\u0629]/g, '\u0647').trim() }}",
              "operator": "like",
              "fieldName": "name"
            }
          ]
        },
        "customResource": "res.partner"
      },
      "typeVersion": 1
    },
    {
      "id": "a4e6111d-55f9-43ac-81ca-268b19025e0a",
      "name": "Get many items",
      "type": "n8n-nodes-base.odoo",
      "position": [
        960,
        2032
      ],
      "parameters": {
        "options": {},
        "resource": "custom",
        "operation": "getAll",
        "returnAll": true,
        "filterRequest": {
          "filter": [
            {
              "value": "={{ $json.id }}",
              "fieldName": "partner_id"
            },
            {
              "value": "posted",
              "fieldName": "parent_state"
            }
          ]
        },
        "customResource": "account.move.line"
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "851c295c-3e2e-4549-96b0-61299590bf20",
      "name": "Calculate Supplier Balance",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        2032
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst supplierName = $('Clean AI Output').first().json.entities.target_name;\n\nlet totalDebit = 0; \nlet totalCredit = 0; \n\nitems.sort((a, b) => new Date(b.json.date) - new Date(a.json.date));\n\nlet details = \"\";\nlet counter = 0;\n\nfor (const item of items) {\n  const debit = item.json.debit || 0;\n  const credit = item.json.credit || 0;\n  const ref = item.json.name || item.json.ref || \"\u0628\u062f\u0648\u0646 \u0648\u0635\u0641\";\n  \n  totalDebit += debit;\n  totalCredit += credit;\n\n  if (counter < 10) {\n    if (credit > 0) details += `\ud83d\udd34 \u0641\u0627\u062a\u0648\u0631\u0629/\u0631\u0635\u064a\u062f (+${credit.toLocaleString()} \u062c) \u2b05\ufe0f ${ref}\\n`;\n    else if (debit > 0) details += `\ud83d\udfe2 \u0633\u062f\u0627\u062f/\u062e\u0635\u0645 (-${debit.toLocaleString()} \u062c) \u2b05\ufe0f ${ref}\\n`;\n    counter++;\n  }\n}\n\nconst balance = totalCredit - totalDebit;\nlet balanceText = \"\";\n\nif (balance > 0) balanceText = `\u26a0\ufe0f \u0627\u0644\u0645\u0648\u0631\u062f \u0644\u064a\u0647 \u0641\u064a \u0630\u0645\u062a\u0646\u0627: ${balance.toLocaleString()} \u062c\u0646\u064a\u0647`;\nelse if (balance < 0) balanceText = `\u2705 \u0644\u064a\u0646\u0627 \u0639\u0646\u062f \u0627\u0644\u0645\u0648\u0631\u062f: ${Math.abs(balance).toLocaleString()} \u062c\u0646\u064a\u0647`;\nelse balanceText = `\ud83d\udc4c \u0627\u0644\u062d\u0633\u0627\u0628 \u0645\u0635\u0641\u0631 (\u062e\u0627\u0644\u0635\u064a\u0646)`;\n\nlet report = `\ud83e\uddfe \u0643\u0634\u0641 \u062d\u0633\u0627\u0628 \u0645\u062c\u0645\u0639: ${supplierName}\\n`;\nreport += `--------------------------\\n`;\nreport += `\u0623\u062d\u062f\u062b \u0627\u0644\u062d\u0631\u0643\u0627\u062a:\\n${details}`;\nif (items.length > 10) report += `... (\u062a\u0645 \u0625\u062e\u0641\u0627\u0621 \u0628\u0627\u0642\u064a \u0627\u0644\u062d\u0631\u0643\u0627\u062a \u0627\u0644\u0642\u062f\u064a\u0645\u0629)\\n`;\nreport += `--------------------------\\n`;\nreport += `\u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0644\u064a \u0646\u0632\u0644 \u062d\u0633\u0627\u0628\u0647: ${totalCredit.toLocaleString()} \u062c\\n`;\nreport += `\u0625\u062c\u0645\u0627\u0644\u064a \u0627\u0644\u0644\u064a \u0625\u062a\u062f\u0641\u0639\u0640\u0644\u0640\u0647: ${totalDebit.toLocaleString()} \u062c\\n`;\nreport += `--------------------------\\n`;\nreport += `\ud83d\udcb0 \u0627\u0644\u0635\u0627\u0641\u064a \u0627\u0644\u0646\u0647\u0627\u0626\u064a:\\n${balanceText}\\n`;\nreport += `--------------------------\\n`;\nreport += `\ud83d\udc81\u200d\u2640\ufe0f \u0627\u0644\u0646\u0638\u0627\u0645: \u0627\u0644\u062a\u0642\u0631\u064a\u0631 \u0645\u062a\u0642\u0641\u0644\u060c \u0623\u0623\u0645\u0631\u0646\u064a \u0628\u0627\u0644\u0644\u064a \u0628\u0639\u062f\u0647!`;\n\nreturn { json: { report_text: report } };"
      },
      "typeVersion": 2
    },
    {
      "id": "f49324c9-3ab3-487c-9618-09ea2a980020",
      "name": "Success Message",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        1040
      ],
      "parameters": {
        "jsCode": "const aiData = $('Clean AI Output').first().json.entities;\nconst amount = aiData.amount || 0;\nconst target = aiData.target_name || aiData.category_or_type || \"\u0627\u0644\u0639\u0645\u0644\u064a\u0629\";\n\nlet report = `\ud83d\udc81\u200d\u2640\ufe0f \u0627\u0644\u0646\u0638\u0627\u0645: \u0643\u0644\u0647 \u062a\u0645\u0627\u0645 \u064a\u0627 \u0631\u064a\u0633! \u2705\\n`;\nreport += `\u062a\u0645 \u0625\u062b\u0628\u0627\u062a \u0648\u062a\u0633\u062c\u064a\u0644 (${target}) \u0628\u0645\u0628\u0644\u063a ${amount.toLocaleString()} \u062c\u0646\u064a\u0647 \u0641\u064a \u0627\u0644\u062f\u0641\u0627\u062a\u0631 \u0628\u0646\u062c\u0627\u062d!`;\n\nreturn { json: { report_text: report } };"
      },
      "typeVersion": 2
    },
    {
      "id": "e30a0644-0b16-43b9-ad5b-533bf399c807",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3280,
        1120
      ],
      "parameters": {
        "width": 1184,
        "height": 432,
        "content": "### How it works\nThis workflow acts as an AI-powered financial assistant, connecting chat platforms (like Telegram or Botpress) to your Odoo ERP. \n\nWhen a user sends a natural language message in Arabic (e.g., \"I spent 2000 on office supplies\"), an OpenAI LangChain agent extracts the intent, amounts, dates, and account categories. The workflow then routes the request to either post a new double-entry journal record in Odoo (for expenses, supplier payments, or employee advances) or fetch real-time account balances to send a formatted Arabic report back to the chat.\n\n### Setup steps\n1. **Credentials:** Connect your Odoo, OpenAI, and Google Calendar credentials.\n2. **Webhook:** Add the Webhook URL to your Telegram/Botpress bot setup.\n3. **Prompt Mapping:** Open the \"AI Financial Agent\" node and update the `# ACCOUNT MAPPING` section with your specific Odoo Chart of Accounts codes (e.g., replace `[EXPENSE_1_CODE]` with your actual Odoo account code).\n4. **Code Nodes:** Open the \"Build [X] Entry\" Code nodes and replace the placeholder `journal_id` and `currency_id` with your Odoo internal IDs.\n\n### Customization tips\nYou can easily expand the `Route to Expense Path` Switch node to handle other operations, such as creating customer invoices or logging petty cash transfers."
      },
      "typeVersion": 1
    },
    {
      "id": "851894c1-1675-4c91-829f-705ab0d597dd",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2000,
        1168
      ],
      "parameters": {
        "color": 7,
        "width": 720,
        "height": 512,
        "content": "## 1. Receive & Classify Intent\nReceives the chat message and uses an AI agent to extract the financial operation, amounts, dates, and target accounts into a clean JSON format."
      },
      "typeVersion": 1
    },
    {
      "id": "843171a8-7bff-4999-8784-7aacb06ea19b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1216,
        1072
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 304,
        "content": "## 2. Route Operation\nDirects the workflow down the correct path based on whether the user is logging an expense, paying a supplier, or requesting a balance report."
      },
      "typeVersion": 1
    },
    {
      "id": "e28f9027-4299-4081-84ca-b1800e8c75f3",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        448
      ],
      "parameters": {
        "color": 7,
        "width": 608,
        "height": 1280,
        "content": "## 3. Odoo Data Lookups\nSearches Odoo to find the internal internal `account_id` and `partner_id` required to build accurate journal entries."
      },
      "typeVersion": 1
    },
    {
      "id": "ae32833a-f179-4156-84ea-d6fc42a4e3a0",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        416,
        352
      ],
      "parameters": {
        "color": 7,
        "height": 1376,
        "content": "## 4. Build & Post Entries\nConstructs the double-entry accounting arrays (debits and credits) and automatically posts the confirmed journal entries into Odoo."
      },
      "typeVersion": 1
    },
    {
      "id": "b146e4fe-a168-4549-8d5f-91006809df52",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -720,
        848
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 240,
        "content": "## 5. Schedule Checks\nParses future due dates for physical check payments and creates a Google Calendar event reminder."
      },
      "typeVersion": 1
    },
    {
      "id": "918285b8-5950-44e2-a439-5569962c8db0",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        912,
        1744
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 416,
        "content": "## 6. Reports & Chat Response\nCalculates net balances from Odoo ledger lines, generates a formatted Arabic summary, and sends the response back to the user via Webhook."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "ef929821-22a6-4dd1-87f6-d09f47581fb4",
  "connections": {
    "Date Resolver": {
      "main": [
        [
          {
            "node": "Fetch Safe/Bank Move Lines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Financial Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Get many items": {
      "main": [
        [
          {
            "node": "Calculate Supplier Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean AI Output": {
      "main": [
        [
          {
            "node": "Route to Expense Path",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create an event": {
      "main": [
        [
          {
            "node": "Find Partner ID2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Partner ID": {
      "main": [
        [
          {
            "node": "Find Debit Account ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Success Message": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Partner ID2": {
      "main": [
        [
          {
            "node": "Find Debit Account ID1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Partner ID3": {
      "main": [
        [
          {
            "node": "Find Debit Account ID2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Partner ID4": {
      "main": [
        [
          {
            "node": "Find Debit Account ID3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Partner ID5": {
      "main": [
        [
          {
            "node": "Find Debit Account ID4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Supplier ID": {
      "main": [
        [
          {
            "node": "Get many items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bot Data Receiver": {
      "main": [
        [
          {
            "node": "AI Financial Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Odoo Entry": {
      "main": [
        [
          {
            "node": "Confirm & Post Entry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Check Date": {
      "main": [
        [
          {
            "node": "Create an event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Financial Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "AI Financial Agent": {
      "main": [
        [
          {
            "node": "Clean AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Final Report": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Journal Lines": {
      "main": [
        [
          {
            "node": "Create Discount Entry1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Discount Entry": {
      "main": [
        [
          {
            "node": "Create Discount Entry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirm & Post Entry": {
      "main": [
        [
          {
            "node": "Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Cash Account ID": {
      "main": [
        [
          {
            "node": "Build Odoo Journal Entry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirm & Post Entry1": {
      "main": [
        [
          {
            "node": "Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirm & Post Entry2": {
      "main": [
        [
          {
            "node": "Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirm & Post Entry3": {
      "main": [
        [
          {
            "node": "Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirm & Post Entry4": {
      "main": [
        [
          {
            "node": "Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Confirm & Post Entry5": {
      "main": [
        [
          {
            "node": "Success Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Discount Entry": {
      "main": [
        [
          {
            "node": "Confirm & Post Entry1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Payment Source": {
      "main": [
        [
          {
            "node": "Format Check Date",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find Partner ID3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Debit Account ID": {
      "main": [
        [
          {
            "node": "Find Credit Account ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route to Expense Path": {
      "main": [
        [
          {
            "node": "Find Expense Account ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find Partner ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Filter Payment Source",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find Partner ID4",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find Partner ID5",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Find Supplier ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Date Resolver",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Discount Entry1": {
      "main": [
        [
          {
            "node": "Confirm & Post Entry2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Discount Entry2": {
      "main": [
        [
          {
            "node": "Confirm & Post Entry3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Discount Entry3": {
      "main": [
        [
          {
            "node": "Confirm & Post Entry4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Discount Entry4": {
      "main": [
        [
          {
            "node": "Confirm & Post Entry5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Credit Account ID": {
      "main": [
        [
          {
            "node": "Build Discount Entry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Debit Account ID1": {
      "main": [
        [
          {
            "node": "Find Credit Account ID1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Debit Account ID2": {
      "main": [
        [
          {
            "node": "Find Credit Account ID2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Debit Account ID3": {
      "main": [
        [
          {
            "node": "Find Credit Account ID3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Debit Account ID4": {
      "main": [
        [
          {
            "node": "Find Credit Account ID4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Credit Account ID1": {
      "main": [
        [
          {
            "node": "Build Journal Lines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Credit Account ID2": {
      "main": [
        [
          {
            "node": "Build Instant Entry Lines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Credit Account ID3": {
      "main": [
        [
          {
            "node": "Build Invoice Entry Lines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Credit Account ID4": {
      "main": [
        [
          {
            "node": "Build Advance Entry Lines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Expense Account ID": {
      "main": [
        [
          {
            "node": "Find Cash Account ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Odoo Journal Entry": {
      "main": [
        [
          {
            "node": "Create Odoo Entry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Advance Entry Lines": {
      "main": [
        [
          {
            "node": "Create Discount Entry4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Instant Entry Lines": {
      "main": [
        [
          {
            "node": "Create Discount Entry2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Invoice Entry Lines": {
      "main": [
        [
          {
            "node": "Create Discount Entry3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Supplier Balance": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Safe/Bank Move Lines": {
      "main": [
        [
          {
            "node": "Build Final Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}