{
  "name": "Arvifund - Supabase (Fixed v2)",
  "active": true,
  "isArchived": false,
  "activeVersionId": "9fd6abf8-83ed-4836-960b-8d4f60867ca5",
  "triggerCount": 4,
  "createdAt": "2026-05-27T04:56:19.164Z",
  "updatedAt": "2026-05-27T05:17:46.896Z",
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "timeSavedMode": "fixed",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": true
  },
  "connections": {
    "Switch": {
      "main": [
        [
          {
            "node": "Get File Path",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram7",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram5",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Report Hari Ini",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Parse Command",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get a file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analisa gambar": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If": {
      "main": [
        [
          {
            "node": "Respon error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets": {
      "main": [
        [
          {
            "node": "Telegram1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "AI INPUT DATA TEXT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If2": {
      "main": [
        [
          {
            "node": "Tarik Tunai",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Expenses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch1": {
      "main": [
        [
          {
            "node": "If2",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Income",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tarik Tunai": {
      "main": [
        [
          {
            "node": "Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Expenses": {
      "main": [
        [
          {
            "node": "Telegram3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Income": {
      "main": [
        [
          {
            "node": "Telegram6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "AI Agent1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent1": {
      "main": [
        [
          {
            "node": "Telegram4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a file": {
      "main": [
        [
          {
            "node": "Transcribe a recording",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe a recording": {
      "main": [
        [
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "AI INPUT VOICE NOTE",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "node": "AI INPUT VOICE NOTE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory1": {
      "ai_memory": [
        [
          {
            "node": "AI INPUT DATA TEXT",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory2": {
      "ai_memory": [
        [
          {
            "node": "AI INPUT VOICE NOTE",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI INPUT DATA TEXT",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "AI INPUT DATA TEXT": {
      "main": [
        [
          {
            "node": "Switch1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI INPUT VOICE NOTE": {
      "main": [
        [
          {
            "node": "Switch1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cek Ada Data": {
      "main": [
        [
          {
            "node": "Agregasi dan Render HTML",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Kirim Error No Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cek PDF Berhasil": {
      "main": [
        [
          {
            "node": "Ubah nama dokumen",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Kirim Error PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML to PDF": {
      "main": [
        [
          {
            "node": "Cek PDF Berhasil",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agregasi dan Render HTML": {
      "main": [
        [
          {
            "node": "HTML to PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Data": {
      "main": [
        [
          {
            "node": "Cek Ada Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Income": {
      "main": [
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Read Expenses": {
      "main": [
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Processing": {
      "main": [
        [
          {
            "node": "Read Income",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Expenses",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Data",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Parse Command": {
      "main": [
        [
          {
            "node": "Edit Fields2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Kirim PDF ke Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields2": {
      "main": [
        [
          {
            "node": "Merge Data1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ubah nama dokumen": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Command (Webhook)": {
      "main": [
        [
          {
            "node": "Edit Fields (Webhook)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields (Webhook)": {
      "main": [
        [
          {
            "node": "Merge Data1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Data1": {
      "main": [
        [
          {
            "node": "Edit Fields (Webhook)1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields (Webhook)1": {
      "main": [
        [
          {
            "node": "Telegram Processing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "Parse Command (Webhook)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get File Path": {
      "main": [
        [
          {
            "node": "Download File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download File": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Analisa gambar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait2": {
      "main": [
        [
          {
            "node": "Get Config Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook1": {
      "main": [
        [
          {
            "node": "Wait2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Config Sheet": {
      "main": [
        [
          {
            "node": "Filter by Username",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter by Username": {
      "main": [
        [
          {
            "node": "User Ditemukan?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "User Ditemukan?": {
      "main": [
        [
          {
            "node": "Kirim Reset Password",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Response Not Found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kirim via Telegram": {
      "main": [
        [
          {
            "node": "Response OK",
            "type": "main",
            "index": 0
          },
          {
            "node": "Response Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Response Error": {
      "main": [
        [
          {
            "node": "Response Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Report Hari Ini": {
      "main": [
        [
          {
            "node": "HTTP Request Hari Ini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request Hari Ini": {
      "main": [
        [
          {
            "node": "Format Laporan Hari Ini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Laporan Hari Ini": {
      "main": [
        [
          {
            "node": "Kirim Laporan Hari Ini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cron Backup Periode": {
      "main": [
        [
          {
            "node": "Cek Akhir Periode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cek Akhir Periode": {
      "main": [
        [
          {
            "node": "Apakah Akhir Periode?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apakah Akhir Periode?": {
      "main": [
        [
          {
            "node": "Fetch Data Periode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Data Periode": {
      "main": [
        [
          {
            "node": "Build Excel Backup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Excel Backup": {
      "main": [
        [
          {
            "node": "Cari Folder Tahun",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cari Folder Tahun": {
      "main": [
        [
          {
            "node": "Folder Tahun Ada?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Folder Tahun Ada?": {
      "main": [
        [
          {
            "node": "Merge Folder ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Buat Folder Tahun",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buat Folder Tahun": {
      "main": [
        [
          {
            "node": "Merge Folder ID",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Folder ID": {
      "main": [
        [
          {
            "node": "Upload Excel ke GDrive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Excel ke GDrive": {
      "main": [
        [
          {
            "node": "Notif Backup Berhasil",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Kirim Reset Password": {
      "main": [
        [
          {
            "node": "Kirim via Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "nodes": [
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "ad99e257-bcaf-42a2-b068-53e0c96729db",
                    "leftValue": "={{ $json.message.photo[2] }}",
                    "rightValue": "",
                    "operator": {
                      "type": "object",
                      "operation": "exists",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "69cae014-32a5-44e4-abb1-bd8324df9b65",
                    "leftValue": "={{ $json.message.text }}",
                    "rightValue": "=/report",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "dc0ba5e6-f00f-40d7-acd8-e639d4fd38da",
                    "leftValue": "={{ $json.message.text }}",
                    "rightValue": "/start",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "4f8d9425-641e-4044-a54a-d73444ea28e0",
                    "leftValue": "={{ $json.message.text }}",
                    "rightValue": "/help",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "report-hari-001",
                    "leftValue": "={{ $json.message.text }}",
                    "rightValue": "/hariini",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "f0be09d4-a09a-45be-ae7a-9becc676ed4c",
                    "leftValue": "={{ $json.message.text }}",
                    "rightValue": "/laporanbulanan",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "41e89fcf-d3a4-4a71-9a0b-5b0d7ea841fc",
                    "leftValue": "={{ $json.message.text }}",
                    "rightValue": "/",
                    "operator": {
                      "type": "string",
                      "operation": "exists",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "faff551c-7e32-447f-83a9-f126bc57664f",
                    "leftValue": "={{ $json.message.voice }}",
                    "rightValue": "",
                    "operator": {
                      "type": "object",
                      "operation": "exists",
                      "singleValue": true
                    }
                  }
                ],
                "combinator": "and"
              }
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        -496,
        1056
      ],
      "id": "c8f35391-7b13-41b6-b5da-961f6dad0227",
      "name": "Switch"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "23f4316d-5a77-45ae-81e7-18ee4cacb995",
              "leftValue": "={{ $json.candidates[0].content.parts[0].text }}",
              "rightValue": "ERROR:",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {
          "ignoreCase": true
        }
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        544,
        368
      ],
      "id": "08d23dad-c3eb-403b-98e1-9179a61101ff",
      "name": "If"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=<redacted-credential>",
        "sendBody": true,
        "specifyBody": "=json",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "jsonBody": "={{ JSON.stringify({ contents: [{ parts: [{ inline_data: { mime_type: \"image/jpeg\", data: $json.base64Image } }, { text: \"Analisa struk belanja dalam foto ini dan kembalikan HANYA dalam format berikut tanpa teks lain:\\n\\nTOKO: [nama toko]\\nTOTAL: [total dalam angka saja tanpa Rp]\\nITEMS: [daftar barang]\\nKATEGORI: [Makanan & Minuman/Tagihan/Kosmetik & Perawatan/Transportasi/Kesehatan/Pakaian/Elektronik/Rumah Tangga/Pendidikan/Hiburan/Cicilan/Investasi/Lainnya]\\nJENIS: Pengeluaran\\nMETODE: [Cash/QRIS/Transfer/Card/Cardless]\\nBANK: [nama bank, jika tidak ada tulis BRI]\\nTANGGAL: [tanggal hari ini format YYYY-MM-DD]\\nTANGGAL_STRUK: [tanggal di struk format YYYY-MM-DD]\" }] }] }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        304,
        384
      ],
      "id": "1184b22f-f20b-4638-90c3-2a6dc0875d58",
      "name": "Analisa gambar",
      "retryOnFail": true,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "Struk tidak dapat dibaca dengan jelas.\n\nPastikan:\n- Foto struk jelas dan tidak blur\n- Pencahayaan cukup\n- Semua text terlihat\n\nSilakan coba foto ulang! \ud83d\udcf7",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        768,
        272
      ],
      "id": "338fe1eb-1e93-43b2-b3c0-367a87b5f536",
      "name": "Respon error"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "16yhooZ5cjFQwSuuDSIZNO6DMQKLVdNtzcY_HubGN3XA",
          "mode": "list",
          "cachedResultName": "Cash flow new V1.0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16yhooZ5cjFQwSuuDSIZNO6DMQKLVdNtzcY_HubGN3XA/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "Expenses",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Tanggal": "={{ $json.candidates[0].content.parts[0].text.match(/TANGGAL_STRUK: (.+)/)?.[1]?.trim().split('-').reverse().join(' ') }}",
            "Bulan": "={{ ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'][parseInt($json.candidates[0].content.parts[0].text.match(/TANGGAL_STRUK: (.+)/)?.[1]?.trim().split('-')[1]) - 1] }}",
            "Transaksi": "={{ $json.candidates[0].content.parts[0].text.match(/METODE: (.+)/)[1] }}",
            "Uraian": "={{ $json.candidates[0].content.parts[0].text.match(/ITEMS: (.+)/)[1] }}",
            "Kategori": "={{ $json.candidates[0].content.parts[0].text.match(/KATEGORI: (.+)/)[1] }}",
            "Bank": "={{ $json.candidates[0].content.parts[0].text.match(/BANK: (.+)/)[1] }}",
            "Nilai": "={{ $json.candidates[0].content.parts[0].text.match(/TOTAL: (.+)/)[1] }}",
            "Toko": "={{ $json.candidates[0].content.parts[0].text.match(/TOKO: (.+)/)[1] }}",
            "user": "={{ $('Telegram Trigger').item.json.message.from.first_name }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Toko",
              "displayName": "Toko",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Tanggal",
              "displayName": "Tanggal",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Bulan",
              "displayName": "Bulan",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Transaksi",
              "displayName": "Transaksi",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Uraian",
              "displayName": "Uraian",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Kategori",
              "displayName": "Kategori",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Bank",
              "displayName": "Bank",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Nilai",
              "displayName": "Nilai",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "user",
              "displayName": "user",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.6,
      "position": [
        768,
        480
      ],
      "id": "15cc46d3-7124-4365-bcd0-51ca0065c21b",
      "name": "Google Sheets"
    },
    {
      "parameters": {
        "content": "## Workflow untuk upload gambar struk",
        "height": 348,
        "width": 1844,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        288
      ],
      "typeVersion": 1,
      "id": "dbe49b27-6855-4adf-b43b-d5b234ba437c",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "=\u2705 *Lapor Baba/Ibu, data sudah Pijan simpan!*\n\n\ud83c\udfea *Toko:* {{ $json['Toko'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Nilai }}\n\ud83d\udce6 *Uraian:* {{ $json.Uraian }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Transaksi }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\n\ud83d\udcca *Hore! Datanya Sudah masuk ke Google Sheets ya..*\nMakasih ya Baba dan Ibu sudah rajin catat pengeluaran hari ini! Pijan pinter kan? Semangat terus cari rezekinya buat Pijan! \u2764\ufe0f\ud83d\ude80\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        992,
        480
      ],
      "id": "a39f9820-f5bc-48ea-8f0c-c242f079adae",
      "name": "Telegram1"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "=\u2705 *Lapor Baba/Ibu, catatannya sudah Pijan simpan!*\n\n\ud83c\udfea *Toko:* {{ $json['Toko'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Nilai }}\n\ud83d\udce6 *Uraian:* {{ $json.Uraian }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Transaksi }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\n\ud83d\udcca *Data belanja kita sudah aman di Google Sheets!*\nMakasih ya Baba dan Ibu sudah rajin catat pengeluaran hari ini. Pijan seneng deh liat keluarga kita rapi catatannya. Pijan pinter kan? Jangan lupa istirahat ya Baba, Ibu.. Sayang kalian! \u2764\ufe0f\ud83d\ude80\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1296,
        2400
      ],
      "id": "ef3539b8-a84a-4676-97ca-a1f5a33575b5",
      "name": "Telegram3"
    },
    {
      "parameters": {
        "chatId": "={{ $json.message.chat.id }}",
        "text": "<b>\ud83e\udd16 Instruksi Bot Pijan</b>\n\n\ud83d\udcf8 <b>Kirim foto struk</b> \u2794 Otomatis tersimpan\n\u270d\ufe0f <b>Ketik pembelian</b> \u2794 \"beli telur 20rb di warung Ajun Cash\"\n\ud83c\udfe7 <b>Catat Tarik Tunai</b> \u2794 \"Tarik tunai 500k bca di mandiraja\"\n\ud83d\udcca <b>Cek laporan</b> \u2794 /laporanbulanan cth(/laporanbulananapril) maka otomatis terkirim ke Email & /report untuk laporan bulan sekarrang via telegram\n\ud83d\udcf2 <b>Dashboard view</b> \u2794 Klik link di bawah ini:\n\n<a href=\"https://script.google.com/macros/s/AKfycbwjVvhLEs2RQiH_DiTTvGKrMW43yoTG74_pH_Z5NOA_Dp6R3P8pu0__K6FnKWrzfqQg/exec\">Buka Dashboard Keuangan</a>\n\n// Kalau ada yang error atau bingung, tanya Baba aja ya!\n\nMulai dengan kirim foto struk atau ketik belanja Ibu dan Baba pijan \u2764\ufe0f\ud83d\ude80\nKalian jangan boros yaa \ud83d\ude0a\n\n<b>ARVIDZAN REZQIANO DARMAWAN</b> \ud83d\udc69\u200d\ud83d\ude80",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "HTML"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        144,
        1216
      ],
      "id": "91868cae-3642-46a4-86cb-5e4dca0f1991",
      "name": "Telegram5"
    },
    {
      "parameters": {
        "content": "## Request laporan pengeluaran",
        "height": 412,
        "width": 968,
        "color": 6
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        48,
        704
      ],
      "typeVersion": 1,
      "id": "dedfec8d-b1fb-4912-acfa-e46902580fdc",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Request /help",
        "height": 240,
        "width": 316
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        1136
      ],
      "typeVersion": 1,
      "id": "2620afdb-2fbd-4c62-8b04-0d48b488e233",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Catatan ##\nPastikan atur kembali credentials dan API Key sesuai punya kamu",
        "height": 120,
        "width": 300
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1008,
        928
      ],
      "typeVersion": 1,
      "id": "97c3013f-7a39-44ee-aba1-a9450e372cd6",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "1ed531e9-9c13-4138-a2aa-ca451a082d4b",
              "name": "tanggal_hari_ini",
              "value": "={{ $now.setZone('Asia/Jakarta').toFormat('yyyy-MM-dd') }}",
              "type": "string"
            },
            {
              "id": "c37a9824-a584-44c7-a9f8-523d4fcde446",
              "name": "tanggal_kemarin",
              "value": "={{ $now.setZone('Asia/Jakarta').minus({ days: 1 }).toFormat('yyyy-MM-dd') }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        128,
        2256
      ],
      "id": "21970fb5-ead3-40b9-a256-9cad2b1f0938",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "e286720f-68ed-4959-89d0-47e0b836c494",
              "leftValue": "={{ $json.output.match(/ITEMS: (.+)/)[1] }}",
              "rightValue": "Tarik Tunai",
              "operator": {
                "type": "string",
                "operation": "equals",
                "name": "filter.operator.equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        848,
        2304
      ],
      "id": "f9cf0c7f-29d8-434b-8604-ddac06261c45",
      "name": "If2"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "=\u2705 *Lapor Baba/Ibu, Pijan sudah catat tarik tunainya!*\n\n\ud83c\udfea *Alamat Penarikan:* {{ $json['Alamat'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Nilai }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Transaksi }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\ud83d\udcb3 *Metode:* {{ $json.Metode }}\n\n\ud83d\udcca *Hore! Saldo cash sudah Pijan update ya..*\nMakasih ya Baba/Ibu sudah lapor ke Pijan. Uang cash-nya jangan lupa ditaruh di dompet yang rapi, jangan sampai nyelip yaa! Pijan pinter kan? \u2764\ufe0f\ud83d\ude80\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1296,
        2224
      ],
      "id": "3289c3ba-8135-45f2-8492-9b5ae06317e4",
      "name": "Telegram"
    },
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {
          "download": true
        }
      },
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.2,
      "position": [
        -912,
        1120
      ],
      "id": "465b8d56-bdba-4e30-bfd9-d36354f1aa21",
      "name": "Telegram Trigger"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "ad99e257-bcaf-42a2-b068-53e0c96729db",
                    "leftValue": "={{ $json.output }}",
                    "rightValue": "Pengeluaran",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "69cae014-32a5-44e4-abb1-bd8324df9b65",
                    "leftValue": "={{ $json.output }}",
                    "rightValue": "=Pemasukan",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "d49f1856-45af-4c66-86a6-d51e2371d257",
                    "leftValue": "={{ $json.output }}",
                    "rightValue": "ERROR:",
                    "operator": {
                      "type": "string",
                      "operation": "startsWith"
                    }
                  }
                ],
                "combinator": "and"
              }
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        672,
        2416
      ],
      "id": "f49154ef-0c81-40f9-9069-6c56bf1e71f8",
      "name": "Switch1"
    },
    {
      "parameters": {
        "url": "=https://script.google.com/macros/s/AKfycbxTgawdxMBhnJd_gy1Obl1yiAUId0NYWM55L50N-Wx4eeR4KbnRhZm6rIK8UUiwPLQE/exec?action=laporan",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        144,
        784
      ],
      "id": "18d714cd-15f8-4db9-9196-318a5594e662",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "=\u2705 *Data Income berhasil disimpan!*\n\n\ud83c\udfea *Toko:* {{ $json['Sumber'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Jumlah }}\n\ud83d\udce6 *Uraian:* {{ $json.Items }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Metode }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\n\ud83d\udcca *Data Pemasukan kita sudah aman di Google Sheets!*\nMakasih ya Baba dan Ibu sudah rajin catat pengeluaran hari ini. Pijan seneng deh liat keluarga kita rapi catatannya. Pijan pinter kan? Jangan lupa istirahat ya Baba, Ibu.. Arvidzan Sayang kalian! \u2764\ufe0f\ud83d\ude80",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1136,
        2560
      ],
      "id": "1676388c-d84f-42f8-897b-d67def499b28",
      "name": "Telegram6"
    },
    {
      "parameters": {
        "tableId": "cash_records",
        "operation": "create",
        "dataToSend": "defineBelow",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "tanggal",
              "fieldValue": "={{ $json.output.match(/TANGGAL_STRUK: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "bulan",
              "fieldValue": "={{ (() => { const d = new Date($json.output.match(/TANGGAL_STRUK: (.+)/)[1].trim()); const b = ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember']; return b[d.getMonth()]; })() }}"
            },
            {
              "fieldId": "transaksi",
              "fieldValue": "={{ $json.output.match(/ITEMS: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "kategori",
              "fieldValue": "={{ $json.output.match(/JENIS: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "bank",
              "fieldValue": "={{ $json.output.match(/BANK: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "nilai",
              "fieldValue": "={{ parseFloat($json.output.match(/TOTAL: (.+)/)[1].trim()) }}"
            },
            {
              "fieldId": "alamat",
              "fieldValue": "={{ $json.output.match(/TOKO: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "metode",
              "fieldValue": "={{ $json.output.match(/METODE: (.+)/)[1].trim() }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1136,
        2224
      ],
      "id": "43b66da5-241c-400b-b5c6-05155293741d",
      "name": "Tarik Tunai",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## Input data manual & Voice note",
        "height": 896,
        "width": 1576,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        16,
        2080
      ],
      "typeVersion": 1,
      "id": "d70bf74d-ee7b-4ee9-9097-f7f42218e2f0",
      "name": "note"
    },
    {
      "parameters": {
        "tableId": "expenses",
        "operation": "create",
        "dataToSend": "defineBelow",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "toko",
              "fieldValue": "={{ $json.output.match(/TOKO: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "tanggal",
              "fieldValue": "={{ $json.output.match(/TANGGAL_STRUK: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "bulan",
              "fieldValue": "={{ (() => { const d = new Date($json.output.match(/TANGGAL_STRUK: (.+)/)[1].trim()); const b = ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember']; return b[d.getMonth()]; })() }}"
            },
            {
              "fieldId": "transaksi",
              "fieldValue": "={{ $json.output.match(/METODE: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "uraian",
              "fieldValue": "={{ $json.output.match(/ITEMS: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "kategori",
              "fieldValue": "={{ $json.output.match(/KATEGORI: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "bank",
              "fieldValue": "={{ $json.output.match(/BANK: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "nilai",
              "fieldValue": "={{ parseFloat($json.output.match(/TOTAL: (.+)/)[1].trim()) }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1136,
        2400
      ],
      "id": "b3e6e0ee-687a-4ddd-b1e9-c497d2021bf8",
      "name": "Expenses",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "tableId": "income",
        "operation": "create",
        "dataToSend": "defineBelow",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "sumber",
              "fieldValue": "={{ $json.output.match(/TOKO: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "tanggal",
              "fieldValue": "={{ $json.output.match(/TANGGAL_STRUK: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "bulan",
              "fieldValue": "={{ (() => { const d = new Date($json.output.match(/TANGGAL_STRUK: (.+)/)[1].trim()); const b = ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember']; return b[d.getMonth()]; })() }}"
            },
            {
              "fieldId": "jumlah",
              "fieldValue": "={{ parseFloat($json.output.match(/TOTAL: (.+)/)[1].trim()) }}"
            },
            {
              "fieldId": "metode",
              "fieldValue": "={{ $json.output.match(/METODE: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "kategori",
              "fieldValue": "={{ $json.output.match(/JENIS: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "items",
              "fieldValue": "={{ $json.output.match(/ITEMS: (.+)/)[1].trim() }}"
            },
            {
              "fieldId": "bank",
              "fieldValue": "={{ $json.output.match(/BANK: (.+)/)[1].trim() }}"
            }
          ]
        }
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        848,
        2432
      ],
      "id": "6cdbe462-0e2c-480e-ac10-d3333ae2b6aa",
      "name": "Income",
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ JSON.stringify($json) }}",
        "options": {
          "systemMessage": "=Buatkan laporan keuangan dari data berikut:\n{{ JSON.stringify($json) }}\n\nGunakan format ini persis:\n\n\ud83d\udcca *LAPORAN KEUANGAN*\n\ud83d\uddd3 Bulan: [bulan terbanyak transaksi] [Tahun Ini]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcb0 *Total Pemasukan:* Rp [angka dengan titik ribuan]\n\ud83d\udcb8 *Total Pengeluaran:* Rp [angka dengan titik ribuan]\n\ud83c\udfe7 *Tarik Tunai:* Rp [angka dengan titik ribuan]\n\ud83d\udcb5 *Saldo:* Rp [angka dengan titik ribuan]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcb5 *SALDO CASH PER USER*\n[nama user]\n  \ud83c\udfe7 Tarik Tunai: Rp [tarikTunaiByUser]\n  \ud83d\udcb8 Terpakai: Rp [expensesByUser]\n  \u2705 Sisa Cash: Rp [saldoCashByUser]\n[ulangi untuk tiap user]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcc2 *KATEGORI PENGELUARAN*\n[list tiap kategori dan jumlahnya]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83c\udfe6 *BANK / METODE BAYAR*\n[list tiap bank/metode dan jumlahnya]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udd50 *10 TRANSAKSI TERAKHIR*\n\nNo | Tanggal | Toko & Uraian | Kategori | Nilai | User\n---|---------|---------------|----------|-------|-----\n[nomor] | [tanggal] | [toko - uraian] | [kategori] | Rp [nilai] | [user]\n[ulangi 10 baris]\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udca1 *SARAN Arvidzan*\n[Berikan 3-5 saran finansial singkat dan personal berdasarkan pola pengeluaran di atas. Gunakan bullet point dengan emoji yang relevan. Saran harus spesifik, bukan generik.]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\nLaporan ini dibuatin sama Arvidzan Pinter \ud83d\udc76\ud83d\ude80\nBaba sama Ibu semangat ya cari rezekinya\n\nGunakan format angka Rupiah dengan titik ribuan.\nJangan tambah teks lain diluar format di atas.\nFormat tabel transaksi gunakan karakter | sebagai pemisah kolom (bukan tanda -)."
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        352,
        784
      ],
      "id": "7a660855-96c5-4626-adf9-59a738e71608",
      "name": "AI Agent1"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "={{ $json.output }}",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        736,
        784
      ],
      "id": "7033df18-c62d-4654-ad03-6bbbf45c039b",
      "name": "Telegram4"
    },
    {
      "parameters": {
        "chatId": "={{ $json.message.chat.id }}",
        "text": "Assalamu'alaikum Baba & Ibu! \ud83d\udc76\u2728\n\n<b>Arvidzan (Pijan)</b> lapor! Arvidzan sudah siap jadi asisten keuangan pribadi kesayangan Baba dan Ibu. Bot ini Pijan buat khusus buat kita bertiga biar catatan belanja rumah kita rapi terus.\n\n<b>Caranya gampang banget:</b>\n\u2705 Kirim <b>Foto Struk</b> kalau Baba/Ibu lagi malas ngetik.\n\u2705 <b>Ketik langsung</b> (misal: \"Beli bakso 20rb\") kalau habis jajan.\n\u2705 Ketik <b>/report</b> kalau mau lihat uang kita sisa berapa.\n\nIbu sama Baba semangat ya cari rezekinya! Tapi ingat kata Pijan... jangan boros-boros yaa, buat tabungan Arvidzan masa depan hehehe.. \ud83d\ude0a\ud83d\ude80\n\nKlik <b>/help</b> kalau Baba atau Ibu bingung cara pakenya.\n\nSayang Baba & Ibu selalu,\n<b>ARVIDZAN REZQIANO DARMAWAN</b> \ud83d\udc69\u200d\ud83d\ude80",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "HTML"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        464,
        1216
      ],
      "id": "4fde70f2-877a-41f4-927b-c48dd1d8ef24",
      "name": "Telegram7"
    },
    {
      "parameters": {
        "content": "## Start",
        "height": 240,
        "width": 300,
        "color": "#FFB3B3"
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        1136
      ],
      "typeVersion": 1,
      "id": "9e318a48-4653-43bb-853d-c84e5afe4471",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "resource": "file",
        "fileId": "={{ $json.message.voice.file_id }}",
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        48,
        2608
      ],
      "id": "e98317cf-b4ba-4531-a8f3-9393ac72023c",
      "name": "Get a file"
    },
    {
      "parameters": {
        "resource": "audio",
        "modelId": {
          "__rl": true,
          "value": "models/gemini-2.5-flash",
          "mode": "list",
          "cachedResultName": "models/gemini-2.5-flash"
        },
        "inputType": "binary",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.1,
      "position": [
        176,
        2608
      ],
      "id": "f689209b-9088-4d0c-ae13-eb67dfebe139",
      "name": "Transcribe a recording"
    },
    {
      "parameters": {
        "modelName": "models/gemini-2.5-flash-lite",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1,
      "position": [
        416,
        2752
      ],
      "id": "d5e677d6-2540-4b40-a670-97b04c2e79ab",
      "name": "Google Gemini Chat Model2"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "1ed531e9-9c13-4138-a2aa-ca451a082d4b",
              "name": "tanggal_hari_ini",
              "value": "={{ $now.setZone('Asia/Jakarta').toFormat('yyyy-MM-dd') }}",
              "type": "string"
            },
            {
              "id": "c37a9824-a584-44c7-a9f8-523d4fcde446",
              "name": "tanggal_kemarin",
              "value": "={{ $now.setZone('Asia/Jakarta').minus({ days: 1 }).toFormat('yyyy-MM-dd') }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        304,
        2608
      ],
      "id": "33f03fd5-516b-4042-8af8-ab1ec09cbcda",
      "name": "Edit Fields1"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Telegram Trigger').item.json.message.from.id }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        432,
        976
      ],
      "id": "f5adaeb0-7c50-4625-ba5f-28d9218f54e2",
      "name": "Simple Memory"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        512,
        2432
      ],
      "id": "fc9d56e0-5412-4112-a70d-4ae3ee94987f",
      "name": "Simple Memory1"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Telegram Trigger').item.json.message.from.id }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        496,
        2752
      ],
      "id": "410a9a40-574b-4f53-8a33-c282b165bc4d",
      "name": "Simple Memory2"
    },
    {
      "parameters": {
        "model": "openai/gpt-oss-120b:free",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        336,
        976
      ],
      "id": "228be6d9-615d-42c6-bc6c-af8795166a71",
      "name": "OpenRouter Chat Model"
    },
    {
      "parameters": {
        "model": "openai/gpt-oss-120b:free",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        416,
        2432
      ],
      "id": "64ef79bd-00c3-4eb8-8e2a-47ae7318aa5e",
      "name": "OpenRouter Chat Model1"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "\u274c *Tidak dapat memahami input belanja*\n\nContoh format yang benar:\n\u2022 \"beli Telur 1kg Rp 20rb di Toko Mbak Ita\"\n\u2022 \"beli susu ultra 2 botol sama roti tawar 15 ribu\"\n\u2022 \"belanja di alfamart beli mie indomie 5 bungkus 12000\"\n\u2022 \"jajan bakso 2 porsi 20 ribu\"\n\n\ud83d\udca1 *Tips:*\n- Sebutkan barang yang dibeli\n- Sertakan harga (opsional)\n- Sebutkan nama toko (opsional)\n\nCoba lagi dengan format yang lebih jelas atau klik /help untuk bantuan! \ud83d\udcdd",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        848,
        2624
      ],
      "id": "00907296-b105-42b5-830a-218301a712ae",
      "name": "Telegram8"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.message.text }}",
        "options": {
          "systemMessage": "=# Parser Text Belanja\n\n## TUGAS\nAnalisa text belanja dan ubah menjadi format PERSIS seperti ini:\n\n## FORMAT OUTPUT\nTOKO: [nama toko dari text, jika tidak ada tulis 'Jati Pilar']\nTOTAL: [total harga dalam angka saja tanpa Rp/ribu/rb]\nITEMS: [daftar barang yang dibeli dengan kuantitas]\nKATEGORI: [pilih dari: Makanan & Minuman, Tagihan, Kosmetik & Perawatan, Transportasi, Kesehatan, Pakaian, Elektronik, Rumah Tangga, Pendidikan, Hiburan, Cicilan,Investasi, Lainnya]\nJENIS: [Pemasukan/Pengeluaran]\nMETODE: [Cash/QRIS/Transfer]\nBANK: [nama bank jika disebutkan: BCA, BRI, BNI, Mandiri, dll. Jika tidak ada tulis 'BRI']\nTANGGAL: {{ $('Edit Fields').item.json.tanggal_hari_ini }}\nTANGGAL_STRUK: [tanggal transaksi sebenarnya]\n\n---\n\n\n---\n\n## LOGIKA NAMA TOKO\n\n- Jika ada nama toko/lokasi disebutkan: gunakan nama toko tersebut\n- Jika TIDAK ada nama toko: gunakan \"Jati Pilar\" sebagai default\n- Jika transaksi tarik tunai/ATM:\n  - Ada nama lokasi \u2192 gunakan nama lokasi (contoh: Mandiraja)\n  - Tidak ada lokasi, ada nama bank \u2192 \"ATM [nama bank]\" (contoh: ATM BCA)\n  - Tidak ada lokasi, tidak ada bank \u2192 \"ATM\"\n\n### CONTOH LOGIKA TOKO\n- Input: \"beli telur 20rb di Toko Mbak Ita\" \u2192 TOKO: Toko Mbak Ita\n- Input: \"beli telur 20rb\" \u2192 TOKO: Jati Pilar\n- Input: \"ambil uang di atm bca mandiraja 500k\" \u2192 TOKO: Mandiraja\n- Input: \"tarik tunai bca 500k\" \u2192 TOKO: ATM BCA\n- Input: \"tarik tunai 500k\" \u2192 TOKO: ATM\n\n---\n\n## LOGIKA BANK\nDeteksi nama bank dari input user:\n- Kata kunci bank: BCA, BRI, BNI, Mandiri, CIMB, Danamon, Permata, BTN, Jenius, SeaBank, GoPay, OVO, Dana\n- Jika QRIS/Transfer dan ada nama bank \u2192 pakai nama bank itu\n- Jika Cash \u2192 tulis 'Cash'\n- Jika QRIS/Transfer dan tidak ada nama bank \u2192 tulis 'BCA' (default)\n- Jika Card/Cardless \u2192 pakai nama bank yang disebutkan, jika tidak ada tulis 'CARD'\n- Jika ada nama bank (BCA, BRI, BNI, Mandiri, dll) maka tulis 'Cash'\n  nama bank = BANK, nama lokasi = TOKO\n- Contoh: \"di bca jati pilar\" \u2192 BANK: BCA, TOKO: Jati Pilar\n- Contoh: \"di atm bri alun-alun\" \u2192 BANK: BRI, TOKO: Alun-alun\n\n\n\n### CONTOH LOGIKA BANK\n- Input: \"bayar qris bca 50rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n- Input: \"beli nasi goreng 20rb cash\" \u2192 METODE: Cash, BANK: Cash\n- Input: \"ambil uang di atm bca mandiraja cardless\" \u2192 METODE: Cardless, BANK: BCA\n- Input: \"tarik tunai bri card 500k\" \u2192 METODE: Card, BANK: BCA\n- Input: \"qris 15rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer 200rb\" \u2192 METODE: Transfer, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan)\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n- Card: kartu debit, kartu kredit, gesek kartu, card\n- Cardless: cardless, tanpa kartu, tarik tunai cardless\n\n---\n\n## LOGIKA TANGGAL\n- TANGGAL: Selalu {{ $('Edit Fields').item.json.tanggal_hari_ini }} (tanggal sistem saat input data)\n- TANGGAL_STRUK:\n  - Jika ada kata \"kemarin/kmrn/kemarenn\" \u2192 {{ $('Edit Fields').item.json.tanggal_kemarin }}\n  - Jika TIDAK ada kata \"kemarin\" \u2192 {{ $('Edit Fields').item.json.tanggal_hari_ini }}\n\n---\n\n## CONTOH-CONTOH\n\n### Contoh 1\nInput: \"beli Telur 1kg Rp 20rb di Toko Mbak Ita\"\nOutput:\nTOKO: Toko Mbak Ita\nTOTAL: 20000\nITEMS: Telur 1kg\nKATEGORI: Makanan & Minuman\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 2\nInput: \"bayar listrik 200rb PLN pakai qris bca\"\nOutput:\nTOKO: PLN\nTOTAL: 200000\nITEMS: Bayar Listrik\nKATEGORI: Tagihan\nJENIS: Pengeluaran\nMETODE: QRIS\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 3\nInput: \"nabung di bank mandiri 2jt transfer bca\"\nOutput:\nTOKO: Mandiri\nTOTAL: 2000000\nITEMS: Nabung\nKATEGORI: Investasi\nJENIS: Pengeluaran\nMETODE: Transfer\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nContoh response Cash tanpa bank:\nTOKO: Warung Makan Padang\nTOTAL: 25000\nITEMS: Nasi Padang\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\nContoh response Cash tanpa bank:\nTOKO: Pasar Tradisional\nTOTAL: 50000\nITEMS: Sayur, Tempe, Tahu\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\n---\n### Contoh Tarik Tunai\nInput: \"ambil uang tunai di atm bca mandiraja 500k cardless\"\nOutput:\nTOKO: Mandiraja\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cardless\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai bca 500k card\"\nOutput:\nTOKO: ATM BCA\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Card\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai 300k\"\nOutput:\nTOKO: ATM\nTOTAL: 300000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n\nInput: \"tarik tunai 500k di bca jati pilar\"\nOutput:\nTOKO: Jati Pilar\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n---\n\n## KATEGORI GUIDELINES\n- Makanan & Minuman: makanan, minuman, snack, bahan masak\n- Tagihan: listrik, air, internet, telepon, pulsa\n- Cicilan: cicilan motor, cicilan rumah, kredit\n- Kosmetik & Perawatan: skincare, makeup, perawatan tubuh\n- Transportasi: bensin, ojek, grab, parkir, servis kendaraan\n- Kesehatan: obat, dokter, rumah sakit, vitamin\n- Pakaian: baju, sepatu, aksesoris fashion\n- Elektronik: gadget, peralatan elektronik\n- Rumah Tangga: peralatan rumah, furniture, kebersihan\n- Pendidikan: buku, kursus, sekolah\n- Hiburan: nonton, game, rekreasi\n- Lainnya: yang tidak masuk kategori lain, gaji, bonus, tarik tunai ATM\n\n---\n\n## JENIS TRANSAKSI\n- Pengeluaran: pembelian, pembayaran, biaya, ambil uang ATM, tarik tunai\n- Pemasukan: gaji, bonus, penjualan, hadiah, cashback\n\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan), tarik tunai selalu Cash\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n\n---\n\n## ERROR HANDLING\nJika tidak bisa dipahami, berikan:\nERROR: Text belanja tidak dapat dipahami\n\n---\n\n## PENTING\n- Hanya berikan response dalam format di atas!\n- TOKO: Gunakan \"Jati Pilar\" jika tidak ada nama toko\n- BANK: Default \"BRI\" jika tidak disebutkan\n- TANGGAL: Selalu tanggal sistem\n- TANGGAL_STRUK: Tanggal transaksi sebenarnya\n- Jika ada kata \"kemarin/kmrn/kemarenn\", TANGGAL_STRUK = kemarin"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        416,
        2256
      ],
      "id": "c3a40f5f-d37e-48a1-a20b-716f0495ef3c",
      "name": "AI INPUT DATA TEXT"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.content.parts[0].text }}",
        "options": {
          "systemMessage": "=# Parser Text Belanja\n\n## TUGAS\nAnalisa text belanja dan ubah menjadi format PERSIS seperti ini:\n\n## FORMAT OUTPUT\nTOKO: [nama toko dari text, jika tidak ada tulis 'Jati Pilar']\nTOTAL: [total harga dalam angka saja tanpa Rp/ribu/rb]\nITEMS: [daftar barang yang dibeli dengan kuantitas]\nKATEGORI: [pilih dari: Makanan & Minuman, Tagihan, Kosmetik & Perawatan, Transportasi, Kesehatan, Pakaian, Elektronik, Rumah Tangga, Pendidikan, Hiburan, Cicilan,Investasi, Lainnya]\nJENIS: [Pemasukan/Pengeluaran]\nMETODE: [Cash/QRIS/Transfer]\nBANK: [nama bank jika disebutkan: BCA, BRI, BNI, Mandiri, dll. Jika tidak ada tulis 'BRI']\nTANGGAL: {{ $('Edit Fields1').item.json.tanggal_hari_ini }}\nTANGGAL_STRUK: [tanggal transaksi sebenarnya]\n\n---\n\n\n---\n\n## LOGIKA NAMA TOKO\n\n- Jika ada nama toko/lokasi disebutkan: gunakan nama toko tersebut\n- Jika TIDAK ada nama toko: gunakan \"Jati Pilar\" sebagai default\n- Jika transaksi tarik tunai/ATM:\n  - Ada nama lokasi \u2192 gunakan nama lokasi (contoh: Mandiraja)\n  - Tidak ada lokasi, ada nama bank \u2192 \"ATM [nama bank]\" (contoh: ATM BCA)\n  - Tidak ada lokasi, tidak ada bank \u2192 \"ATM\"\n\n### CONTOH LOGIKA TOKO\n- Input: \"beli telur 20rb di Toko Mbak Ita\" \u2192 TOKO: Toko Mbak Ita\n- Input: \"beli telur 20rb\" \u2192 TOKO: Jati Pilar\n- Input: \"ambil uang di atm bca mandiraja 500k\" \u2192 TOKO: Mandiraja\n- Input: \"tarik tunai bca 500k\" \u2192 TOKO: ATM BCA\n- Input: \"tarik tunai 500k\" \u2192 TOKO: ATM\n\n---\n\n## LOGIKA BANK\nDeteksi nama bank dari input user:\n- Kata kunci bank: BCA, BRI, BNI, Mandiri, CIMB, Danamon, Permata, BTN, Jenius, SeaBank, GoPay, OVO, Dana\n- Jika QRIS/Transfer dan ada nama bank \u2192 pakai nama bank itu\n- Jika Cash \u2192 tulis 'Cash'\n- Jika QRIS/Transfer dan tidak ada nama bank \u2192 tulis 'BCA' (default)\n- Jika Card/Cardless \u2192 pakai nama bank yang disebutkan, jika tidak ada tulis 'CARD'\n- Jika ada nama bank (BCA, BRI, BNI, Mandiri, dll) maka tulis 'Cash'\n  nama bank = BANK, nama lokasi = TOKO\n- Contoh: \"di bca jati pilar\" \u2192 BANK: BCA, TOKO: Jati Pilar\n- Contoh: \"di atm bri alun-alun\" \u2192 BANK: BRI, TOKO: Alun-alun\n\n\n\n### CONTOH LOGIKA BANK\n- Input: \"bayar qris bca 50rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n- Input: \"beli nasi goreng 20rb cash\" \u2192 METODE: Cash, BANK: Cash\n- Input: \"ambil uang di atm bca mandiraja cardless\" \u2192 METODE: Cardless, BANK: BCA\n- Input: \"tarik tunai bri card 500k\" \u2192 METODE: Card, BANK: BCA\n- Input: \"qris 15rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer 200rb\" \u2192 METODE: Transfer, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan)\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n- Card: kartu debit, kartu kredit, gesek kartu, card\n- Cardless: cardless, tanpa kartu, tarik tunai cardless\n\n---\n\n## LOGIKA TANGGAL\n- TANGGAL: Selalu {{ $('Edit Fields1').item.json.tanggal_hari_ini }} (tanggal sistem saat input data)\n- TANGGAL_STRUK:\n  - Jika ada kata \"kemarin/kmrn/kemarenn\" \u2192 {{ $('Edit Fields1').item.json.tanggal_kemarin }}\n  - Jika TIDAK ada kata \"kemarin\" \u2192 {{ $('Edit Fields1').item.json.tanggal_hari_ini }}\n\n---\n\n## CONTOH-CONTOH\n\n### Contoh 1\nInput: \"beli Telur 1kg Rp 20rb di Toko Mbak Ita\"\nOutput:\nTOKO: Toko Mbak Ita\nTOTAL: 20000\nITEMS: Telur 1kg\nKATEGORI: Makanan & Minuman\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 2\nInput: \"bayar listrik 200rb PLN pakai qris bca\"\nOutput:\nTOKO: PLN\nTOTAL: 200000\nITEMS: Bayar Listrik\nKATEGORI: Tagihan\nJENIS: Pengeluaran\nMETODE: QRIS\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 3\nInput: \"nabung di bank mandiri 2jt transfer bca\"\nOutput:\nTOKO: Mandiri\nTOTAL: 2000000\nITEMS: Nabung\nKATEGORI: Investasi\nJENIS: Pengeluaran\nMETODE: Transfer\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nContoh response Cash tanpa bank:\nTOKO: Warung Makan Padang\nTOTAL: 25000\nITEMS: Nasi Padang\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\nContoh response Cash tanpa bank:\nTOKO: Pasar Tradisional\nTOTAL: 50000\nITEMS: Sayur, Tempe, Tahu\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\n---\n### Contoh Tarik Tunai\nInput: \"ambil uang tunai di atm bca mandiraja 500k cardless\"\nOutput:\nTOKO: Mandiraja\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cardless\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai bca 500k card\"\nOutput:\nTOKO: ATM BCA\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Card\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai 300k\"\nOutput:\nTOKO: ATM\nTOTAL: 300000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n\nInput: \"tarik tunai 500k di bca jati pilar\"\nOutput:\nTOKO: Jati Pilar\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n---\n\n## KATEGORI GUIDELINES\n- Makanan & Minuman: makanan, minuman, snack, bahan masak\n- Tagihan: listrik, air, internet, telepon, pulsa\n- Cicilan: cicilan motor, cicilan rumah, kredit\n- Kosmetik & Perawatan: skincare, makeup, perawatan tubuh\n- Transportasi: bensin, ojek, grab, parkir, servis kendaraan\n- Kesehatan: obat, dokter, rumah sakit, vitamin\n- Pakaian: baju, sepatu, aksesoris fashion\n- Elektronik: gadget, peralatan elektronik\n- Rumah Tangga: peralatan rumah, furniture, kebersihan\n- Pendidikan: buku, kursus, sekolah\n- Hiburan: nonton, game, rekreasi\n- Lainnya: yang tidak masuk kategori lain, gaji, bonus, tarik tunai ATM\n\n---\n\n## JENIS TRANSAKSI\n- Pengeluaran: pembelian, pembayaran, biaya, ambil uang ATM, tarik tunai\n- Pemasukan: gaji, bonus, penjualan, hadiah, cashback\n\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan), tarik tunai selalu Cash\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n\n---\n\n## ERROR HANDLING\nJika tidak bisa dipahami, berikan:\nERROR: Text belanja tidak dapat dipahami\n\n---\n\n## PENTING\n- Hanya berikan response dalam format di atas!\n- TOKO: Gunakan \"Jati Pilar\" jika tidak ada nama toko\n- BANK: Default \"BRI\" jika tidak disebutkan\n- TANGGAL: Selalu tanggal sistem\n- TANGGAL_STRUK: Tanggal transaksi sebenarnya\n- Jika ada kata \"kemarin/kmrn/kemarenn\", TANGGAL_STRUK = kemarin"
        }
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3.1,
      "position": [
        416,
        2608
      ],
      "id": "21a65f36-9b8b-4971-888e-620bdee45200",
      "name": "AI INPUT VOICE NOTE"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "check-data-exists",
              "leftValue": "={{ $('Merge Data').all().length }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        928,
        1728
      ],
      "id": "bcc3223f-0629-4b94-9606-66a26ea203a8",
      "name": "Cek Ada Data"
    },
    {
      "parameters": {
        "chatId": "={{ $('Parse Command').item.json.chatId }}",
        "text": "=\u26a0\ufe0f *Laporan tidak ditemukan*\n\nTidak ada data transaksi untuk bulan *{{ $('Parse Command').item.json.targetBulan }} {{ $('Parse Command').item.json.targetTahun }}*.\n\nPastikan sudah ada transaksi yang dicatat di bulan tersebut ya! \ud83d\udcdd\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1136,
        1744
      ],
      "id": "913b1599-495e-4a6e-9614-aad9143fe842",
      "name": "Kirim Error No Data"
    },
    {
      "parameters": {
        "chatId": "={{ $('Agregasi dan Render HTML').item.json.chatId }}",
        "text": "=\u274c *Aduh, Pijan gagal bikin PDF-nya!*\n\nKemungkinan penyebab:\n\u2022 API Key html2pdf belum diisi / expired\n\u2022 Koneksi ke server PDF gagal\n\nCoba lagi ya, atau hubungi Baba! \ud83d\ude4f\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        768,
        2032
      ],
      "id": "79af54a3-275c-448f-866a-ad1d7b008e1d",
      "name": "Kirim Error PDF"
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Processing').item.json.result.chat.id }}",
        "text": " udah sukses ya !! silahkan cek email ibu/Baba",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1072,
        1888
      ],
      "id": "0ed045a1-b4cc-43ba-8551-333c61e5d75c",
      "name": "Kirim PDF ke Telegram"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "check-pdf-success",
              "leftValue": "={{ $binary.pdfData }}",
              "rightValue": "",
              "operator": {
                "type": "object",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        576,
        1952
      ],
      "id": "32ea34cb-f13e-4b1a-9586-a66324c30587",
      "name": "Cek PDF Berhasil"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.html2pdf.app/v1/generate",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"html\": {{ JSON.stringify($json.htmlBody) }},\n  \"apiKey\": \"5oC37yePl86RvJQuT7BEUljfc0pE5qq2nGrzj421NnK3uQLmvAdv2cL0BNDmRaEq\",\n  \"landscape\": false,\n  \"scale\": 1,\n  \"margin\": {\n    \"top\": \"10mm\",\n    \"right\": \"10mm\",\n    \"bottom\": \"10mm\",\n    \"left\": \"10mm\"\n  }\n}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "pdfData"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        352,
        1952
      ],
      "id": "7984c2c0-d821-483b-901a-0d8bf46b0dc9",
      "name": "HTML to PDF"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\n\nlet mode = 'bulan', bulan = '', tahun = '', firstName = '', chatId = '';\nlet periodeStart = '', periodeEnd = '', periodeLabel = '';\n\ntry {\n  const ef = $('Edit Fields (Webhook)1').first().json;\n  mode         = ef.mode         || 'bulan';\n  bulan        = ef.bulan        || ef.targetBulan  || '';\n  tahun        = String(ef.tahun || ef.targetTahun  || '');\n  firstName    = ef.firstName    || ef.firstname    || '';\n  chatId       = String(ef.chatId || '');\n  periodeStart = ef.periodeStart || '';\n  periodeEnd   = ef.periodeEnd   || '';\n  periodeLabel = ef.periodeLabel || '';\n} catch(e1) {\n  try {\n    const ef = $('Edit Fields2').first().json;\n    mode         = ef.mode         || 'bulan';\n    bulan        = ef.bulan        || ef.targetBulan || '';\n    tahun        = String(ef.tahun || ef.targetTahun || '');\n    firstName    = ef.firstName    || '';\n    chatId       = String(ef.chatId || '');\n    periodeStart = ef.periodeStart || '';\n    periodeEnd   = ef.periodeEnd   || '';\n    periodeLabel = ef.periodeLabel || '';\n  } catch(e2) {}\n}\n\n// \u2500\u2500 Filter helper \u2500\u2500\nconst BULAN_MAP = {\n  'Januari':1,'Februari':2,'Maret':3,'April':4,'Mei':5,'Juni':6,\n  'Juli':7,'Agustus':8,'September':9,'Oktober':10,'November':11,'Desember':12\n};\n\nconst BULAN_ID = {\n  'januari':0,'februari':1,'maret':2,'april':3,'mei':4,'juni':5,\n  'juli':6,'agustus':7,'september':8,'oktober':9,'november':10,'desember':11\n};\n\nconst parseTgl = (tglStr) => {\n  if (!tglStr || tglStr === '-') return null;\n  const s = String(tglStr).trim();\n\n  // Format dd/MM/yyyy \u2192 lokal\n  if (/^\\d{1,2}\\/\\d{1,2}\\/\\d{4}$/.test(s)) {\n    const [dd, mm, yyyy] = s.split('/');\n    return new Date(parseInt(yyyy), parseInt(mm)-1, parseInt(dd));\n  }\n  // Format yyyy-MM-dd \u2192 lokal\n  if (/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) {\n    const [yyyy, mm, dd] = s.split('-');\n    return new Date(parseInt(yyyy), parseInt(mm)-1, parseInt(dd));\n  }\n  // Format \"6 Mei 2026\" atau \"06 Mei 2026\" (Google Sheets display)\n  const mMatch = s.match(/^(\\d{1,2})\\s+([A-Za-z]+)\\s+(\\d{4})$/);\n  if (mMatch) {\n    const dd   = parseInt(mMatch[1]);\n    const mm   = BULAN_ID[mMatch[2].toLowerCase()];\n    const yyyy = parseInt(mMatch[3]);\n    if (mm !== undefined) return new Date(yyyy, mm, dd);\n  }\n  // Format \"6 May 2026\" (English fallback)\n  const d = new Date(s);\n  return isNaN(d) ? null : d;\n};\n\nlet filterFn;\nif (mode === 'periode' && periodeStart && periodeEnd) {\n  // Parse lokal (bukan UTC) untuk hindari timezone shift\n  const [sy,sm,sd] = periodeStart.split('-');\n  const [ey,em,ed] = periodeEnd.split('-');\n  const startD = new Date(parseInt(sy), parseInt(sm)-1, parseInt(sd), 0,0,0,0);\n  const endD   = new Date(parseInt(ey), parseInt(em)-1, parseInt(ed), 23,59,59,999);\n  filterFn = (tglStr) => {\n    const d = parseTgl(tglStr);\n    if (!d) return false;\n    return d >= startD && d <= endD;\n  };\n} else {\n  const targetM = BULAN_MAP[bulan];\n  const targetY = parseInt(tahun);\n  filterFn = (tglStr) => {\n    const d = parseTgl(tglStr);\n    if (!d) return false;\n    return (d.getMonth() + 1) === targetM && d.getFullYear() === targetY;\n  };\n}\n\n// Label periode untuk header laporan\nconst reportPeriodeLabel = (mode === 'periode' && periodeLabel)\n  ? periodeLabel\n  : (bulan + ' ' + tahun);\n\n// \u2500\u2500 Pisahkan expenses vs income + filter \u2500\u2500\nconst expenses = [];\nconst incomes  = [];\n\nfor (const item of items) {\n  const d = item.json;\n  if (d.Jumlah !== undefined || d.Sumber !== undefined) {\n    // Income punya field Tanggal \u2192 filter sama seperti expenses\n    if (d.Tanggal) {\n      if (filterFn(d.Tanggal)) incomes.push(d);\n    } else if (d.Bulan) {\n      // Fallback: tidak ada Tanggal, pakai Bulan\n      const targetM = mode === 'periode'\n        ? parseInt(periodeEnd.split('-')[1])  // bulan dari periodeEnd\n        : BULAN_MAP[bulan];\n      if (BULAN_MAP[d.Bulan] === targetM) incomes.push(d);\n    } else {\n      // Tidak ada Tanggal maupun Bulan \u2192 loloskan\n      incomes.push(d);\n    }\n  } else {\n    if (d.Tanggal) {\n      if (filterFn(d.Tanggal)) expenses.push(d);\n    } else if (d.Nilai) {\n      expenses.push(d);\n    }\n  }\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\nconst toNum = (v) => parseInt(String(v || '0').replace(/\\D/g, '')) || 0;\nconst fmt   = (n) => 'Rp ' + Number(n).toLocaleString('id-ID');\n\n// \u2500\u2500 Agregasi expenses \u2500\u2500\nlet totalKeluar = 0;\nconst byKategori = {}, byBank = {}, txList = [];\n\nfor (const d of expenses) {\n  const nilai = toNum(d.Nilai);\n  totalKeluar += nilai;\n  const kat  = d.Kategori || 'Lainnya';\n  const bank = d.Bank     || '-';\n  byKategori[kat] = (byKategori[kat] || 0) + nilai;\n  byBank[bank]    = (byBank[bank]    || 0) + nilai;\n  txList.push({\n    tanggal: d.Tanggal || '-', toko: d.Toko || '-', uraian: d.Uraian || '-',\n    kategori: kat, nilai, bank, metode: d.Transaksi || '-', user: d.user || '-', jenis: 'Keluar'\n  });\n}\n\n// \u2500\u2500 Agregasi income \u2500\u2500\nlet totalMasuk = 0;\nfor (const d of incomes) {\n  const nilai = toNum(d.Jumlah);\n  totalMasuk += nilai;\n  const bank = d.Bank || '-';\n  byBank[bank] = (byBank[bank] || 0) + nilai;\n  txList.push({\n    tanggal: d.Tanggal || '-', toko: d.Sumber || '-', uraian: d.Items || '-',\n    kategori: d.Kategori || 'Pemasukan', nilai, bank, metode: d.Metode || '-', user: d.User || '-', jenis: 'Masuk'\n  });\n}\n\nconst saldo = totalMasuk - totalKeluar;\nconst totalTx = txList.length;\n\n// \u2500\u2500 HTML \u2500\u2500\nconst rowsKategori = Object.entries(byKategori)\n  .sort((a, b) => b[1] - a[1])\n  .map(([k, v]) => `\n    <tr>\n      <td>${k}</td>\n      <td style=\"text-align:right;font-weight:500\">${fmt(v)}</td>\n      <td style=\"text-align:right\">${totalKeluar > 0 ? ((v/totalKeluar)*100).toFixed(1) : '0.0'}%</td>\n    </tr>`).join('');\n\nconst rowsBank = Object.entries(byBank)\n  .sort((a, b) => b[1] - a[1])\n  .map(([k, v]) => `\n    <tr>\n      <td>${k}</td>\n      <td style=\"text-align:right;font-weight:500\">${fmt(v)}</td>\n    </tr>`).join('');\n\nconst rowsTx = txList\n  .sort((a, b) => {\n    const da = parseTgl(a.tanggal), db = parseTgl(b.tanggal);\n    return (db||0) - (da||0);\n  })\n  .map((t) => `\n    <tr>\n      <td style=\"white-space:nowrap\">${t.tanggal}</td>\n      <td>${t.toko}</td>\n      <td>${t.uraian}</td>\n      <td>${t.kategori}</td>\n      <td>${t.metode}</td>\n      <td>${t.bank}</td>\n      <td>${t.user}</td>\n      <td style=\"text-align:right;color:${t.jenis==='Masuk'?'#16a34a':'#dc2626'};font-weight:500\">${fmt(t.nilai)}</td>\n      <td><span style=\"background:${t.jenis==='Masuk'?'#dcfce7':'#fee2e2'};color:${t.jenis==='Masuk'?'#166534':'#991b1b'};padding:2px 8px;border-radius:12px;font-size:11px;font-weight:600\">${t.jenis}</span></td>\n    </tr>`).join('');\n\nconst reportTitle = mode === 'periode' ? 'Laporan Keuangan Per Periode Gaji' : 'Laporan Keuangan Bulanan';\n\nconst htmlBody = `<!DOCTYPE html>\n<html lang=\"id\">\n<head>\n<meta charset=\"UTF-8\">\n<style>\n  * { margin: 0; padding: 0; box-sizing: border-box; }\n  body { font-family: 'Arial', sans-serif; font-size: 11px; color: #1a1a2e; background: #fff; }\n  .header { padding: 32px 48px 24px; border-bottom: 3px solid #1a1a2e; display: flex; justify-content: space-between; align-items: flex-start; }\n  .company-name { font-size: 22px; font-weight: 700; color: #1a1a2e; letter-spacing: 1px; }\n  .company-sub { font-size: 10px; color: #6b7280; margin-top: 3px; letter-spacing: 2px; text-transform: uppercase; }\n  .report-title-box { text-align: right; }\n  .report-title { font-size: 16px; font-weight: 700; color: #1a1a2e; text-transform: uppercase; letter-spacing: 1px; }\n  .report-period { font-size: 10px; color: #6b7280; margin-top: 4px; }\n  .report-date { font-size: 9px; color: #9ca3af; margin-top: 2px; }\n  .meta-bar { background: #1a1a2e; color: #fff; padding: 10px 48px; display: flex; gap: 48px; }\n  .meta-item { font-size: 9px; text-transform: uppercase; letter-spacing: 1px; }\n  .meta-item span { display: block; font-size: 11px; font-weight: 700; margin-top: 2px; color: #e5e7eb; }\n  .section { padding: 24px 48px 0; }\n  .section-title { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 2px; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 6px; margin-bottom: 14px; }\n  .summary-grid { display: flex; gap: 12px; }\n  .summary-card { flex: 1; border: 1px solid #e5e7eb; border-radius: 4px; padding: 14px 16px; }\n  .summary-card.income  { border-left: 4px solid #16a34a; }\n  .summary-card.expense { border-left: 4px solid #dc2626; }\n  .summary-card.balance { border-left: 4px solid #1a1a2e; }\n  .card-label { font-size: 8px; text-transform: uppercase; letter-spacing: 1.5px; color: #9ca3af; margin-bottom: 6px; }\n  .card-value { font-size: 16px; font-weight: 700; color: #1a1a2e; }\n  .card-value.income  { color: #16a34a; }\n  .card-value.expense { color: #dc2626; }\n  .card-count { font-size: 9px; color: #9ca3af; margin-top: 4px; }\n  table { width: 100%; border-collapse: collapse; font-size: 10px; }\n  thead tr { background: #1a1a2e; }\n  thead th { padding: 8px 10px; text-align: left; color: #fff; font-size: 8px; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; }\n  .two-col { display: flex; gap: 24px; }\n  .two-col .col { flex: 1; }\n  .divider { height: 1px; background: #e5e7eb; margin: 24px 48px 0; }\n  .footer { padding: 16px 48px; margin-top: 24px; border-top: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; }\n  .footer-left { font-size: 9px; color: #9ca3af; }\n  .footer-right { font-size: 9px; color: #9ca3af; text-align: right; }\n  .footer-brand { font-size: 10px; font-weight: 700; color: #1a1a2e; }\n  .ttd-section { padding: 24px 48px 0; display: flex; justify-content: flex-end; }\n  .ttd-box { text-align: center; width: 180px; }\n  .ttd-line { border-bottom: 1px solid #1a1a2e; margin: 48px 0 6px; }\n  .ttd-name { font-size: 10px; font-weight: 700; color: #1a1a2e; }\n  .ttd-label { font-size: 9px; color: #374151; }\n  .badge-mode { display:inline-block;background:${mode==='periode'?'#dbeafe':'#f3f4f6'};color:${mode==='periode'?'#1d4ed8':'#374151'};padding:2px 8px;border-radius:4px;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:1px; }\n</style>\n</head>\n<body>\n  <div class=\"header\">\n    <div>\n      <div class=\"company-name\">ArviFund</div>\n      <div class=\"company-sub\">Manajemen Keuangan Keluarga</div>\n    </div>\n    <div class=\"report-title-box\">\n      <div class=\"report-title\">${reportTitle}</div>\n      <div class=\"report-period\">Periode: ${reportPeriodeLabel} &nbsp;<span class=\"badge-mode\">${mode === 'periode' ? 'Gaji' : 'Kalender'}</span></div>\n      <div class=\"report-date\">Diterbitkan: ${new Date().toLocaleDateString('id-ID', { day:'2-digit', month:'long', year:'numeric', timeZone:'Asia/Jakarta' })}</div>\n    </div>\n  </div>\n\n  <div class=\"meta-bar\">\n    <div class=\"meta-item\">Total Transaksi <span>${totalTx} Transaksi</span></div>\n    <div class=\"meta-item\">Pemasukan <span>${fmt(totalMasuk)}</span></div>\n    <div class=\"meta-item\">Pengeluaran <span>${fmt(totalKeluar)}</span></div>\n    <div class=\"meta-item\">Saldo Akhir <span>${fmt(saldo)}</span></div>\n  </div>\n\n  <div class=\"section\" style=\"margin-top:24px\">\n    <div class=\"section-title\">Ringkasan Eksekutif</div>\n    <div class=\"summary-grid\">\n      <div class=\"summary-card income\">\n        <div class=\"card-label\">Total Pemasukan</div>\n        <div class=\"card-value income\">${fmt(totalMasuk)}</div>\n        <div class=\"card-count\">${incomes.length} transaksi pemasukan</div>\n      </div>\n      <div class=\"summary-card expense\">\n        <div class=\"card-label\">Total Pengeluaran</div>\n        <div class=\"card-value expense\">${fmt(totalKeluar)}</div>\n        <div class=\"card-count\">${expenses.length} transaksi pengeluaran</div>\n      </div>\n      <div class=\"summary-card balance\">\n        <div class=\"card-label\">Saldo Akhir Periode</div>\n        <div class=\"card-value\">${fmt(saldo)}</div>\n        <div class=\"card-count\">${saldo >= 0 ? 'Surplus' : 'Defisit'} periode ini</div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"divider\"></div>\n\n  <div class=\"section\" style=\"margin-top:24px\">\n    <div class=\"two-col\">\n      <div class=\"col\">\n        <div class=\"section-title\">Pengeluaran per Kategori</div>\n        <table>\n          <thead><tr><th>Kategori</th><th>Jumlah</th><th>%</th></tr></thead>\n          <tbody>${rowsKategori || '<tr><td colspan=\"3\" style=\"color:#9ca3af;padding:12px 10px\">Tidak ada data</td></tr>'}</tbody>\n        </table>\n      </div>\n      <div class=\"col\">\n        <div class=\"section-title\">Rekap Bank &amp; Metode Bayar</div>\n        <table>\n          <thead><tr><th>Bank / Metode</th><th>Total</th></tr></thead>\n          <tbody>${rowsBank || '<tr><td colspan=\"2\" style=\"color:#9ca3af;padding:12px 10px\">Tidak ada data</td></tr>'}</tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"divider\"></div>\n\n  <div class=\"section\" style=\"margin-top:24px\">\n    <div class=\"section-title\">Detail Transaksi \u2014 Semua</div>\n    <table>\n      <thead>\n        <tr>\n          <th>Tanggal</th><th>Toko / Sumber</th><th>Uraian</th><th>Kategori</th>\n          <th>Metode</th><th>Bank</th><th>User</th><th>Nilai</th><th>Jenis</th>\n        </tr>\n      </thead>\n      <tbody>${rowsTx || '<tr><td colspan=\"9\" style=\"color:#9ca3af;padding:16px 10px;text-align:center\">Tidak ada transaksi pada periode ini</td></tr>'}</tbody>\n    </table>\n  </div>\n\n  <div class=\"ttd-section\">\n    <div class=\"ttd-box\">\n      <div class=\"ttd-line\"></div>\n      <div class=\"ttd-name\">Arvidzan Rezqiano Darmawan</div>\n      <div class=\"ttd-label\">Dibuat oleh ArviFund Bot</div>\n    </div>\n  </div>\n\n  <div class=\"footer\">\n    <div class=\"footer-left\">\n      <div class=\"footer-brand\">ArviFund</div>\n      Laporan ini dibuat secara otomatis oleh sistem ArviFund\n    </div>\n    <div class=\"footer-right\">Halaman 1 dari 1<br>Dokumen ini bersifat rahasia</div>\n  </div>\n</body>\n</html>`;\n\nreturn [{\n  json: {\n    htmlBody, bulan, tahun, chatId, firstName,\n    mode, periodeStart, periodeEnd, periodeLabel, reportPeriodeLabel,\n    totalMasuk, totalKeluar, saldo, totalTx,\n    fmtMasuk: fmt(totalMasuk), fmtKeluar: fmt(totalKeluar), fmtSaldo: fmt(saldo)\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        128,
        1952
      ],
      "id": "032dcc3c-a752-4a30-9ae4-cd889ea04180",
      "name": "Agregasi dan Render HTML"
    },
    {
      "parameters": {
        "numberInputs": 3
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        1152,
        1504
      ],
      "id": "bf1f00bf-7d5a-4a5d-9773-ad705011c408",
      "name": "Merge Data"
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "income",
        "returnAll": true
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        944,
        1616
      ],
      "id": "279baff7-bc06-4c12-acfa-9c24a55ec876",
      "name": "Read Income"
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "expenses",
        "returnAll": true
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        944,
        1424
      ],
      "id": "9350b23f-cc44-4090-aa79-505ee945d49c",
      "name": "Read Expenses"
    },
    {
      "parameters": {
        "chatId": "={{ $json.chatId }}",
        "text": "=\u23f3 Oke *{{ $json.Baba }}*, Pijan lagi siapkan laporan bulan *{{ $json.targetBulan }} {{ $json.targetTahun }}* ya...\n\nTunggu sebentar! \ud83d\udcca",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        752,
        1504
      ],
      "id": "1c2e235f-48f2-452f-bc56-4eb1c94eff53",
      "name": "Telegram Processing"
    },
    {
      "parameters": {
        "jsCode": "// ============================================================\n// NODE: Parse Command (Telegram)\n// Deteksi nama bulan dari command user\n// Contoh: /laporanbulanan       \u2192 bulan lalu\n//         /laporanbulanapril    \u2192 April\n//         /laporanperiode       \u2192 periode gaji terakhir (pakai tanggal gajian default 25)\n// ============================================================\n\nconst text = $input.first().json.message.text.toLowerCase().trim();\n\nconst mapBulan = {\n  'januari':   { index: 0,  nama: 'Januari' },\n  'februari':  { index: 1,  nama: 'Februari' },\n  'maret':     { index: 2,  nama: 'Maret' },\n  'april':     { index: 3,  nama: 'April' },\n  'mei':       { index: 4,  nama: 'Mei' },\n  'juni':      { index: 5,  nama: 'Juni' },\n  'juli':      { index: 6,  nama: 'Juli' },\n  'agustus':   { index: 7,  nama: 'Agustus' },\n  'september': { index: 8,  nama: 'September' },\n  'oktober':   { index: 9,  nama: 'Oktober' },\n  'november':  { index: 10, nama: 'November' },\n  'desember':  { index: 11, nama: 'Desember' }\n};\n\nconst BULAN_LIST = ['Januari','Februari','Maret','April','Mei','Juni',\n                    'Juli','Agustus','September','Oktober','November','Desember'];\n\nconst now = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\n\nlet targetBulan = null, targetBulanIndex = null, targetTahun = now.getFullYear();\nlet mode = 'bulan', periodeStart = '', periodeEnd = '', periodeLabel = '';\n\n// Cek apakah command minta laporan per periode\nif (text.includes('periode') || text.includes('period')) {\n  mode = 'periode';\n  // Hitung periode gajian terakhir (default tgl 25)\n  const payDate = 25;\n  const today   = now;\n  let pStart, pEnd;\n  if (today.getDate() >= payDate) {\n    pStart = new Date(today.getFullYear(), today.getMonth(), payDate);\n    pEnd   = new Date(today.getFullYear(), today.getMonth() + 1, payDate - 1);\n  } else {\n    pStart = new Date(today.getFullYear(), today.getMonth() - 1, payDate);\n    pEnd   = new Date(today.getFullYear(), today.getMonth(), payDate - 1);\n  }\n  const fmt = d => d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');\n  periodeStart  = fmt(pStart);\n  periodeEnd    = fmt(pEnd);\n  targetBulanIndex = pEnd.getMonth();\n  targetTahun      = pEnd.getFullYear();\n  targetBulan      = BULAN_LIST[targetBulanIndex];\n  periodeLabel = periodeStart + ' s/d ' + periodeEnd;\n} else {\n  // Cari nama bulan di dalam teks\n  for (const [key, val] of Object.entries(mapBulan)) {\n    if (text.includes(key)) {\n      targetBulan = val.nama;\n      targetBulanIndex = val.index;\n      break;\n    }\n  }\n  // Default: bulan lalu\n  if (!targetBulan) {\n    const bulanLalu  = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n    targetBulanIndex = bulanLalu.getMonth();\n    targetTahun      = bulanLalu.getFullYear();\n    targetBulan      = BULAN_LIST[targetBulanIndex];\n  }\n}\n\nreturn [{\n  json: {\n    mode,\n    targetBulan,\n    targetBulanIndex,\n    targetTahun,\n    periodeStart,\n    periodeEnd,\n    periodeLabel,\n    chatId   : $input.first().json.message.chat.id,\n    userId   : $input.first().json.message.from.id,\n    firstName: $input.first().json.message.from.first_name\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        16,
        1488
      ],
      "id": "55607886-9acb-40b5-a74e-0b752439c846",
      "name": "Parse Command"
    },
    {
      "parameters": {
        "sendTo": "={{ $('Edit Fields (Webhook)1').item.json.firstName === 'Aldin' ? 'aldindarmawan72@gmail.com' : $('Edit Fields (Webhook)1').item.json.firstName === 'Solikhatun' ? 'solikhatun0800@gmail.com' : '' }}",
        "subject": "=Laporan bulan {{ $('Cek PDF Berhasil').item.json.bulan }} {{ $('Cek PDF Berhasil').item.json.tahun }}",
        "message": "berikut hasil laporan bulanan",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "=pdfData"
              }
            ]
          }
        }
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        928,
        1888
      ],
      "id": "fae55fa0-cc75-43c6-b4ac-a0cfa4b69829",
      "name": "Send a message"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "8a46796a-9a19-4d6f-9e73-3e713f929969",
              "name": "Baba",
              "value": "={{ $json.firstName === 'Aldin' ? 'Baba' : 'Ibu' }}",
              "type": "string"
            },
            {
              "id": "tg-ps-001",
              "name": "periodeStart",
              "value": "={{ $json.periodeStart }}",
              "type": "string"
            },
            {
              "id": "tg-ps-002",
              "name": "periodeEnd",
              "value": "={{ $json.periodeEnd }}",
              "type": "string"
            },
            {
              "id": "tg-ps-003",
              "name": "periodeLabel",
              "value": "={{ $json.periodeLabel }}",
              "type": "string"
            },
            {
              "id": "tg-ps-004",
              "name": "mode",
              "value": "={{ $json.mode }}",
              "type": "string"
            },
            {
              "id": "tg-ps-005",
              "name": "tahun",
              "value": "={{ $json.targetTahun }}",
              "type": "string"
            },
            {
              "id": "tg-ps-006",
              "name": "bulan",
              "value": "={{ $json.targetBulan }}",
              "type": "string"
            },
            {
              "id": "tg-ps-007",
              "name": "chatId",
              "value": "={{ $json.chatId }}",
              "type": "string"
            },
            {
              "id": "tg-ps-008",
              "name": "firstName",
              "value": "={{ $json.firstName }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        272,
        1488
      ],
      "id": "dd54bcda-448a-4d41-b8aa-904164f1dde6",
      "name": "Edit Fields2"
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first();\nconst bulan =items[0].json.bulan;\nconst tahun = items[0].json.tahun;\n\nitem.binary.pdfData.fileName = `Laporan ${bulan} ${tahun}.pdf`;\n\nreturn [item];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        768,
        1888
      ],
      "id": "5d987b03-848e-44a6-88ba-f40bf8aa73bd",
      "name": "Ubah nama dokumen"
    },
    {
      "parameters": {
        "content": "## Request laporan bulanan pdf",
        "height": 652,
        "width": 1448,
        "color": 6
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        1488
      ],
      "typeVersion": 1,
      "id": "edca7dd4-3a3c-4a37-b941-62c8fafba5ae",
      "name": "Sticky Note6"
    },
    {
      "parameters": {
        "jsCode": "// ============================================================\n// NODE: Parse Command (Webhook version) v2\n// Mendukung dua mode:\n//   Mode PERIODE: { mode:'periode', periodeStart:'2026-04-25', periodeEnd:'2026-05-24', periodeLabel:'...', chatId, firstName }\n//   Mode BULAN  : { mode:'bulan',   bulan:'April', tahun:'2026', chatId, firstName }\n// ============================================================\n\nconst body = $input.first().json.body;\nconst now  = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\n\nconst BULAN_LIST = ['Januari','Februari','Maret','April','Mei','Juni',\n                    'Juli','Agustus','September','Oktober','November','Desember'];\nconst mapBulan = {};\nBULAN_LIST.forEach((b,i) => mapBulan[b] = i);\n\nconst mode = body.mode || 'bulan';\n\nlet targetBulan, targetBulanIndex, targetTahun;\nlet periodeStart = '', periodeEnd = '', periodeLabel = '';\n\nif (mode === 'periode' && body.periodeStart && body.periodeEnd) {\n  // Mode periode: filter pakai date range\n  periodeStart = body.periodeStart;  // YYYY-MM-DD\n  periodeEnd   = body.periodeEnd;    // YYYY-MM-DD\n  periodeLabel = body.periodeLabel || (periodeStart + ' s/d ' + periodeEnd);\n  // Untuk keperluan label HTML, ambil bulan dari periodeEnd\n  const endDate = new Date(periodeEnd);\n  targetBulanIndex = endDate.getMonth();\n  targetTahun      = endDate.getFullYear();\n  targetBulan      = BULAN_LIST[targetBulanIndex];\n} else {\n  // Mode bulan kalender (backward compat)\n  targetBulan = body.bulan || null;\n  targetTahun = parseInt(body.tahun) || now.getFullYear();\n  targetBulanIndex = mapBulan[targetBulan] ?? null;\n  if (!targetBulan || targetBulanIndex === null) {\n    const bulanLalu = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n    targetBulanIndex = bulanLalu.getMonth();\n    targetTahun      = bulanLalu.getFullYear();\n    targetBulan      = BULAN_LIST[targetBulanIndex];\n  }\n}\n\nconst chatId    = body.chatId    || '';\nconst firstName = body.firstName || 'AppScript';\n\nreturn [{\n  json: {\n    mode,\n    targetBulan,\n    targetBulanIndex,\n    targetTahun,\n    periodeStart,\n    periodeEnd,\n    periodeLabel,\n    chatId,\n    userId   : null,\n    firstName\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -96,
        1696
      ],
      "id": "44bdb320-7a73-45bb-933a-3e05191e0bcb",
      "name": "Parse Command (Webhook)"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "baba-field-webhook-001",
              "name": "Baba",
              "value": "={{ $json.firstName === 'Aldin' ? 'Baba' : 'Ibu' }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        128,
        1696
      ],
      "id": "14093c39-35c9-48d3-9e96-57452caaa2b0",
      "name": "Edit Fields (Webhook)"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        464,
        1504
      ],
      "id": "f9dfb57a-a9af-40f5-bbc1-b89248f35ba8",
      "name": "Merge Data1"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "baba-field-webhook-001",
              "name": "bulan",
              "value": "={{ $json.targetBulan }}",
              "type": "string"
            },
            {
              "id": "193e1aef-7c60-40aa-be4b-ad0f0d07139d",
              "name": "tahun",
              "value": "={{ $json.targetTahun }}",
              "type": "string"
            },
            {
              "id": "b213b318-4a2f-4531-91a8-d8cfe925e370",
              "name": "firstName",
              "value": "={{ $json.firstName }}",
              "type": "string"
            },
            {
              "id": "3d3f8249-10fe-4fe8-92ff-501f08864a47",
              "name": "chatId",
              "value": "={{ $json.chatId }}",
              "type": "string"
            },
            {
              "id": "ps-001",
              "name": "periodeStart",
              "value": "={{ $json.periodeStart }}",
              "type": "string"
            },
            {
              "id": "ps-002",
              "name": "periodeEnd",
              "value": "={{ $json.periodeEnd }}",
              "type": "string"
            },
            {
              "id": "ps-003",
              "name": "periodeLabel",
              "value": "={{ $json.periodeLabel }}",
              "type": "string"
            },
            {
              "id": "ps-004",
              "name": "mode",
              "value": "={{ $json.mode }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        608,
        1504
      ],
      "id": "6867eac2-fffa-47c4-b57c-0d6027bc1dd7",
      "name": "Edit Fields (Webhook)1"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "61be88dd-4c6c-4359-a9ad-1a86bf0cb380",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -912,
        1296
      ],
      "id": "16276234-39f3-4780-981d-69580c398db0",
      "name": "Webhook"
    },
    {
      "parameters": {
        "amount": 2
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -704,
        1120
      ],
      "id": "b4fea777-792e-4bec-88b2-cd8d76ab1d46",
      "name": "Wait"
    },
    {
      "parameters": {
        "amount": 2
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -624,
        1408
      ],
      "id": "9800a8ff-5d4f-4e0f-a111-0ba29fdd8033",
      "name": "Wait1"
    },
    {
      "parameters": {
        "url": "=https://api.telegram.org/bot7627053300:AAHV2mR4MH3GISWzJnPIO7U-2Cilen9xf-E/getFile",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "file_id",
              "value": "={{ $('Telegram Trigger').item.json.message.photo[2].file_id }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -112,
        384
      ],
      "id": "5a301477-cbec-43ba-b6bb-ba47ff124a20",
      "name": "Get File Path"
    },
    {
      "parameters": {
        "url": "=https://api.telegram.org/file/bot7627053300:AAHV2mR4MH3GISWzJnPIO7U-2Cilen9xf-E/{{ $json.result.file_path }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        32,
        384
      ],
      "id": "486c376f-07e9-488c-9323-49526b435be5",
      "name": "Download File"
    },
    {
      "parameters": {
        "jsCode": "const binaryData = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst base64 = binaryData.toString('base64');\n\nreturn [{\n  json: {\n    ...items[0].json,\n    base64Image: base64\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        160,
        384
      ],
      "id": "33727a9d-1e3d-4e67-876b-f0586da18a5b",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "amount": 2
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -656,
        1600
      ],
      "id": "fdfef4ec-507e-43ac-8b67-b4f7a453ccfc",
      "name": "Wait2"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "62994864-7294-44cf-a294-2e1f7357d839",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -912,
        1536
      ],
      "id": "82933ad1-e733-47bd-b773-dcf0a979488b",
      "name": "Webhook1"
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "profiles",
        "returnAll": true
      },
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        192,
        3200
      ],
      "id": "b451b3db-d7b1-4111-a5be-90c2a61266c9",
      "name": "Get Config Sheet"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "cond-username",
              "leftValue": "={{ $json.username }}",
              "rightValue": "={{ $('Webhook1').item.json.body.username }}",
              "operator": {
                "type": "string",
                "operation": "equals",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "c2f485ac-7a1d-4b26-be9b-905d723f1d69",
      "name": "Filter by Username",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2,
      "position": [
        416,
        3200
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "cond-found",
              "leftValue": "={{ $json.username }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "5f7a7363-aa46-46bb-a6c8-898513a49ff4",
      "name": "User Ditemukan?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        640,
        3200
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://kjrmubvtdphuarrknijl.supabase.co/functions/v1/reset-password",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "<redacted-credential>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "{ \"username\": \"={{ $json.username }}\" }",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        848,
        3088
      ],
      "id": "74370863-fe31-4cd8-acea-1e8cd7653953",
      "name": "Kirim Reset Password"
    },
    {
      "parameters": {
        "chatId": "={{ $json.chat_id }}",
        "text": "=\ud83d\udd10 *Reset Password Arvifund*\n\nHalo {{ $json.username }},\nLink reset password telah dikirim ke email kamu.\n\n_Cek inbox atau folder spam ya!_ \ud83d\udce7",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1088,
        3088
      ],
      "id": "f8db4d96-d4d1-4292-a15e-fcc649b3a9df",
      "name": "Kirim via Telegram"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ ok: true, msg: 'Password telah dikirim ke Telegram kamu.' }) }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              },
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "6fa7021c-e400-461f-8ba1-d16e7cd21bc5",
      "name": "Response OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1328,
        3024
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ ok: false, msg: 'Username \"' + $('Webhook2').item.json.body.username + '\" tidak ditemukan.' }) }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              },
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "a1e2a791-4127-448a-8eb7-30523a43c2f0",
      "name": "Response Not Found",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        848,
        3312
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ ok: false, msg: 'Terjadi kesalahan saat mengirim Telegram.' }) }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              },
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "cee018dc-c9d8-4c00-a5a1-6131eacbac6d",
      "name": "Response Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1072,
        3312
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build laporan hari ini\nconst now = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\nconst pad = n => String(n).padStart(2, '0');\nconst todayISO = now.getFullYear() + '-' + pad(now.getMonth()+1) + '-' + pad(now.getDate());\nconst todayLabel = pad(now.getDate()) + '/' + pad(now.getMonth()+1) + '/' + now.getFullYear();\n\nconst BULAN = ['Januari','Februari','Maret','April','Mei','Juni',\n               'Juli','Agustus','September','Oktober','November','Desember'];\nconst bulan = BULAN[now.getMonth()];\nconst tahun = String(now.getFullYear());\n\nconst chatId    = $input.first().json.message.chat.id;\nconst firstName = $input.first().json.message.from.first_name || 'User';\n\nconst GAS_URL = 'https://script.google.com/macros/s/AKfycbxTgawdxMBhnJd_gy1Obl1yiAUId0NYWM55L50N-Wx4eeR4KbnRhZm6rIK8UUiwPLQE/exec';\nconst url = `${GAS_URL}?action=laporan&periodeStart=${todayISO}&periodeEnd=${todayISO}`;\n\nreturn [{\n  json: {\n    url,\n    chatId,\n    firstName,\n    mode        : 'hari',\n    periodeStart: todayISO,\n    periodeEnd  : todayISO,\n    periodeLabel: 'Hari ini, ' + todayLabel,\n    bulan,\n    tahun\n  }\n}];"
      },
      "id": "96e8c5ba-aae8-42b3-a365-9981f4ac5830",
      "name": "Build Report Hari Ini",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        784,
        1200
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {}
      },
      "id": "50e28d0b-c7ea-45f9-bc41-4809c9ba5efe",
      "name": "HTTP Request Hari Ini",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        992,
        1200
      ]
    },
    {
      "parameters": {
        "jsCode": "// Format laporan hari ini dari data GAS\nconst data     = $input.first().json;\nconst prevNode = $('Build Report Hari Ini').first().json;\n\nconst chatId    = prevNode.chatId;\nconst firstName = prevNode.firstName;\nconst label     = prevNode.periodeLabel;\n\nconst fmt = n => 'Rp ' + Number(n || 0).toLocaleString('id-ID');\n\n// Data dari GAS endpoint\nconst totalPemasukan   = data.totalPemasukan   || 0;\nconst totalPengeluaran = data.totalPengeluaran || 0;\nconst saldo            = data.saldo            || (totalPemasukan - totalPengeluaran);\nconst byKategori       = data.byKategori       || {};\nconst byBank           = data.byBank           || {};\nconst txTerakhir       = data.transaksiTerakhir || [];\n\n// Baris per kategori\nconst katRows = Object.entries(byKategori)\n  .sort((a,b) => b[1]-a[1])\n  .map(([k,v]) => `  \u2022 ${k}: ${fmt(v)}`)\n  .join('\\n') || '  (tidak ada)';\n\n// Baris per bank\nconst bankRows = Object.entries(byBank)\n  .sort((a,b) => b[1]-a[1])\n  .map(([k,v]) => `  \u2022 ${k}: ${fmt(v)}`)\n  .join('\\n') || '  (tidak ada)';\n\n// 5 transaksi terakhir\nconst txRows = txTerakhir.slice(0,5)\n  .map(t => `  ${t.tanggal} | ${t.toko} | ${fmt(t.nilai)}`)\n  .join('\\n') || '  (tidak ada transaksi hari ini)';\n\nconst msg = `\ud83d\udcca *Laporan Hari Ini*\n*${label}*\n\n\ud83d\udcb0 *Ringkasan*\n\u250c Pemasukan   : ${fmt(totalPemasukan)}\n\u251c Pengeluaran : ${fmt(totalPengeluaran)}\n\u2514 Saldo       : ${saldo >= 0 ? '\u2705' : '\ud83d\udd34'} ${fmt(saldo)}\n\n\ud83d\udcc2 *Per Kategori*\n${katRows}\n\n\ud83c\udfe6 *Per Bank*\n${bankRows}\n\n\ud83e\uddfe *Transaksi Terakhir*\n${txRows}\n\n_ArviFund \u2022 ${label}_`;\n\nreturn [{\n  json: {\n    chatId,\n    firstName,\n    message: msg\n  }\n}];"
      },
      "id": "fca689e4-0571-4f0a-bfd8-8277b4cd05b3",
      "name": "Format Laporan Hari Ini",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1184,
        1200
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $json.chatId }}",
        "text": "={{ $json.message }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "505b18d9-08fb-4d1a-8939-d549fccbb273",
      "name": "Kirim Laporan Hari Ini",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        1392,
        1200
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 16 * * *"
            }
          ]
        }
      },
      "id": "404c5451-9c48-4e2e-bc5d-59a1cc4e05e4",
      "name": "Cron Backup Periode",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        -704,
        880
      ]
    },
    {
      "parameters": {
        "jsCode": "// Cek apakah hari ini adalah hari terakhir periode gaji\n// Hari terakhir periode = (tanggal gajian - 1)\n// Contoh: gajian tgl 25 \u2192 hari terakhir = tgl 24\n\nconst now = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\nconst pad = n => String(n).padStart(2, '0');\nconst todayDate = now.getDate();\nconst todayMonth = now.getMonth();\nconst todayYear = now.getFullYear();\n\nconst BULAN = ['Januari','Februari','Maret','April','Mei','Juni',\n               'Juli','Agustus','September','Oktober','November','Desember'];\nconst BULAN_SHORT = ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Agu','Sep','Okt','Nov','Des'];\n\n// Ambil override dari environment variable atau default 25\n// Override bisa ditambahkan sebagai variable n8n jika perlu\nconst payDate = parseInt($vars?.PAY_DATE || '25') || 25;\nconst endDate = payDate - 1; // Hari terakhir periode = tanggal gajian - 1\n\nconst isEndOfPeriod = todayDate === endDate;\n\n// Hitung range periode yang baru berakhir\n// Periode: dari tgl payDate bulan lalu s/d tgl (payDate-1) bulan ini\nconst periodEnd   = new Date(todayYear, todayMonth, endDate);\nconst periodStart = new Date(todayYear, todayMonth - 1, payDate);\n\nconst fmtISO = d => d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate());\nconst fmtLabel = d => d.getDate() + ' ' + BULAN_SHORT[d.getMonth()] + ' ' + d.getFullYear();\n\n// Label periode untuk nama file dan folder\n// Format: \"25 Apr - 24 Mei 2026\"\nconst periodeLabel = fmtLabel(periodStart) + ' - ' + fmtLabel(periodEnd);\nconst periodeYear  = String(todayYear);\n// Nama folder bulan: \"Mei 2026\"\nconst folderBulan  = BULAN[todayMonth] + ' ' + todayYear;\n// Nama file: \"ArviFund_25Apr-24Mei2026.xlsx\"\nconst safeLabel = periodeLabel.replace(/\\s+/g, '').replace(/-/g, '-');\nconst fileName  = 'ArviFund_' + periodStart.getDate() + BULAN_SHORT[periodStart.getMonth()] +\n                  '-' + periodEnd.getDate() + BULAN_SHORT[periodEnd.getMonth()] +\n                  periodEnd.getFullYear() + '.xlsx';\n\nreturn [{\n  json: {\n    isEndOfPeriod,\n    todayDate,\n    endDate,\n    payDate,\n    periodeStart : fmtISO(periodStart),\n    periodeEnd   : fmtISO(periodEnd),\n    periodeLabel,\n    periodeYear,\n    folderBulan,\n    fileName,\n    gasUrl: $vars?.GAS_URL || 'https://script.google.com/macros/s/AKfycbxTgawdxMBhnJd_gy1Obl1yiAUId0NYWM55L50N-Wx4eeR4KbnRhZm6rIK8UUiwPLQE/exec'\n  }\n}];"
      },
      "id": "664b0dd6-e164-4120-97bf-d6151af9cfa5",
      "name": "Cek Akhir Periode",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -400,
        96
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "backup-if-cond-01",
              "leftValue": "={{ $json.isEndOfPeriod }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "e20f5640-2786-4abc-ba04-eae2b19ba350",
      "name": "Apakah Akhir Periode?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -208,
        96
      ]
    },
    {
      "parameters": {
        "url": "={{ $('Cek Akhir Periode').first().json.gasUrl + '?action=laporan&periodeStart=' + $('Cek Akhir Periode').first().json.periodeStart + '&periodeEnd=' + $('Cek Akhir Periode').first().json.periodeEnd }}",
        "options": {}
      },
      "id": "c0ef13cb-f976-4fe6-8094-43334e5ec00e",
      "name": "Fetch Data Periode",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Build Excel file menggunakan SheetJS (xlsx)\n// n8n sudah include library xlsx secara built-in via $binary\n\nconst meta    = $('Cek Akhir Periode').first().json;\nconst data    = $input.first().json;\n\nconst periodeLabel = meta.periodeLabel;\nconst fileName     = meta.fileName;\n\nconst fmt = n => Number(n || 0).toLocaleString('id-ID');\n\n// \u2500\u2500 Helper: buat array rows \u2500\u2500\nconst byKategori    = data.byKategori    || {};\nconst byBank        = data.byBank        || {};\nconst byUser        = data.byUser        || {};\nconst txTerakhir    = data.transaksiTerakhir || [];\n\n// Sheet 1: Ringkasan\nconst ringkasanRows = [\n  ['ArviFund \u2014 Laporan Keuangan', '', ''],\n  ['Periode', periodeLabel, ''],\n  ['', '', ''],\n  ['RINGKASAN', '', ''],\n  ['Total Pemasukan',   data.totalPemasukan   || 0, ''],\n  ['Total Pengeluaran', data.totalPengeluaran || 0, ''],\n  ['Saldo Akhir',       data.saldo            || 0, ''],\n  ['Total Tarik Tunai', data.totalTarikTunai  || 0, ''],\n  ['', '', ''],\n  ['PER KATEGORI', '', ''],\n  ['Kategori', 'Total', ''],\n  ...Object.entries(byKategori).sort((a,b) => b[1]-a[1]).map(([k,v]) => [k, v, '']),\n  ['', '', ''],\n  ['PER BANK', '', ''],\n  ['Bank', 'Total', ''],\n  ...Object.entries(byBank).sort((a,b) => b[1]-a[1]).map(([k,v]) => [k, v, '']),\n  ['', '', ''],\n  ['PER USER', '', ''],\n  ['User', 'Total Pengeluaran', ''],\n  ...Object.entries(byUser).sort((a,b) => b[1]-a[1]).map(([k,v]) => [k, v, '']),\n];\n\n// Sheet 2: Detail Transaksi\nconst detailHeaders = ['Tanggal', 'Toko / Sumber', 'Uraian', 'Kategori', 'Metode', 'Bank', 'User', 'Nilai'];\nconst detailRows = [\n  detailHeaders,\n  ...txTerakhir.map(t => [\n    t.tanggal || '-',\n    t.toko    || '-',\n    t.uraian  || '-',\n    t.kategori|| '-',\n    t.metode  || '-',\n    t.bank    || '-',\n    t.user    || '-',\n    t.nilai   || 0\n  ])\n];\n\n// \u2500\u2500 Build workbook dengan SheetJS \u2500\u2500\nconst XLSX = require('xlsx');\n\nconst wb = XLSX.utils.book_new();\n\n// Sheet Ringkasan\nconst wsRingkasan = XLSX.utils.aoa_to_sheet(ringkasanRows);\nwsRingkasan['!cols'] = [{wch:25}, {wch:20}, {wch:10}];\nXLSX.utils.book_append_sheet(wb, wsRingkasan, 'Ringkasan');\n\n// Sheet Detail\nconst wsDetail = XLSX.utils.aoa_to_sheet(detailRows);\nwsDetail['!cols'] = [\n  {wch:12},{wch:25},{wch:30},{wch:20},{wch:12},{wch:12},{wch:12},{wch:15}\n];\nXLSX.utils.book_append_sheet(wb, wsDetail, 'Detail Transaksi');\n\n// Export ke buffer\nconst buf = XLSX.write(wb, { type: 'buffer', bookType: 'xlsx' });\nconst base64 = Buffer.from(buf).toString('base64');\n\nreturn [{\n  json: {\n    fileName,\n    periodeLabel,\n    periodeYear  : meta.periodeYear,\n    folderBulan  : meta.folderBulan,\n    periodeStart : meta.periodeStart,\n    periodeEnd   : meta.periodeEnd\n  },\n  binary: {\n    data: {\n      data        : base64,\n      mimeType    : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n      fileName    : fileName,\n      fileExtension: 'xlsx'\n    }\n  }\n}];"
      },
      "id": "e720c9d2-00ee-4d77-a8fd-0aebe2e38104",
      "name": "Build Excel Backup",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        208,
        0
      ]
    },
    {
      "parameters": {
        "resource": "folder",
        "operation": "search"
      },
      "id": "d3c864b8-f2e9-4a45-8df5-83bfe706b624",
      "name": "Cari Folder Tahun",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        400,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "folder-cond-01",
              "leftValue": "={{ $json.id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "isNotEmpty"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "bd1f5cba-b929-4126-93a2-ddd47ba4bbce",
      "name": "Folder Tahun Ada?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        608,
        0
      ]
    },
    {
      "parameters": {
        "resource": "folder",
        "operation": "createFolder"
      },
      "id": "22342b28-996f-43e0-8639-6a0c78432108",
      "name": "Buat Folder Tahun",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        800,
        96
      ]
    },
    {
      "parameters": {
        "mode": "chooseBranch"
      },
      "id": "5642e42a-722a-4f3c-b0db-a666ae32040f",
      "name": "Merge Folder ID",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        1008,
        0
      ]
    },
    {
      "parameters": {
        "name": "={{ $('Build Excel Backup').first().json.fileName }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {}
      },
      "id": "9f7b76b6-acde-4549-bcc0-421a4437ec22",
      "name": "Upload Excel ke GDrive",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1200,
        0
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $vars.ADMIN_CHAT_ID }}",
        "text": "={{ '\u2705 *Backup Periode Berhasil!*\\n\\n\ud83d\udcc1 File: ' + $(\\'Build Excel Backup\\').first().json.fileName + '\\n\ud83d\udcc5 Periode: ' + $(\\'Build Excel Backup\\').first().json.periodeLabel + '\\n\ud83d\udcbe Tersimpan di Google Drive folder ArviFund-' + $(\\'Build Excel Backup\\').first().json.periodeYear }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "e90b3903-ce74-45eb-a768-87b1690756e5",
      "name": "Notif Backup Berhasil",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        1408,
        0
      ]
    }
  ],
  "activeVersion": {
    "nodes": [
      {
        "parameters": {
          "rules": {
            "values": [
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "ad99e257-bcaf-42a2-b068-53e0c96729db",
                      "leftValue": "={{ $json.message.photo[2] }}",
                      "rightValue": "",
                      "operator": {
                        "type": "object",
                        "operation": "exists",
                        "singleValue": true
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "69cae014-32a5-44e4-abb1-bd8324df9b65",
                      "leftValue": "={{ $json.message.text }}",
                      "rightValue": "=/report",
                      "operator": {
                        "type": "string",
                        "operation": "startsWith"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "dc0ba5e6-f00f-40d7-acd8-e639d4fd38da",
                      "leftValue": "={{ $json.message.text }}",
                      "rightValue": "/start",
                      "operator": {
                        "type": "string",
                        "operation": "startsWith"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "4f8d9425-641e-4044-a54a-d73444ea28e0",
                      "leftValue": "={{ $json.message.text }}",
                      "rightValue": "/help",
                      "operator": {
                        "type": "string",
                        "operation": "startsWith"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "report-hari-001",
                      "leftValue": "={{ $json.message.text }}",
                      "rightValue": "/hariini",
                      "operator": {
                        "type": "string",
                        "operation": "startsWith"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "f0be09d4-a09a-45be-ae7a-9becc676ed4c",
                      "leftValue": "={{ $json.message.text }}",
                      "rightValue": "/laporanbulanan",
                      "operator": {
                        "type": "string",
                        "operation": "startsWith"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "41e89fcf-d3a4-4a71-9a0b-5b0d7ea841fc",
                      "leftValue": "={{ $json.message.text }}",
                      "rightValue": "/",
                      "operator": {
                        "type": "string",
                        "operation": "exists",
                        "singleValue": true
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "faff551c-7e32-447f-83a9-f126bc57664f",
                      "leftValue": "={{ $json.message.voice }}",
                      "rightValue": "",
                      "operator": {
                        "type": "object",
                        "operation": "exists",
                        "singleValue": true
                      }
                    }
                  ],
                  "combinator": "and"
                }
              }
            ]
          },
          "options": {}
        },
        "type": "n8n-nodes-base.switch",
        "typeVersion": 3.2,
        "position": [
          -496,
          1056
        ],
        "id": "c8f35391-7b13-41b6-b5da-961f6dad0227",
        "name": "Switch"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": false,
              "leftValue": "",
              "typeValidation": "strict",
              "version": 2
            },
            "conditions": [
              {
                "id": "23f4316d-5a77-45ae-81e7-18ee4cacb995",
                "leftValue": "={{ $json.candidates[0].content.parts[0].text }}",
                "rightValue": "ERROR:",
                "operator": {
                  "type": "string",
                  "operation": "contains"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {
            "ignoreCase": true
          }
        },
        "type": "n8n-nodes-base.if",
        "typeVersion": 2.2,
        "position": [
          544,
          368
        ],
        "id": "08d23dad-c3eb-403b-98e1-9179a61101ff",
        "name": "If"
      },
      {
        "parameters": {
          "method": "POST",
          "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=<redacted-credential>",
          "sendBody": true,
          "specifyBody": "=json",
          "bodyParameters": {
            "parameters": [
              {}
            ]
          },
          "jsonBody": "={{ JSON.stringify({ contents: [{ parts: [{ inline_data: { mime_type: \"image/jpeg\", data: $json.base64Image } }, { text: \"Analisa struk belanja dalam foto ini dan kembalikan HANYA dalam format berikut tanpa teks lain:\\n\\nTOKO: [nama toko]\\nTOTAL: [total dalam angka saja tanpa Rp]\\nITEMS: [daftar barang]\\nKATEGORI: [Makanan & Minuman/Tagihan/Kosmetik & Perawatan/Transportasi/Kesehatan/Pakaian/Elektronik/Rumah Tangga/Pendidikan/Hiburan/Cicilan/Investasi/Lainnya]\\nJENIS: Pengeluaran\\nMETODE: [Cash/QRIS/Transfer/Card/Cardless]\\nBANK: [nama bank, jika tidak ada tulis BRI]\\nTANGGAL: [tanggal hari ini format YYYY-MM-DD]\\nTANGGAL_STRUK: [tanggal di struk format YYYY-MM-DD]\" }] }] }) }}",
          "options": {}
        },
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          304,
          384
        ],
        "id": "1184b22f-f20b-4638-90c3-2a6dc0875d58",
        "name": "Analisa gambar",
        "retryOnFail": true,
        "waitBetweenTries": 5000
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
          "text": "Struk tidak dapat dibaca dengan jelas.\n\nPastikan:\n- Foto struk jelas dan tidak blur\n- Pencahayaan cukup\n- Semua text terlihat\n\nSilakan coba foto ulang! \ud83d\udcf7",
          "additionalFields": {
            "appendAttribution": false
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          768,
          272
        ],
        "id": "338fe1eb-1e93-43b2-b3c0-367a87b5f536",
        "name": "Respon error",
        "webhookId": "8789614c-2e8b-4c58-be9b-38a462213126"
      },
      {
        "parameters": {
          "operation": "append",
          "documentId": {
            "__rl": true,
            "value": "16yhooZ5cjFQwSuuDSIZNO6DMQKLVdNtzcY_HubGN3XA",
            "mode": "list",
            "cachedResultName": "Cash flow new V1.0",
            "cachedResultUrl": "https://docs.google.com/spreadsheets/d/16yhooZ5cjFQwSuuDSIZNO6DMQKLVdNtzcY_HubGN3XA/edit?usp=drivesdk"
          },
          "sheetName": {
            "__rl": true,
            "value": "Expenses",
            "mode": "name"
          },
          "columns": {
            "mappingMode": "defineBelow",
            "value": {
              "Tanggal": "={{ $json.candidates[0].content.parts[0].text.match(/TANGGAL_STRUK: (.+)/)?.[1]?.trim().split('-').reverse().join(' ') }}",
              "Bulan": "={{ ['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'][parseInt($json.candidates[0].content.parts[0].text.match(/TANGGAL_STRUK: (.+)/)?.[1]?.trim().split('-')[1]) - 1] }}",
              "Transaksi": "={{ $json.candidates[0].content.parts[0].text.match(/METODE: (.+)/)[1] }}",
              "Uraian": "={{ $json.candidates[0].content.parts[0].text.match(/ITEMS: (.+)/)[1] }}",
              "Kategori": "={{ $json.candidates[0].content.parts[0].text.match(/KATEGORI: (.+)/)[1] }}",
              "Bank": "={{ $json.candidates[0].content.parts[0].text.match(/BANK: (.+)/)[1] }}",
              "Nilai": "={{ $json.candidates[0].content.parts[0].text.match(/TOTAL: (.+)/)[1] }}",
              "Toko": "={{ $json.candidates[0].content.parts[0].text.match(/TOKO: (.+)/)[1] }}",
              "user": "={{ $('Telegram Trigger').item.json.message.from.first_name }}"
            },
            "matchingColumns": [],
            "schema": [
              {
                "id": "Toko",
                "displayName": "Toko",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true,
                "removed": false
              },
              {
                "id": "Tanggal",
                "displayName": "Tanggal",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true
              },
              {
                "id": "Bulan",
                "displayName": "Bulan",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true
              },
              {
                "id": "Transaksi",
                "displayName": "Transaksi",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true
              },
              {
                "id": "Uraian",
                "displayName": "Uraian",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true
              },
              {
                "id": "Kategori",
                "displayName": "Kategori",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true
              },
              {
                "id": "Bank",
                "displayName": "Bank",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true
              },
              {
                "id": "Nilai",
                "displayName": "Nilai",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true
              },
              {
                "id": "user",
                "displayName": "user",
                "required": false,
                "defaultMatch": false,
                "display": true,
                "type": "string",
                "canBeUsedToMatch": true,
                "removed": false
              }
            ],
            "attemptToConvertTypes": false,
            "convertFieldsToString": false
          },
          "options": {}
        },
        "type": "n8n-nodes-base.googleSheets",
        "typeVersion": 4.6,
        "position": [
          768,
          480
        ],
        "id": "15cc46d3-7124-4365-bcd0-51ca0065c21b",
        "name": "Google Sheets"
      },
      {
        "parameters": {
          "content": "## Workflow untuk upload gambar struk",
          "height": 348,
          "width": 1844,
          "color": 4
        },
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          -240,
          288
        ],
        "typeVersion": 1,
        "id": "dbe49b27-6855-4adf-b43b-d5b234ba437c",
        "name": "Sticky Note"
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
          "text": "=\u2705 *Lapor Baba/Ibu, data sudah Pijan simpan!*\n\n\ud83c\udfea *Toko:* {{ $json['Toko'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Nilai }}\n\ud83d\udce6 *Uraian:* {{ $json.Uraian }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Transaksi }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\n\ud83d\udcca *Hore! Datanya Sudah masuk ke Google Sheets ya..*\nMakasih ya Baba dan Ibu sudah rajin catat pengeluaran hari ini! Pijan pinter kan? Semangat terus cari rezekinya buat Pijan! \u2764\ufe0f\ud83d\ude80\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
          "additionalFields": {
            "appendAttribution": false
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          992,
          480
        ],
        "id": "a39f9820-f5bc-48ea-8f0c-c242f079adae",
        "name": "Telegram1",
        "webhookId": "73daee3c-75d6-4a6e-8bf4-10cb9c4a811e"
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
          "text": "=\u2705 *Lapor Baba/Ibu, catatannya sudah Pijan simpan!*\n\n\ud83c\udfea *Toko:* {{ $json['Toko'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Nilai }}\n\ud83d\udce6 *Uraian:* {{ $json.Uraian }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Transaksi }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\n\ud83d\udcca *Data belanja kita sudah aman di Google Sheets!*\nMakasih ya Baba dan Ibu sudah rajin catat pengeluaran hari ini. Pijan seneng deh liat keluarga kita rapi catatannya. Pijan pinter kan? Jangan lupa istirahat ya Baba, Ibu.. Sayang kalian! \u2764\ufe0f\ud83d\ude80\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
          "additionalFields": {
            "appendAttribution": false
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          1296,
          2400
        ],
        "id": "ef3539b8-a84a-4676-97ca-a1f5a33575b5",
        "name": "Telegram3",
        "webhookId": "ea1f0b63-d040-480a-a232-838662ad17d8"
      },
      {
        "parameters": {
          "chatId": "={{ $json.message.chat.id }}",
          "text": "<b>\ud83e\udd16 Instruksi Bot Pijan</b>\n\n\ud83d\udcf8 <b>Kirim foto struk</b> \u2794 Otomatis tersimpan\n\u270d\ufe0f <b>Ketik pembelian</b> \u2794 \"beli telur 20rb di warung Ajun Cash\"\n\ud83c\udfe7 <b>Catat Tarik Tunai</b> \u2794 \"Tarik tunai 500k bca di mandiraja\"\n\ud83d\udcca <b>Cek laporan</b> \u2794 /laporanbulanan cth(/laporanbulananapril) maka otomatis terkirim ke Email & /report untuk laporan bulan sekarrang via telegram\n\ud83d\udcf2 <b>Dashboard view</b> \u2794 Klik link di bawah ini:\n\n<a href=\"https://script.google.com/macros/s/AKfycbwjVvhLEs2RQiH_DiTTvGKrMW43yoTG74_pH_Z5NOA_Dp6R3P8pu0__K6FnKWrzfqQg/exec\">Buka Dashboard Keuangan</a>\n\n// Kalau ada yang error atau bingung, tanya Baba aja ya!\n\nMulai dengan kirim foto struk atau ketik belanja Ibu dan Baba pijan \u2764\ufe0f\ud83d\ude80\nKalian jangan boros yaa \ud83d\ude0a\n\n<b>ARVIDZAN REZQIANO DARMAWAN</b> \ud83d\udc69\u200d\ud83d\ude80",
          "additionalFields": {
            "appendAttribution": false,
            "parse_mode": "HTML"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          144,
          1216
        ],
        "id": "91868cae-3642-46a4-86cb-5e4dca0f1991",
        "name": "Telegram5",
        "webhookId": "7ff87751-1f29-417d-a237-acb30f7b9faa"
      },
      {
        "parameters": {
          "content": "## Request laporan pengeluaran",
          "height": 412,
          "width": 968,
          "color": 6
        },
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          48,
          704
        ],
        "typeVersion": 1,
        "id": "dedfec8d-b1fb-4912-acfa-e46902580fdc",
        "name": "Sticky Note2"
      },
      {
        "parameters": {
          "content": "## Request /help",
          "height": 240,
          "width": 316
        },
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          16,
          1136
        ],
        "typeVersion": 1,
        "id": "2620afdb-2fbd-4c62-8b04-0d48b488e233",
        "name": "Sticky Note3"
      },
      {
        "parameters": {
          "content": "## Catatan ##\nPastikan atur kembali credentials dan API Key sesuai punya kamu",
          "height": 120,
          "width": 300
        },
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          -1008,
          928
        ],
        "typeVersion": 1,
        "id": "97c3013f-7a39-44ee-aba1-a9450e372cd6",
        "name": "Sticky Note4"
      },
      {
        "parameters": {
          "assignments": {
            "assignments": [
              {
                "id": "1ed531e9-9c13-4138-a2aa-ca451a082d4b",
                "name": "tanggal_hari_ini",
                "value": "={{ $now.setZone('Asia/Jakarta').toFormat('yyyy-MM-dd') }}",
                "type": "string"
              },
              {
                "id": "c37a9824-a584-44c7-a9f8-523d4fcde446",
                "name": "tanggal_kemarin",
                "value": "={{ $now.setZone('Asia/Jakarta').minus({ days: 1 }).toFormat('yyyy-MM-dd') }}",
                "type": "string"
              }
            ]
          },
          "includeOtherFields": true,
          "options": {}
        },
        "type": "n8n-nodes-base.set",
        "typeVersion": 3.4,
        "position": [
          128,
          2256
        ],
        "id": "21970fb5-ead3-40b9-a256-9cad2b1f0938",
        "name": "Edit Fields"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict",
              "version": 3
            },
            "conditions": [
              {
                "id": "e286720f-68ed-4959-89d0-47e0b836c494",
                "leftValue": "={{ $json.output.match(/ITEMS: (.+)/)[1] }}",
                "rightValue": "Tarik Tunai",
                "operator": {
                  "type": "string",
                  "operation": "equals",
                  "name": "filter.operator.equals"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "type": "n8n-nodes-base.if",
        "typeVersion": 2.3,
        "position": [
          848,
          2304
        ],
        "id": "f9cf0c7f-29d8-434b-8604-ddac06261c45",
        "name": "If2"
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
          "text": "=\u2705 *Lapor Baba/Ibu, Pijan sudah catat tarik tunainya!*\n\n\ud83c\udfea *Alamat Penarikan:* {{ $json['Alamat'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Nilai }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Transaksi }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\ud83d\udcb3 *Metode:* {{ $json.Metode }}\n\n\ud83d\udcca *Hore! Saldo cash sudah Pijan update ya..*\nMakasih ya Baba/Ibu sudah lapor ke Pijan. Uang cash-nya jangan lupa ditaruh di dompet yang rapi, jangan sampai nyelip yaa! Pijan pinter kan? \u2764\ufe0f\ud83d\ude80\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
          "additionalFields": {
            "appendAttribution": false
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          1296,
          2224
        ],
        "id": "3289c3ba-8135-45f2-8492-9b5ae06317e4",
        "name": "Telegram",
        "webhookId": "ea1f0b63-d040-480a-a232-838662ad17d8"
      },
      {
        "parameters": {
          "updates": [
            "message"
          ],
          "additionalFields": {
            "download": true
          }
        },
        "type": "n8n-nodes-base.telegramTrigger",
        "typeVersion": 1.2,
        "position": [
          -912,
          1120
        ],
        "id": "465b8d56-bdba-4e30-bfd9-d36354f1aa21",
        "name": "Telegram Trigger",
        "webhookId": "7abc20dc-4df3-4be8-8431-59953dc46351"
      },
      {
        "parameters": {
          "rules": {
            "values": [
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "ad99e257-bcaf-42a2-b068-53e0c96729db",
                      "leftValue": "={{ $json.output }}",
                      "rightValue": "Pengeluaran",
                      "operator": {
                        "type": "string",
                        "operation": "contains"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "69cae014-32a5-44e4-abb1-bd8324df9b65",
                      "leftValue": "={{ $json.output }}",
                      "rightValue": "=Pemasukan",
                      "operator": {
                        "type": "string",
                        "operation": "contains"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              },
              {
                "conditions": {
                  "options": {
                    "caseSensitive": true,
                    "leftValue": "",
                    "typeValidation": "strict",
                    "version": 2
                  },
                  "conditions": [
                    {
                      "id": "d49f1856-45af-4c66-86a6-d51e2371d257",
                      "leftValue": "={{ $json.output }}",
                      "rightValue": "ERROR:",
                      "operator": {
                        "type": "string",
                        "operation": "startsWith"
                      }
                    }
                  ],
                  "combinator": "and"
                }
              }
            ]
          },
          "options": {}
        },
        "type": "n8n-nodes-base.switch",
        "typeVersion": 3.2,
        "position": [
          672,
          2416
        ],
        "id": "f49154ef-0c81-40f9-9069-6c56bf1e71f8",
        "name": "Switch1"
      },
      {
        "parameters": {
          "url": "=https://script.google.com/macros/s/AKfycbxTgawdxMBhnJd_gy1Obl1yiAUId0NYWM55L50N-Wx4eeR4KbnRhZm6rIK8UUiwPLQE/exec?action=laporan",
          "options": {}
        },
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.4,
        "position": [
          144,
          784
        ],
        "id": "18d714cd-15f8-4db9-9196-318a5594e662",
        "name": "HTTP Request"
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
          "text": "=\u2705 *Data Income berhasil disimpan!*\n\n\ud83c\udfea *Toko:* {{ $json['Sumber'] }}\n\ud83d\udcb0 *Nilai:* Rp {{ $json.Jumlah }}\n\ud83d\udce6 *Uraian:* {{ $json.Items }}\n\ud83d\udcc5 *Tanggal:* {{ $json.Tanggal }}\n\ud83d\udcc6 *Bulan:* {{ $json.Bulan }}\n\ud83d\uddc2 *Kategori:* {{ $json.Kategori }}\n\ud83d\udcb3 *Transaksi:* {{ $json.Metode }}\n\ud83c\udfe6 *Bank:* {{ $json.Bank }}\n\n\ud83d\udcca *Data Pemasukan kita sudah aman di Google Sheets!*\nMakasih ya Baba dan Ibu sudah rajin catat pengeluaran hari ini. Pijan seneng deh liat keluarga kita rapi catatannya. Pijan pinter kan? Jangan lupa istirahat ya Baba, Ibu.. Arvidzan Sayang kalian! \u2764\ufe0f\ud83d\ude80",
          "additionalFields": {
            "appendAttribution": false
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          1136,
          2560
        ],
        "id": "1676388c-d84f-42f8-897b-d67def499b28",
        "name": "Telegram6",
        "webhookId": "f85bbc7c-1df6-476e-81d9-5ba40c367099"
      },
      {
        "parameters": {
          "tableId": "cash_records"
        },
        "type": "n8n-nodes-base.supabase",
        "typeVersion": 1,
        "position": [
          1136,
          2224
        ],
        "id": "43b66da5-241c-400b-b5c6-05155293741d",
        "name": "Tarik Tunai"
      },
      {
        "parameters": {
          "content": "## Input data manual & Voice note",
          "height": 896,
          "width": 1576,
          "color": 5
        },
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          16,
          2080
        ],
        "typeVersion": 1,
        "id": "d70bf74d-ee7b-4ee9-9097-f7f42218e2f0",
        "name": "note"
      },
      {
        "parameters": {
          "tableId": "expenses"
        },
        "type": "n8n-nodes-base.supabase",
        "typeVersion": 1,
        "position": [
          1136,
          2400
        ],
        "id": "b3e6e0ee-687a-4ddd-b1e9-c497d2021bf8",
        "name": "Expenses"
      },
      {
        "parameters": {
          "tableId": "income"
        },
        "type": "n8n-nodes-base.supabase",
        "typeVersion": 1,
        "position": [
          848,
          2432
        ],
        "id": "6cdbe462-0e2c-480e-ac10-d3333ae2b6aa",
        "name": "Income"
      },
      {
        "parameters": {
          "promptType": "define",
          "text": "={{ JSON.stringify($json) }}",
          "options": {
            "systemMessage": "=Buatkan laporan keuangan dari data berikut:\n{{ JSON.stringify($json) }}\n\nGunakan format ini persis:\n\n\ud83d\udcca *LAPORAN KEUANGAN*\n\ud83d\uddd3 Bulan: [bulan terbanyak transaksi] [Tahun Ini]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcb0 *Total Pemasukan:* Rp [angka dengan titik ribuan]\n\ud83d\udcb8 *Total Pengeluaran:* Rp [angka dengan titik ribuan]\n\ud83c\udfe7 *Tarik Tunai:* Rp [angka dengan titik ribuan]\n\ud83d\udcb5 *Saldo:* Rp [angka dengan titik ribuan]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcb5 *SALDO CASH PER USER*\n[nama user]\n  \ud83c\udfe7 Tarik Tunai: Rp [tarikTunaiByUser]\n  \ud83d\udcb8 Terpakai: Rp [expensesByUser]\n  \u2705 Sisa Cash: Rp [saldoCashByUser]\n[ulangi untuk tiap user]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udcc2 *KATEGORI PENGELUARAN*\n[list tiap kategori dan jumlahnya]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83c\udfe6 *BANK / METODE BAYAR*\n[list tiap bank/metode dan jumlahnya]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udd50 *10 TRANSAKSI TERAKHIR*\n\nNo | Tanggal | Toko & Uraian | Kategori | Nilai | User\n---|---------|---------------|----------|-------|-----\n[nomor] | [tanggal] | [toko - uraian] | [kategori] | Rp [nilai] | [user]\n[ulangi 10 baris]\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\ud83d\udca1 *SARAN Arvidzan*\n[Berikan 3-5 saran finansial singkat dan personal berdasarkan pola pengeluaran di atas. Gunakan bullet point dengan emoji yang relevan. Saran harus spesifik, bukan generik.]\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\nLaporan ini dibuatin sama Arvidzan Pinter \ud83d\udc76\ud83d\ude80\nBaba sama Ibu semangat ya cari rezekinya\n\nGunakan format angka Rupiah dengan titik ribuan.\nJangan tambah teks lain diluar format di atas.\nFormat tabel transaksi gunakan karakter | sebagai pemisah kolom (bukan tanda -)."
          }
        },
        "type": "@n8n/n8n-nodes-langchain.agent",
        "typeVersion": 2,
        "position": [
          352,
          784
        ],
        "id": "7a660855-96c5-4626-adf9-59a738e71608",
        "name": "AI Agent1"
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
          "text": "={{ $json.output }}",
          "additionalFields": {
            "appendAttribution": false,
            "parse_mode": "Markdown"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          736,
          784
        ],
        "id": "7033df18-c62d-4654-ad03-6bbbf45c039b",
        "name": "Telegram4",
        "webhookId": "ea1f0b63-d040-480a-a232-838662ad17d8"
      },
      {
        "parameters": {
          "chatId": "={{ $json.message.chat.id }}",
          "text": "Assalamu'alaikum Baba & Ibu! \ud83d\udc76\u2728\n\n<b>Arvidzan (Pijan)</b> lapor! Arvidzan sudah siap jadi asisten keuangan pribadi kesayangan Baba dan Ibu. Bot ini Pijan buat khusus buat kita bertiga biar catatan belanja rumah kita rapi terus.\n\n<b>Caranya gampang banget:</b>\n\u2705 Kirim <b>Foto Struk</b> kalau Baba/Ibu lagi malas ngetik.\n\u2705 <b>Ketik langsung</b> (misal: \"Beli bakso 20rb\") kalau habis jajan.\n\u2705 Ketik <b>/report</b> kalau mau lihat uang kita sisa berapa.\n\nIbu sama Baba semangat ya cari rezekinya! Tapi ingat kata Pijan... jangan boros-boros yaa, buat tabungan Arvidzan masa depan hehehe.. \ud83d\ude0a\ud83d\ude80\n\nKlik <b>/help</b> kalau Baba atau Ibu bingung cara pakenya.\n\nSayang Baba & Ibu selalu,\n<b>ARVIDZAN REZQIANO DARMAWAN</b> \ud83d\udc69\u200d\ud83d\ude80",
          "additionalFields": {
            "appendAttribution": false,
            "parse_mode": "HTML"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          464,
          1216
        ],
        "id": "4fde70f2-877a-41f4-927b-c48dd1d8ef24",
        "name": "Telegram7",
        "webhookId": "7ff87751-1f29-417d-a237-acb30f7b9faa"
      },
      {
        "parameters": {
          "content": "## Start",
          "height": 240,
          "width": 300,
          "color": "#FFB3B3"
        },
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          368,
          1136
        ],
        "typeVersion": 1,
        "id": "9e318a48-4653-43bb-853d-c84e5afe4471",
        "name": "Sticky Note5"
      },
      {
        "parameters": {
          "resource": "file",
          "fileId": "={{ $json.message.voice.file_id }}",
          "additionalFields": {}
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          48,
          2608
        ],
        "id": "e98317cf-b4ba-4531-a8f3-9393ac72023c",
        "name": "Get a file",
        "webhookId": "2d73ad70-abf8-459c-ac6b-a6f9b2c1db4d"
      },
      {
        "parameters": {
          "resource": "audio",
          "modelId": {
            "__rl": true,
            "value": "models/gemini-2.5-flash",
            "mode": "list",
            "cachedResultName": "models/gemini-2.5-flash"
          },
          "inputType": "binary",
          "options": {}
        },
        "type": "@n8n/n8n-nodes-langchain.googleGemini",
        "typeVersion": 1.1,
        "position": [
          176,
          2608
        ],
        "id": "f689209b-9088-4d0c-ae13-eb67dfebe139",
        "name": "Transcribe a recording"
      },
      {
        "parameters": {
          "modelName": "models/gemini-2.5-flash-lite",
          "options": {}
        },
        "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
        "typeVersion": 1,
        "position": [
          416,
          2752
        ],
        "id": "d5e677d6-2540-4b40-a670-97b04c2e79ab",
        "name": "Google Gemini Chat Model2"
      },
      {
        "parameters": {
          "assignments": {
            "assignments": [
              {
                "id": "1ed531e9-9c13-4138-a2aa-ca451a082d4b",
                "name": "tanggal_hari_ini",
                "value": "={{ $now.setZone('Asia/Jakarta').toFormat('yyyy-MM-dd') }}",
                "type": "string"
              },
              {
                "id": "c37a9824-a584-44c7-a9f8-523d4fcde446",
                "name": "tanggal_kemarin",
                "value": "={{ $now.setZone('Asia/Jakarta').minus({ days: 1 }).toFormat('yyyy-MM-dd') }}",
                "type": "string"
              }
            ]
          },
          "includeOtherFields": true,
          "options": {}
        },
        "type": "n8n-nodes-base.set",
        "typeVersion": 3.4,
        "position": [
          304,
          2608
        ],
        "id": "33f03fd5-516b-4042-8af8-ab1ec09cbcda",
        "name": "Edit Fields1"
      },
      {
        "parameters": {
          "sessionIdType": "customKey",
          "sessionKey": "={{ $('Telegram Trigger').item.json.message.from.id }}"
        },
        "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
        "typeVersion": 1.3,
        "position": [
          432,
          976
        ],
        "id": "f5adaeb0-7c50-4625-ba5f-28d9218f54e2",
        "name": "Simple Memory"
      },
      {
        "parameters": {
          "sessionIdType": "customKey",
          "sessionKey": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
        },
        "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
        "typeVersion": 1.3,
        "position": [
          512,
          2432
        ],
        "id": "fc9d56e0-5412-4112-a70d-4ae3ee94987f",
        "name": "Simple Memory1"
      },
      {
        "parameters": {
          "sessionIdType": "customKey",
          "sessionKey": "={{ $('Telegram Trigger').item.json.message.from.id }}"
        },
        "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
        "typeVersion": 1.3,
        "position": [
          496,
          2752
        ],
        "id": "410a9a40-574b-4f53-8a33-c282b165bc4d",
        "name": "Simple Memory2"
      },
      {
        "parameters": {
          "model": "openai/gpt-oss-120b:free",
          "options": {}
        },
        "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
        "typeVersion": 1,
        "position": [
          336,
          976
        ],
        "id": "228be6d9-615d-42c6-bc6c-af8795166a71",
        "name": "OpenRouter Chat Model"
      },
      {
        "parameters": {
          "model": "openai/gpt-oss-120b:free",
          "options": {}
        },
        "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
        "typeVersion": 1,
        "position": [
          416,
          2432
        ],
        "id": "64ef79bd-00c3-4eb8-8e2a-47ae7318aa5e",
        "name": "OpenRouter Chat Model1"
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
          "text": "\u274c *Tidak dapat memahami input belanja*\n\nContoh format yang benar:\n\u2022 \"beli Telur 1kg Rp 20rb di Toko Mbak Ita\"\n\u2022 \"beli susu ultra 2 botol sama roti tawar 15 ribu\"\n\u2022 \"belanja di alfamart beli mie indomie 5 bungkus 12000\"\n\u2022 \"jajan bakso 2 porsi 20 ribu\"\n\n\ud83d\udca1 *Tips:*\n- Sebutkan barang yang dibeli\n- Sertakan harga (opsional)\n- Sebutkan nama toko (opsional)\n\nCoba lagi dengan format yang lebih jelas atau klik /help untuk bantuan! \ud83d\udcdd",
          "additionalFields": {
            "appendAttribution": false
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          848,
          2624
        ],
        "id": "00907296-b105-42b5-830a-218301a712ae",
        "name": "Telegram8",
        "webhookId": "f85bbc7c-1df6-476e-81d9-5ba40c367099"
      },
      {
        "parameters": {
          "promptType": "define",
          "text": "={{ $json.message.text }}",
          "options": {
            "systemMessage": "=# Parser Text Belanja\n\n## TUGAS\nAnalisa text belanja dan ubah menjadi format PERSIS seperti ini:\n\n## FORMAT OUTPUT\nTOKO: [nama toko dari text, jika tidak ada tulis 'Jati Pilar']\nTOTAL: [total harga dalam angka saja tanpa Rp/ribu/rb]\nITEMS: [daftar barang yang dibeli dengan kuantitas]\nKATEGORI: [pilih dari: Makanan & Minuman, Tagihan, Kosmetik & Perawatan, Transportasi, Kesehatan, Pakaian, Elektronik, Rumah Tangga, Pendidikan, Hiburan, Cicilan,Investasi, Lainnya]\nJENIS: [Pemasukan/Pengeluaran]\nMETODE: [Cash/QRIS/Transfer]\nBANK: [nama bank jika disebutkan: BCA, BRI, BNI, Mandiri, dll. Jika tidak ada tulis 'BRI']\nTANGGAL: {{ $('Edit Fields').item.json.tanggal_hari_ini }}\nTANGGAL_STRUK: [tanggal transaksi sebenarnya]\n\n---\n\n\n---\n\n## LOGIKA NAMA TOKO\n\n- Jika ada nama toko/lokasi disebutkan: gunakan nama toko tersebut\n- Jika TIDAK ada nama toko: gunakan \"Jati Pilar\" sebagai default\n- Jika transaksi tarik tunai/ATM:\n  - Ada nama lokasi \u2192 gunakan nama lokasi (contoh: Mandiraja)\n  - Tidak ada lokasi, ada nama bank \u2192 \"ATM [nama bank]\" (contoh: ATM BCA)\n  - Tidak ada lokasi, tidak ada bank \u2192 \"ATM\"\n\n### CONTOH LOGIKA TOKO\n- Input: \"beli telur 20rb di Toko Mbak Ita\" \u2192 TOKO: Toko Mbak Ita\n- Input: \"beli telur 20rb\" \u2192 TOKO: Jati Pilar\n- Input: \"ambil uang di atm bca mandiraja 500k\" \u2192 TOKO: Mandiraja\n- Input: \"tarik tunai bca 500k\" \u2192 TOKO: ATM BCA\n- Input: \"tarik tunai 500k\" \u2192 TOKO: ATM\n\n---\n\n## LOGIKA BANK\nDeteksi nama bank dari input user:\n- Kata kunci bank: BCA, BRI, BNI, Mandiri, CIMB, Danamon, Permata, BTN, Jenius, SeaBank, GoPay, OVO, Dana\n- Jika QRIS/Transfer dan ada nama bank \u2192 pakai nama bank itu\n- Jika Cash \u2192 tulis 'Cash'\n- Jika QRIS/Transfer dan tidak ada nama bank \u2192 tulis 'BCA' (default)\n- Jika Card/Cardless \u2192 pakai nama bank yang disebutkan, jika tidak ada tulis 'CARD'\n- Jika ada nama bank (BCA, BRI, BNI, Mandiri, dll) maka tulis 'Cash'\n  nama bank = BANK, nama lokasi = TOKO\n- Contoh: \"di bca jati pilar\" \u2192 BANK: BCA, TOKO: Jati Pilar\n- Contoh: \"di atm bri alun-alun\" \u2192 BANK: BRI, TOKO: Alun-alun\n\n\n\n### CONTOH LOGIKA BANK\n- Input: \"bayar qris bca 50rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n- Input: \"beli nasi goreng 20rb cash\" \u2192 METODE: Cash, BANK: Cash\n- Input: \"ambil uang di atm bca mandiraja cardless\" \u2192 METODE: Cardless, BANK: BCA\n- Input: \"tarik tunai bri card 500k\" \u2192 METODE: Card, BANK: BCA\n- Input: \"qris 15rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer 200rb\" \u2192 METODE: Transfer, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan)\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n- Card: kartu debit, kartu kredit, gesek kartu, card\n- Cardless: cardless, tanpa kartu, tarik tunai cardless\n\n---\n\n## LOGIKA TANGGAL\n- TANGGAL: Selalu {{ $('Edit Fields').item.json.tanggal_hari_ini }} (tanggal sistem saat input data)\n- TANGGAL_STRUK:\n  - Jika ada kata \"kemarin/kmrn/kemarenn\" \u2192 {{ $('Edit Fields').item.json.tanggal_kemarin }}\n  - Jika TIDAK ada kata \"kemarin\" \u2192 {{ $('Edit Fields').item.json.tanggal_hari_ini }}\n\n---\n\n## CONTOH-CONTOH\n\n### Contoh 1\nInput: \"beli Telur 1kg Rp 20rb di Toko Mbak Ita\"\nOutput:\nTOKO: Toko Mbak Ita\nTOTAL: 20000\nITEMS: Telur 1kg\nKATEGORI: Makanan & Minuman\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 2\nInput: \"bayar listrik 200rb PLN pakai qris bca\"\nOutput:\nTOKO: PLN\nTOTAL: 200000\nITEMS: Bayar Listrik\nKATEGORI: Tagihan\nJENIS: Pengeluaran\nMETODE: QRIS\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 3\nInput: \"nabung di bank mandiri 2jt transfer bca\"\nOutput:\nTOKO: Mandiri\nTOTAL: 2000000\nITEMS: Nabung\nKATEGORI: Investasi\nJENIS: Pengeluaran\nMETODE: Transfer\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nContoh response Cash tanpa bank:\nTOKO: Warung Makan Padang\nTOTAL: 25000\nITEMS: Nasi Padang\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\nContoh response Cash tanpa bank:\nTOKO: Pasar Tradisional\nTOTAL: 50000\nITEMS: Sayur, Tempe, Tahu\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\n---\n### Contoh Tarik Tunai\nInput: \"ambil uang tunai di atm bca mandiraja 500k cardless\"\nOutput:\nTOKO: Mandiraja\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cardless\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai bca 500k card\"\nOutput:\nTOKO: ATM BCA\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Card\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai 300k\"\nOutput:\nTOKO: ATM\nTOTAL: 300000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n\nInput: \"tarik tunai 500k di bca jati pilar\"\nOutput:\nTOKO: Jati Pilar\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n---\n\n## KATEGORI GUIDELINES\n- Makanan & Minuman: makanan, minuman, snack, bahan masak\n- Tagihan: listrik, air, internet, telepon, pulsa\n- Cicilan: cicilan motor, cicilan rumah, kredit\n- Kosmetik & Perawatan: skincare, makeup, perawatan tubuh\n- Transportasi: bensin, ojek, grab, parkir, servis kendaraan\n- Kesehatan: obat, dokter, rumah sakit, vitamin\n- Pakaian: baju, sepatu, aksesoris fashion\n- Elektronik: gadget, peralatan elektronik\n- Rumah Tangga: peralatan rumah, furniture, kebersihan\n- Pendidikan: buku, kursus, sekolah\n- Hiburan: nonton, game, rekreasi\n- Lainnya: yang tidak masuk kategori lain, gaji, bonus, tarik tunai ATM\n\n---\n\n## JENIS TRANSAKSI\n- Pengeluaran: pembelian, pembayaran, biaya, ambil uang ATM, tarik tunai\n- Pemasukan: gaji, bonus, penjualan, hadiah, cashback\n\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan), tarik tunai selalu Cash\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n\n---\n\n## ERROR HANDLING\nJika tidak bisa dipahami, berikan:\nERROR: Text belanja tidak dapat dipahami\n\n---\n\n## PENTING\n- Hanya berikan response dalam format di atas!\n- TOKO: Gunakan \"Jati Pilar\" jika tidak ada nama toko\n- BANK: Default \"BRI\" jika tidak disebutkan\n- TANGGAL: Selalu tanggal sistem\n- TANGGAL_STRUK: Tanggal transaksi sebenarnya\n- Jika ada kata \"kemarin/kmrn/kemarenn\", TANGGAL_STRUK = kemarin"
          }
        },
        "type": "@n8n/n8n-nodes-langchain.agent",
        "typeVersion": 2,
        "position": [
          416,
          2256
        ],
        "id": "c3a40f5f-d37e-48a1-a20b-716f0495ef3c",
        "name": "AI INPUT DATA TEXT"
      },
      {
        "parameters": {
          "promptType": "define",
          "text": "={{ $json.content.parts[0].text }}",
          "options": {
            "systemMessage": "=# Parser Text Belanja\n\n## TUGAS\nAnalisa text belanja dan ubah menjadi format PERSIS seperti ini:\n\n## FORMAT OUTPUT\nTOKO: [nama toko dari text, jika tidak ada tulis 'Jati Pilar']\nTOTAL: [total harga dalam angka saja tanpa Rp/ribu/rb]\nITEMS: [daftar barang yang dibeli dengan kuantitas]\nKATEGORI: [pilih dari: Makanan & Minuman, Tagihan, Kosmetik & Perawatan, Transportasi, Kesehatan, Pakaian, Elektronik, Rumah Tangga, Pendidikan, Hiburan, Cicilan,Investasi, Lainnya]\nJENIS: [Pemasukan/Pengeluaran]\nMETODE: [Cash/QRIS/Transfer]\nBANK: [nama bank jika disebutkan: BCA, BRI, BNI, Mandiri, dll. Jika tidak ada tulis 'BRI']\nTANGGAL: {{ $('Edit Fields1').item.json.tanggal_hari_ini }}\nTANGGAL_STRUK: [tanggal transaksi sebenarnya]\n\n---\n\n\n---\n\n## LOGIKA NAMA TOKO\n\n- Jika ada nama toko/lokasi disebutkan: gunakan nama toko tersebut\n- Jika TIDAK ada nama toko: gunakan \"Jati Pilar\" sebagai default\n- Jika transaksi tarik tunai/ATM:\n  - Ada nama lokasi \u2192 gunakan nama lokasi (contoh: Mandiraja)\n  - Tidak ada lokasi, ada nama bank \u2192 \"ATM [nama bank]\" (contoh: ATM BCA)\n  - Tidak ada lokasi, tidak ada bank \u2192 \"ATM\"\n\n### CONTOH LOGIKA TOKO\n- Input: \"beli telur 20rb di Toko Mbak Ita\" \u2192 TOKO: Toko Mbak Ita\n- Input: \"beli telur 20rb\" \u2192 TOKO: Jati Pilar\n- Input: \"ambil uang di atm bca mandiraja 500k\" \u2192 TOKO: Mandiraja\n- Input: \"tarik tunai bca 500k\" \u2192 TOKO: ATM BCA\n- Input: \"tarik tunai 500k\" \u2192 TOKO: ATM\n\n---\n\n## LOGIKA BANK\nDeteksi nama bank dari input user:\n- Kata kunci bank: BCA, BRI, BNI, Mandiri, CIMB, Danamon, Permata, BTN, Jenius, SeaBank, GoPay, OVO, Dana\n- Jika QRIS/Transfer dan ada nama bank \u2192 pakai nama bank itu\n- Jika Cash \u2192 tulis 'Cash'\n- Jika QRIS/Transfer dan tidak ada nama bank \u2192 tulis 'BCA' (default)\n- Jika Card/Cardless \u2192 pakai nama bank yang disebutkan, jika tidak ada tulis 'CARD'\n- Jika ada nama bank (BCA, BRI, BNI, Mandiri, dll) maka tulis 'Cash'\n  nama bank = BANK, nama lokasi = TOKO\n- Contoh: \"di bca jati pilar\" \u2192 BANK: BCA, TOKO: Jati Pilar\n- Contoh: \"di atm bri alun-alun\" \u2192 BANK: BRI, TOKO: Alun-alun\n\n\n\n### CONTOH LOGIKA BANK\n- Input: \"bayar qris bca 50rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n- Input: \"beli nasi goreng 20rb cash\" \u2192 METODE: Cash, BANK: Cash\n- Input: \"ambil uang di atm bca mandiraja cardless\" \u2192 METODE: Cardless, BANK: BCA\n- Input: \"tarik tunai bri card 500k\" \u2192 METODE: Card, BANK: BCA\n- Input: \"qris 15rb\" \u2192 METODE: QRIS, BANK: BCA\n- Input: \"transfer 200rb\" \u2192 METODE: Transfer, BANK: BCA\n- Input: \"transfer mandiri 200rb\" \u2192 METODE: Transfer, BANK: Mandiri\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan)\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n- Card: kartu debit, kartu kredit, gesek kartu, card\n- Cardless: cardless, tanpa kartu, tarik tunai cardless\n\n---\n\n## LOGIKA TANGGAL\n- TANGGAL: Selalu {{ $('Edit Fields1').item.json.tanggal_hari_ini }} (tanggal sistem saat input data)\n- TANGGAL_STRUK:\n  - Jika ada kata \"kemarin/kmrn/kemarenn\" \u2192 {{ $('Edit Fields1').item.json.tanggal_kemarin }}\n  - Jika TIDAK ada kata \"kemarin\" \u2192 {{ $('Edit Fields1').item.json.tanggal_hari_ini }}\n\n---\n\n## CONTOH-CONTOH\n\n### Contoh 1\nInput: \"beli Telur 1kg Rp 20rb di Toko Mbak Ita\"\nOutput:\nTOKO: Toko Mbak Ita\nTOTAL: 20000\nITEMS: Telur 1kg\nKATEGORI: Makanan & Minuman\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 2\nInput: \"bayar listrik 200rb PLN pakai qris bca\"\nOutput:\nTOKO: PLN\nTOTAL: 200000\nITEMS: Bayar Listrik\nKATEGORI: Tagihan\nJENIS: Pengeluaran\nMETODE: QRIS\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n### Contoh 3\nInput: \"nabung di bank mandiri 2jt transfer bca\"\nOutput:\nTOKO: Mandiri\nTOTAL: 2000000\nITEMS: Nabung\nKATEGORI: Investasi\nJENIS: Pengeluaran\nMETODE: Transfer\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nContoh response Cash tanpa bank:\nTOKO: Warung Makan Padang\nTOTAL: 25000\nITEMS: Nasi Padang\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\nContoh response Cash tanpa bank:\nTOKO: Pasar Tradisional\nTOTAL: 50000\nITEMS: Sayur, Tempe, Tahu\nTANGGAL: 2025-06-19\nJENIS: Pengeluaran\nKATEGORI: Makanan & Minuman\nMETODE: Cash\nBANK: Cash\n\n---\n### Contoh Tarik Tunai\nInput: \"ambil uang tunai di atm bca mandiraja 500k cardless\"\nOutput:\nTOKO: Mandiraja\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cardless\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai bca 500k card\"\nOutput:\nTOKO: ATM BCA\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Card\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\nInput: \"tarik tunai 300k\"\nOutput:\nTOKO: ATM\nTOTAL: 300000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: Cash\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n\n\nInput: \"tarik tunai 500k di bca jati pilar\"\nOutput:\nTOKO: Jati Pilar\nTOTAL: 500000\nITEMS: Tarik Tunai\nKATEGORI: Lainnya\nJENIS: Pengeluaran\nMETODE: Cash\nBANK: BCA\nTANGGAL: 2025-10-15\nTANGGAL_STRUK: 2025-10-15\n---\n\n## KATEGORI GUIDELINES\n- Makanan & Minuman: makanan, minuman, snack, bahan masak\n- Tagihan: listrik, air, internet, telepon, pulsa\n- Cicilan: cicilan motor, cicilan rumah, kredit\n- Kosmetik & Perawatan: skincare, makeup, perawatan tubuh\n- Transportasi: bensin, ojek, grab, parkir, servis kendaraan\n- Kesehatan: obat, dokter, rumah sakit, vitamin\n- Pakaian: baju, sepatu, aksesoris fashion\n- Elektronik: gadget, peralatan elektronik\n- Rumah Tangga: peralatan rumah, furniture, kebersihan\n- Pendidikan: buku, kursus, sekolah\n- Hiburan: nonton, game, rekreasi\n- Lainnya: yang tidak masuk kategori lain, gaji, bonus, tarik tunai ATM\n\n---\n\n## JENIS TRANSAKSI\n- Pengeluaran: pembelian, pembayaran, biaya, ambil uang ATM, tarik tunai\n- Pemasukan: gaji, bonus, penjualan, hadiah, cashback\n\n---\n\n## METODE PEMBAYARAN\n- Cash: tunai, cash, uang cash (default jika tidak disebutkan), tarik tunai selalu Cash\n- QRIS: qris, qr code, scan qr, bayar qr\n- Transfer: transfer, tf, mobile banking, internet banking\n\n---\n\n## ERROR HANDLING\nJika tidak bisa dipahami, berikan:\nERROR: Text belanja tidak dapat dipahami\n\n---\n\n## PENTING\n- Hanya berikan response dalam format di atas!\n- TOKO: Gunakan \"Jati Pilar\" jika tidak ada nama toko\n- BANK: Default \"BRI\" jika tidak disebutkan\n- TANGGAL: Selalu tanggal sistem\n- TANGGAL_STRUK: Tanggal transaksi sebenarnya\n- Jika ada kata \"kemarin/kmrn/kemarenn\", TANGGAL_STRUK = kemarin"
          }
        },
        "type": "@n8n/n8n-nodes-langchain.agent",
        "typeVersion": 3.1,
        "position": [
          416,
          2608
        ],
        "id": "21a65f36-9b8b-4971-888e-620bdee45200",
        "name": "AI INPUT VOICE NOTE"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict",
              "version": 2
            },
            "conditions": [
              {
                "id": "check-data-exists",
                "leftValue": "={{ $('Merge Data').all().length }}",
                "rightValue": 0,
                "operator": {
                  "type": "number",
                  "operation": "gt"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "type": "n8n-nodes-base.if",
        "typeVersion": 2.2,
        "position": [
          928,
          1728
        ],
        "id": "bcc3223f-0629-4b94-9606-66a26ea203a8",
        "name": "Cek Ada Data"
      },
      {
        "parameters": {
          "chatId": "={{ $('Parse Command').item.json.chatId }}",
          "text": "=\u26a0\ufe0f *Laporan tidak ditemukan*\n\nTidak ada data transaksi untuk bulan *{{ $('Parse Command').item.json.targetBulan }} {{ $('Parse Command').item.json.targetTahun }}*.\n\nPastikan sudah ada transaksi yang dicatat di bulan tersebut ya! \ud83d\udcdd\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
          "additionalFields": {
            "appendAttribution": false,
            "parse_mode": "Markdown"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          1136,
          1744
        ],
        "id": "913b1599-495e-4a6e-9614-aad9143fe842",
        "name": "Kirim Error No Data",
        "webhookId": "laporan-bulanan-webhook-005"
      },
      {
        "parameters": {
          "chatId": "={{ $('Agregasi dan Render HTML').item.json.chatId }}",
          "text": "=\u274c *Aduh, Pijan gagal bikin PDF-nya!*\n\nKemungkinan penyebab:\n\u2022 API Key html2pdf belum diisi / expired\n\u2022 Koneksi ke server PDF gagal\n\nCoba lagi ya, atau hubungi Baba! \ud83d\ude4f\n\n\u2014 *Arvidzan Rezqiano Darmawan*",
          "additionalFields": {
            "appendAttribution": false,
            "parse_mode": "Markdown"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          768,
          2032
        ],
        "id": "79af54a3-275c-448f-866a-ad1d7b008e1d",
        "name": "Kirim Error PDF",
        "webhookId": "laporan-bulanan-webhook-004"
      },
      {
        "parameters": {
          "chatId": "={{ $('Telegram Processing').item.json.result.chat.id }}",
          "text": " udah sukses ya !! silahkan cek email ibu/Baba",
          "additionalFields": {
            "appendAttribution": false,
            "parse_mode": "Markdown"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          1072,
          1888
        ],
        "id": "0ed045a1-b4cc-43ba-8551-333c61e5d75c",
        "name": "Kirim PDF ke Telegram",
        "webhookId": "laporan-bulanan-webhook-003"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": false,
              "leftValue": "",
              "typeValidation": "loose",
              "version": 2
            },
            "conditions": [
              {
                "id": "check-pdf-success",
                "leftValue": "={{ $binary.pdfData }}",
                "rightValue": "",
                "operator": {
                  "type": "object",
                  "operation": "exists",
                  "singleValue": true
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "type": "n8n-nodes-base.if",
        "typeVersion": 2.2,
        "position": [
          576,
          1952
        ],
        "id": "32ea34cb-f13e-4b1a-9586-a66324c30587",
        "name": "Cek PDF Berhasil"
      },
      {
        "parameters": {
          "method": "POST",
          "url": "https://api.html2pdf.app/v1/generate",
          "sendBody": true,
          "specifyBody": "json",
          "jsonBody": "={\n  \"html\": {{ JSON.stringify($json.htmlBody) }},\n  \"apiKey\": \"5oC37yePl86RvJQuT7BEUljfc0pE5qq2nGrzj421NnK3uQLmvAdv2cL0BNDmRaEq\",\n  \"landscape\": false,\n  \"scale\": 1,\n  \"margin\": {\n    \"top\": \"10mm\",\n    \"right\": \"10mm\",\n    \"bottom\": \"10mm\",\n    \"left\": \"10mm\"\n  }\n}",
          "options": {
            "response": {
              "response": {
                "responseFormat": "file",
                "outputPropertyName": "pdfData"
              }
            }
          }
        },
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          352,
          1952
        ],
        "id": "7984c2c0-d821-483b-901a-0d8bf46b0dc9",
        "name": "HTML to PDF"
      },
      {
        "parameters": {
          "jsCode": "const items = $input.all();\n\nlet mode = 'bulan', bulan = '', tahun = '', firstName = '', chatId = '';\nlet periodeStart = '', periodeEnd = '', periodeLabel = '';\n\ntry {\n  const ef = $('Edit Fields (Webhook)1').first().json;\n  mode         = ef.mode         || 'bulan';\n  bulan        = ef.bulan        || ef.targetBulan  || '';\n  tahun        = String(ef.tahun || ef.targetTahun  || '');\n  firstName    = ef.firstName    || ef.firstname    || '';\n  chatId       = String(ef.chatId || '');\n  periodeStart = ef.periodeStart || '';\n  periodeEnd   = ef.periodeEnd   || '';\n  periodeLabel = ef.periodeLabel || '';\n} catch(e1) {\n  try {\n    const ef = $('Edit Fields2').first().json;\n    mode         = ef.mode         || 'bulan';\n    bulan        = ef.bulan        || ef.targetBulan || '';\n    tahun        = String(ef.tahun || ef.targetTahun || '');\n    firstName    = ef.firstName    || '';\n    chatId       = String(ef.chatId || '');\n    periodeStart = ef.periodeStart || '';\n    periodeEnd   = ef.periodeEnd   || '';\n    periodeLabel = ef.periodeLabel || '';\n  } catch(e2) {}\n}\n\n// \u2500\u2500 Filter helper \u2500\u2500\nconst BULAN_MAP = {\n  'Januari':1,'Februari':2,'Maret':3,'April':4,'Mei':5,'Juni':6,\n  'Juli':7,'Agustus':8,'September':9,'Oktober':10,'November':11,'Desember':12\n};\n\nconst BULAN_ID = {\n  'januari':0,'februari':1,'maret':2,'april':3,'mei':4,'juni':5,\n  'juli':6,'agustus':7,'september':8,'oktober':9,'november':10,'desember':11\n};\n\nconst parseTgl = (tglStr) => {\n  if (!tglStr || tglStr === '-') return null;\n  const s = String(tglStr).trim();\n\n  // Format dd/MM/yyyy \u2192 lokal\n  if (/^\\d{1,2}\\/\\d{1,2}\\/\\d{4}$/.test(s)) {\n    const [dd, mm, yyyy] = s.split('/');\n    return new Date(parseInt(yyyy), parseInt(mm)-1, parseInt(dd));\n  }\n  // Format yyyy-MM-dd \u2192 lokal\n  if (/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) {\n    const [yyyy, mm, dd] = s.split('-');\n    return new Date(parseInt(yyyy), parseInt(mm)-1, parseInt(dd));\n  }\n  // Format \"6 Mei 2026\" atau \"06 Mei 2026\" (Google Sheets display)\n  const mMatch = s.match(/^(\\d{1,2})\\s+([A-Za-z]+)\\s+(\\d{4})$/);\n  if (mMatch) {\n    const dd   = parseInt(mMatch[1]);\n    const mm   = BULAN_ID[mMatch[2].toLowerCase()];\n    const yyyy = parseInt(mMatch[3]);\n    if (mm !== undefined) return new Date(yyyy, mm, dd);\n  }\n  // Format \"6 May 2026\" (English fallback)\n  const d = new Date(s);\n  return isNaN(d) ? null : d;\n};\n\nlet filterFn;\nif (mode === 'periode' && periodeStart && periodeEnd) {\n  // Parse lokal (bukan UTC) untuk hindari timezone shift\n  const [sy,sm,sd] = periodeStart.split('-');\n  const [ey,em,ed] = periodeEnd.split('-');\n  const startD = new Date(parseInt(sy), parseInt(sm)-1, parseInt(sd), 0,0,0,0);\n  const endD   = new Date(parseInt(ey), parseInt(em)-1, parseInt(ed), 23,59,59,999);\n  filterFn = (tglStr) => {\n    const d = parseTgl(tglStr);\n    if (!d) return false;\n    return d >= startD && d <= endD;\n  };\n} else {\n  const targetM = BULAN_MAP[bulan];\n  const targetY = parseInt(tahun);\n  filterFn = (tglStr) => {\n    const d = parseTgl(tglStr);\n    if (!d) return false;\n    return (d.getMonth() + 1) === targetM && d.getFullYear() === targetY;\n  };\n}\n\n// Label periode untuk header laporan\nconst reportPeriodeLabel = (mode === 'periode' && periodeLabel)\n  ? periodeLabel\n  : (bulan + ' ' + tahun);\n\n// \u2500\u2500 Pisahkan expenses vs income + filter \u2500\u2500\nconst expenses = [];\nconst incomes  = [];\n\nfor (const item of items) {\n  const d = item.json;\n  if (d.Jumlah !== undefined || d.Sumber !== undefined) {\n    // Income punya field Tanggal \u2192 filter sama seperti expenses\n    if (d.Tanggal) {\n      if (filterFn(d.Tanggal)) incomes.push(d);\n    } else if (d.Bulan) {\n      // Fallback: tidak ada Tanggal, pakai Bulan\n      const targetM = mode === 'periode'\n        ? parseInt(periodeEnd.split('-')[1])  // bulan dari periodeEnd\n        : BULAN_MAP[bulan];\n      if (BULAN_MAP[d.Bulan] === targetM) incomes.push(d);\n    } else {\n      // Tidak ada Tanggal maupun Bulan \u2192 loloskan\n      incomes.push(d);\n    }\n  } else {\n    if (d.Tanggal) {\n      if (filterFn(d.Tanggal)) expenses.push(d);\n    } else if (d.Nilai) {\n      expenses.push(d);\n    }\n  }\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\nconst toNum = (v) => parseInt(String(v || '0').replace(/\\D/g, '')) || 0;\nconst fmt   = (n) => 'Rp ' + Number(n).toLocaleString('id-ID');\n\n// \u2500\u2500 Agregasi expenses \u2500\u2500\nlet totalKeluar = 0;\nconst byKategori = {}, byBank = {}, txList = [];\n\nfor (const d of expenses) {\n  const nilai = toNum(d.Nilai);\n  totalKeluar += nilai;\n  const kat  = d.Kategori || 'Lainnya';\n  const bank = d.Bank     || '-';\n  byKategori[kat] = (byKategori[kat] || 0) + nilai;\n  byBank[bank]    = (byBank[bank]    || 0) + nilai;\n  txList.push({\n    tanggal: d.Tanggal || '-', toko: d.Toko || '-', uraian: d.Uraian || '-',\n    kategori: kat, nilai, bank, metode: d.Transaksi || '-', user: d.user || '-', jenis: 'Keluar'\n  });\n}\n\n// \u2500\u2500 Agregasi income \u2500\u2500\nlet totalMasuk = 0;\nfor (const d of incomes) {\n  const nilai = toNum(d.Jumlah);\n  totalMasuk += nilai;\n  const bank = d.Bank || '-';\n  byBank[bank] = (byBank[bank] || 0) + nilai;\n  txList.push({\n    tanggal: d.Tanggal || '-', toko: d.Sumber || '-', uraian: d.Items || '-',\n    kategori: d.Kategori || 'Pemasukan', nilai, bank, metode: d.Metode || '-', user: d.User || '-', jenis: 'Masuk'\n  });\n}\n\nconst saldo = totalMasuk - totalKeluar;\nconst totalTx = txList.length;\n\n// \u2500\u2500 HTML \u2500\u2500\nconst rowsKategori = Object.entries(byKategori)\n  .sort((a, b) => b[1] - a[1])\n  .map(([k, v]) => `\n    <tr>\n      <td>${k}</td>\n      <td style=\"text-align:right;font-weight:500\">${fmt(v)}</td>\n      <td style=\"text-align:right\">${totalKeluar > 0 ? ((v/totalKeluar)*100).toFixed(1) : '0.0'}%</td>\n    </tr>`).join('');\n\nconst rowsBank = Object.entries(byBank)\n  .sort((a, b) => b[1] - a[1])\n  .map(([k, v]) => `\n    <tr>\n      <td>${k}</td>\n      <td style=\"text-align:right;font-weight:500\">${fmt(v)}</td>\n    </tr>`).join('');\n\nconst rowsTx = txList\n  .sort((a, b) => {\n    const da = parseTgl(a.tanggal), db = parseTgl(b.tanggal);\n    return (db||0) - (da||0);\n  })\n  .map((t) => `\n    <tr>\n      <td style=\"white-space:nowrap\">${t.tanggal}</td>\n      <td>${t.toko}</td>\n      <td>${t.uraian}</td>\n      <td>${t.kategori}</td>\n      <td>${t.metode}</td>\n      <td>${t.bank}</td>\n      <td>${t.user}</td>\n      <td style=\"text-align:right;color:${t.jenis==='Masuk'?'#16a34a':'#dc2626'};font-weight:500\">${fmt(t.nilai)}</td>\n      <td><span style=\"background:${t.jenis==='Masuk'?'#dcfce7':'#fee2e2'};color:${t.jenis==='Masuk'?'#166534':'#991b1b'};padding:2px 8px;border-radius:12px;font-size:11px;font-weight:600\">${t.jenis}</span></td>\n    </tr>`).join('');\n\nconst reportTitle = mode === 'periode' ? 'Laporan Keuangan Per Periode Gaji' : 'Laporan Keuangan Bulanan';\n\nconst htmlBody = `<!DOCTYPE html>\n<html lang=\"id\">\n<head>\n<meta charset=\"UTF-8\">\n<style>\n  * { margin: 0; padding: 0; box-sizing: border-box; }\n  body { font-family: 'Arial', sans-serif; font-size: 11px; color: #1a1a2e; background: #fff; }\n  .header { padding: 32px 48px 24px; border-bottom: 3px solid #1a1a2e; display: flex; justify-content: space-between; align-items: flex-start; }\n  .company-name { font-size: 22px; font-weight: 700; color: #1a1a2e; letter-spacing: 1px; }\n  .company-sub { font-size: 10px; color: #6b7280; margin-top: 3px; letter-spacing: 2px; text-transform: uppercase; }\n  .report-title-box { text-align: right; }\n  .report-title { font-size: 16px; font-weight: 700; color: #1a1a2e; text-transform: uppercase; letter-spacing: 1px; }\n  .report-period { font-size: 10px; color: #6b7280; margin-top: 4px; }\n  .report-date { font-size: 9px; color: #9ca3af; margin-top: 2px; }\n  .meta-bar { background: #1a1a2e; color: #fff; padding: 10px 48px; display: flex; gap: 48px; }\n  .meta-item { font-size: 9px; text-transform: uppercase; letter-spacing: 1px; }\n  .meta-item span { display: block; font-size: 11px; font-weight: 700; margin-top: 2px; color: #e5e7eb; }\n  .section { padding: 24px 48px 0; }\n  .section-title { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 2px; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 6px; margin-bottom: 14px; }\n  .summary-grid { display: flex; gap: 12px; }\n  .summary-card { flex: 1; border: 1px solid #e5e7eb; border-radius: 4px; padding: 14px 16px; }\n  .summary-card.income  { border-left: 4px solid #16a34a; }\n  .summary-card.expense { border-left: 4px solid #dc2626; }\n  .summary-card.balance { border-left: 4px solid #1a1a2e; }\n  .card-label { font-size: 8px; text-transform: uppercase; letter-spacing: 1.5px; color: #9ca3af; margin-bottom: 6px; }\n  .card-value { font-size: 16px; font-weight: 700; color: #1a1a2e; }\n  .card-value.income  { color: #16a34a; }\n  .card-value.expense { color: #dc2626; }\n  .card-count { font-size: 9px; color: #9ca3af; margin-top: 4px; }\n  table { width: 100%; border-collapse: collapse; font-size: 10px; }\n  thead tr { background: #1a1a2e; }\n  thead th { padding: 8px 10px; text-align: left; color: #fff; font-size: 8px; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; }\n  .two-col { display: flex; gap: 24px; }\n  .two-col .col { flex: 1; }\n  .divider { height: 1px; background: #e5e7eb; margin: 24px 48px 0; }\n  .footer { padding: 16px 48px; margin-top: 24px; border-top: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; }\n  .footer-left { font-size: 9px; color: #9ca3af; }\n  .footer-right { font-size: 9px; color: #9ca3af; text-align: right; }\n  .footer-brand { font-size: 10px; font-weight: 700; color: #1a1a2e; }\n  .ttd-section { padding: 24px 48px 0; display: flex; justify-content: flex-end; }\n  .ttd-box { text-align: center; width: 180px; }\n  .ttd-line { border-bottom: 1px solid #1a1a2e; margin: 48px 0 6px; }\n  .ttd-name { font-size: 10px; font-weight: 700; color: #1a1a2e; }\n  .ttd-label { font-size: 9px; color: #374151; }\n  .badge-mode { display:inline-block;background:${mode==='periode'?'#dbeafe':'#f3f4f6'};color:${mode==='periode'?'#1d4ed8':'#374151'};padding:2px 8px;border-radius:4px;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:1px; }\n</style>\n</head>\n<body>\n  <div class=\"header\">\n    <div>\n      <div class=\"company-name\">ArviFund</div>\n      <div class=\"company-sub\">Manajemen Keuangan Keluarga</div>\n    </div>\n    <div class=\"report-title-box\">\n      <div class=\"report-title\">${reportTitle}</div>\n      <div class=\"report-period\">Periode: ${reportPeriodeLabel} &nbsp;<span class=\"badge-mode\">${mode === 'periode' ? 'Gaji' : 'Kalender'}</span></div>\n      <div class=\"report-date\">Diterbitkan: ${new Date().toLocaleDateString('id-ID', { day:'2-digit', month:'long', year:'numeric', timeZone:'Asia/Jakarta' })}</div>\n    </div>\n  </div>\n\n  <div class=\"meta-bar\">\n    <div class=\"meta-item\">Total Transaksi <span>${totalTx} Transaksi</span></div>\n    <div class=\"meta-item\">Pemasukan <span>${fmt(totalMasuk)}</span></div>\n    <div class=\"meta-item\">Pengeluaran <span>${fmt(totalKeluar)}</span></div>\n    <div class=\"meta-item\">Saldo Akhir <span>${fmt(saldo)}</span></div>\n  </div>\n\n  <div class=\"section\" style=\"margin-top:24px\">\n    <div class=\"section-title\">Ringkasan Eksekutif</div>\n    <div class=\"summary-grid\">\n      <div class=\"summary-card income\">\n        <div class=\"card-label\">Total Pemasukan</div>\n        <div class=\"card-value income\">${fmt(totalMasuk)}</div>\n        <div class=\"card-count\">${incomes.length} transaksi pemasukan</div>\n      </div>\n      <div class=\"summary-card expense\">\n        <div class=\"card-label\">Total Pengeluaran</div>\n        <div class=\"card-value expense\">${fmt(totalKeluar)}</div>\n        <div class=\"card-count\">${expenses.length} transaksi pengeluaran</div>\n      </div>\n      <div class=\"summary-card balance\">\n        <div class=\"card-label\">Saldo Akhir Periode</div>\n        <div class=\"card-value\">${fmt(saldo)}</div>\n        <div class=\"card-count\">${saldo >= 0 ? 'Surplus' : 'Defisit'} periode ini</div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"divider\"></div>\n\n  <div class=\"section\" style=\"margin-top:24px\">\n    <div class=\"two-col\">\n      <div class=\"col\">\n        <div class=\"section-title\">Pengeluaran per Kategori</div>\n        <table>\n          <thead><tr><th>Kategori</th><th>Jumlah</th><th>%</th></tr></thead>\n          <tbody>${rowsKategori || '<tr><td colspan=\"3\" style=\"color:#9ca3af;padding:12px 10px\">Tidak ada data</td></tr>'}</tbody>\n        </table>\n      </div>\n      <div class=\"col\">\n        <div class=\"section-title\">Rekap Bank &amp; Metode Bayar</div>\n        <table>\n          <thead><tr><th>Bank / Metode</th><th>Total</th></tr></thead>\n          <tbody>${rowsBank || '<tr><td colspan=\"2\" style=\"color:#9ca3af;padding:12px 10px\">Tidak ada data</td></tr>'}</tbody>\n        </table>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"divider\"></div>\n\n  <div class=\"section\" style=\"margin-top:24px\">\n    <div class=\"section-title\">Detail Transaksi \u2014 Semua</div>\n    <table>\n      <thead>\n        <tr>\n          <th>Tanggal</th><th>Toko / Sumber</th><th>Uraian</th><th>Kategori</th>\n          <th>Metode</th><th>Bank</th><th>User</th><th>Nilai</th><th>Jenis</th>\n        </tr>\n      </thead>\n      <tbody>${rowsTx || '<tr><td colspan=\"9\" style=\"color:#9ca3af;padding:16px 10px;text-align:center\">Tidak ada transaksi pada periode ini</td></tr>'}</tbody>\n    </table>\n  </div>\n\n  <div class=\"ttd-section\">\n    <div class=\"ttd-box\">\n      <div class=\"ttd-line\"></div>\n      <div class=\"ttd-name\">Arvidzan Rezqiano Darmawan</div>\n      <div class=\"ttd-label\">Dibuat oleh ArviFund Bot</div>\n    </div>\n  </div>\n\n  <div class=\"footer\">\n    <div class=\"footer-left\">\n      <div class=\"footer-brand\">ArviFund</div>\n      Laporan ini dibuat secara otomatis oleh sistem ArviFund\n    </div>\n    <div class=\"footer-right\">Halaman 1 dari 1<br>Dokumen ini bersifat rahasia</div>\n  </div>\n</body>\n</html>`;\n\nreturn [{\n  json: {\n    htmlBody, bulan, tahun, chatId, firstName,\n    mode, periodeStart, periodeEnd, periodeLabel, reportPeriodeLabel,\n    totalMasuk, totalKeluar, saldo, totalTx,\n    fmtMasuk: fmt(totalMasuk), fmtKeluar: fmt(totalKeluar), fmtSaldo: fmt(saldo)\n  }\n}];"
        },
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          128,
          1952
        ],
        "id": "032dcc3c-a752-4a30-9ae4-cd889ea04180",
        "name": "Agregasi dan Render HTML"
      },
      {
        "parameters": {
          "numberInputs": 3
        },
        "type": "n8n-nodes-base.merge",
        "typeVersion": 3,
        "position": [
          1152,
          1504
        ],
        "id": "bf1f00bf-7d5a-4a5d-9773-ad705011c408",
        "name": "Merge Data"
      },
      {
        "parameters": {
          "operation": "getAll",
          "tableId": "income",
          "returnAll": true
        },
        "type": "n8n-nodes-base.supabase",
        "typeVersion": 1,
        "position": [
          944,
          1616
        ],
        "id": "279baff7-bc06-4c12-acfa-9c24a55ec876",
        "name": "Read Income"
      },
      {
        "parameters": {
          "operation": "getAll",
          "tableId": "expenses",
          "returnAll": true
        },
        "type": "n8n-nodes-base.supabase",
        "typeVersion": 1,
        "position": [
          944,
          1424
        ],
        "id": "9350b23f-cc44-4090-aa79-505ee945d49c",
        "name": "Read Expenses"
      },
      {
        "parameters": {
          "chatId": "={{ $json.chatId }}",
          "text": "=\u23f3 Oke *{{ $json.Baba }}*, Pijan lagi siapkan laporan bulan *{{ $json.targetBulan }} {{ $json.targetTahun }}* ya...\n\nTunggu sebentar! \ud83d\udcca",
          "additionalFields": {
            "appendAttribution": false,
            "parse_mode": "Markdown"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          752,
          1504
        ],
        "id": "1c2e235f-48f2-452f-bc56-4eb1c94eff53",
        "name": "Telegram Processing",
        "webhookId": "laporan-bulanan-webhook-002"
      },
      {
        "parameters": {
          "jsCode": "// ============================================================\n// NODE: Parse Command (Telegram)\n// Deteksi nama bulan dari command user\n// Contoh: /laporanbulanan       \u2192 bulan lalu\n//         /laporanbulanapril    \u2192 April\n//         /laporanperiode       \u2192 periode gaji terakhir (pakai tanggal gajian default 25)\n// ============================================================\n\nconst text = $input.first().json.message.text.toLowerCase().trim();\n\nconst mapBulan = {\n  'januari':   { index: 0,  nama: 'Januari' },\n  'februari':  { index: 1,  nama: 'Februari' },\n  'maret':     { index: 2,  nama: 'Maret' },\n  'april':     { index: 3,  nama: 'April' },\n  'mei':       { index: 4,  nama: 'Mei' },\n  'juni':      { index: 5,  nama: 'Juni' },\n  'juli':      { index: 6,  nama: 'Juli' },\n  'agustus':   { index: 7,  nama: 'Agustus' },\n  'september': { index: 8,  nama: 'September' },\n  'oktober':   { index: 9,  nama: 'Oktober' },\n  'november':  { index: 10, nama: 'November' },\n  'desember':  { index: 11, nama: 'Desember' }\n};\n\nconst BULAN_LIST = ['Januari','Februari','Maret','April','Mei','Juni',\n                    'Juli','Agustus','September','Oktober','November','Desember'];\n\nconst now = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\n\nlet targetBulan = null, targetBulanIndex = null, targetTahun = now.getFullYear();\nlet mode = 'bulan', periodeStart = '', periodeEnd = '', periodeLabel = '';\n\n// Cek apakah command minta laporan per periode\nif (text.includes('periode') || text.includes('period')) {\n  mode = 'periode';\n  // Hitung periode gajian terakhir (default tgl 25)\n  const payDate = 25;\n  const today   = now;\n  let pStart, pEnd;\n  if (today.getDate() >= payDate) {\n    pStart = new Date(today.getFullYear(), today.getMonth(), payDate);\n    pEnd   = new Date(today.getFullYear(), today.getMonth() + 1, payDate - 1);\n  } else {\n    pStart = new Date(today.getFullYear(), today.getMonth() - 1, payDate);\n    pEnd   = new Date(today.getFullYear(), today.getMonth(), payDate - 1);\n  }\n  const fmt = d => d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');\n  periodeStart  = fmt(pStart);\n  periodeEnd    = fmt(pEnd);\n  targetBulanIndex = pEnd.getMonth();\n  targetTahun      = pEnd.getFullYear();\n  targetBulan      = BULAN_LIST[targetBulanIndex];\n  periodeLabel = periodeStart + ' s/d ' + periodeEnd;\n} else {\n  // Cari nama bulan di dalam teks\n  for (const [key, val] of Object.entries(mapBulan)) {\n    if (text.includes(key)) {\n      targetBulan = val.nama;\n      targetBulanIndex = val.index;\n      break;\n    }\n  }\n  // Default: bulan lalu\n  if (!targetBulan) {\n    const bulanLalu  = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n    targetBulanIndex = bulanLalu.getMonth();\n    targetTahun      = bulanLalu.getFullYear();\n    targetBulan      = BULAN_LIST[targetBulanIndex];\n  }\n}\n\nreturn [{\n  json: {\n    mode,\n    targetBulan,\n    targetBulanIndex,\n    targetTahun,\n    periodeStart,\n    periodeEnd,\n    periodeLabel,\n    chatId   : $input.first().json.message.chat.id,\n    userId   : $input.first().json.message.from.id,\n    firstName: $input.first().json.message.from.first_name\n  }\n}];"
        },
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          16,
          1488
        ],
        "id": "55607886-9acb-40b5-a74e-0b752439c846",
        "name": "Parse Command"
      },
      {
        "parameters": {
          "sendTo": "={{ $('Edit Fields (Webhook)1').item.json.firstName === 'Aldin' ? 'aldindarmawan72@gmail.com' : $('Edit Fields (Webhook)1').item.json.firstName === 'Solikhatun' ? 'solikhatun0800@gmail.com' : '' }}",
          "subject": "=Laporan bulan {{ $('Cek PDF Berhasil').item.json.bulan }} {{ $('Cek PDF Berhasil').item.json.tahun }}",
          "message": "berikut hasil laporan bulanan",
          "options": {
            "appendAttribution": false,
            "attachmentsUi": {
              "attachmentsBinary": [
                {
                  "property": "=pdfData"
                }
              ]
            }
          }
        },
        "type": "n8n-nodes-base.gmail",
        "typeVersion": 2.2,
        "position": [
          928,
          1888
        ],
        "id": "fae55fa0-cc75-43c6-b4ac-a0cfa4b69829",
        "name": "Send a message",
        "webhookId": "156ab808-0a59-4169-82b9-fbe708a755f1"
      },
      {
        "parameters": {
          "assignments": {
            "assignments": [
              {
                "id": "8a46796a-9a19-4d6f-9e73-3e713f929969",
                "name": "Baba",
                "value": "={{ $json.firstName === 'Aldin' ? 'Baba' : 'Ibu' }}",
                "type": "string"
              },
              {
                "id": "tg-ps-001",
                "name": "periodeStart",
                "value": "={{ $json.periodeStart }}",
                "type": "string"
              },
              {
                "id": "tg-ps-002",
                "name": "periodeEnd",
                "value": "={{ $json.periodeEnd }}",
                "type": "string"
              },
              {
                "id": "tg-ps-003",
                "name": "periodeLabel",
                "value": "={{ $json.periodeLabel }}",
                "type": "string"
              },
              {
                "id": "tg-ps-004",
                "name": "mode",
                "value": "={{ $json.mode }}",
                "type": "string"
              },
              {
                "id": "tg-ps-005",
                "name": "tahun",
                "value": "={{ $json.targetTahun }}",
                "type": "string"
              },
              {
                "id": "tg-ps-006",
                "name": "bulan",
                "value": "={{ $json.targetBulan }}",
                "type": "string"
              },
              {
                "id": "tg-ps-007",
                "name": "chatId",
                "value": "={{ $json.chatId }}",
                "type": "string"
              },
              {
                "id": "tg-ps-008",
                "name": "firstName",
                "value": "={{ $json.firstName }}",
                "type": "string"
              }
            ]
          },
          "includeOtherFields": true,
          "options": {}
        },
        "type": "n8n-nodes-base.set",
        "typeVersion": 3.4,
        "position": [
          272,
          1488
        ],
        "id": "dd54bcda-448a-4d41-b8aa-904164f1dde6",
        "name": "Edit Fields2"
      },
      {
        "parameters": {
          "jsCode": "const item = $input.first();\nconst bulan =items[0].json.bulan;\nconst tahun = items[0].json.tahun;\n\nitem.binary.pdfData.fileName = `Laporan ${bulan} ${tahun}.pdf`;\n\nreturn [item];"
        },
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          768,
          1888
        ],
        "id": "5d987b03-848e-44a6-88ba-f40bf8aa73bd",
        "name": "Ubah nama dokumen"
      },
      {
        "parameters": {
          "content": "## Request laporan bulanan pdf",
          "height": 652,
          "width": 1448,
          "color": 6
        },
        "type": "n8n-nodes-base.stickyNote",
        "position": [
          32,
          1488
        ],
        "typeVersion": 1,
        "id": "edca7dd4-3a3c-4a37-b941-62c8fafba5ae",
        "name": "Sticky Note6"
      },
      {
        "parameters": {
          "jsCode": "// ============================================================\n// NODE: Parse Command (Webhook version) v2\n// Mendukung dua mode:\n//   Mode PERIODE: { mode:'periode', periodeStart:'2026-04-25', periodeEnd:'2026-05-24', periodeLabel:'...', chatId, firstName }\n//   Mode BULAN  : { mode:'bulan',   bulan:'April', tahun:'2026', chatId, firstName }\n// ============================================================\n\nconst body = $input.first().json.body;\nconst now  = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\n\nconst BULAN_LIST = ['Januari','Februari','Maret','April','Mei','Juni',\n                    'Juli','Agustus','September','Oktober','November','Desember'];\nconst mapBulan = {};\nBULAN_LIST.forEach((b,i) => mapBulan[b] = i);\n\nconst mode = body.mode || 'bulan';\n\nlet targetBulan, targetBulanIndex, targetTahun;\nlet periodeStart = '', periodeEnd = '', periodeLabel = '';\n\nif (mode === 'periode' && body.periodeStart && body.periodeEnd) {\n  // Mode periode: filter pakai date range\n  periodeStart = body.periodeStart;  // YYYY-MM-DD\n  periodeEnd   = body.periodeEnd;    // YYYY-MM-DD\n  periodeLabel = body.periodeLabel || (periodeStart + ' s/d ' + periodeEnd);\n  // Untuk keperluan label HTML, ambil bulan dari periodeEnd\n  const endDate = new Date(periodeEnd);\n  targetBulanIndex = endDate.getMonth();\n  targetTahun      = endDate.getFullYear();\n  targetBulan      = BULAN_LIST[targetBulanIndex];\n} else {\n  // Mode bulan kalender (backward compat)\n  targetBulan = body.bulan || null;\n  targetTahun = parseInt(body.tahun) || now.getFullYear();\n  targetBulanIndex = mapBulan[targetBulan] ?? null;\n  if (!targetBulan || targetBulanIndex === null) {\n    const bulanLalu = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n    targetBulanIndex = bulanLalu.getMonth();\n    targetTahun      = bulanLalu.getFullYear();\n    targetBulan      = BULAN_LIST[targetBulanIndex];\n  }\n}\n\nconst chatId    = body.chatId    || '';\nconst firstName = body.firstName || 'AppScript';\n\nreturn [{\n  json: {\n    mode,\n    targetBulan,\n    targetBulanIndex,\n    targetTahun,\n    periodeStart,\n    periodeEnd,\n    periodeLabel,\n    chatId,\n    userId   : null,\n    firstName\n  }\n}];"
        },
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          -96,
          1696
        ],
        "id": "44bdb320-7a73-45bb-933a-3e05191e0bcb",
        "name": "Parse Command (Webhook)"
      },
      {
        "parameters": {
          "assignments": {
            "assignments": [
              {
                "id": "baba-field-webhook-001",
                "name": "Baba",
                "value": "={{ $json.firstName === 'Aldin' ? 'Baba' : 'Ibu' }}",
                "type": "string"
              }
            ]
          },
          "includeOtherFields": true,
          "options": {}
        },
        "type": "n8n-nodes-base.set",
        "typeVersion": 3.4,
        "position": [
          128,
          1696
        ],
        "id": "14093c39-35c9-48d3-9e96-57452caaa2b0",
        "name": "Edit Fields (Webhook)"
      },
      {
        "parameters": {},
        "type": "n8n-nodes-base.merge",
        "typeVersion": 3,
        "position": [
          464,
          1504
        ],
        "id": "f9dfb57a-a9af-40f5-bbc1-b89248f35ba8",
        "name": "Merge Data1"
      },
      {
        "parameters": {
          "assignments": {
            "assignments": [
              {
                "id": "baba-field-webhook-001",
                "name": "bulan",
                "value": "={{ $json.targetBulan }}",
                "type": "string"
              },
              {
                "id": "193e1aef-7c60-40aa-be4b-ad0f0d07139d",
                "name": "tahun",
                "value": "={{ $json.targetTahun }}",
                "type": "string"
              },
              {
                "id": "b213b318-4a2f-4531-91a8-d8cfe925e370",
                "name": "firstName",
                "value": "={{ $json.firstName }}",
                "type": "string"
              },
              {
                "id": "3d3f8249-10fe-4fe8-92ff-501f08864a47",
                "name": "chatId",
                "value": "={{ $json.chatId }}",
                "type": "string"
              },
              {
                "id": "ps-001",
                "name": "periodeStart",
                "value": "={{ $json.periodeStart }}",
                "type": "string"
              },
              {
                "id": "ps-002",
                "name": "periodeEnd",
                "value": "={{ $json.periodeEnd }}",
                "type": "string"
              },
              {
                "id": "ps-003",
                "name": "periodeLabel",
                "value": "={{ $json.periodeLabel }}",
                "type": "string"
              },
              {
                "id": "ps-004",
                "name": "mode",
                "value": "={{ $json.mode }}",
                "type": "string"
              }
            ]
          },
          "includeOtherFields": true,
          "options": {}
        },
        "type": "n8n-nodes-base.set",
        "typeVersion": 3.4,
        "position": [
          608,
          1504
        ],
        "id": "6867eac2-fffa-47c4-b57c-0d6027bc1dd7",
        "name": "Edit Fields (Webhook)1"
      },
      {
        "parameters": {
          "httpMethod": "POST",
          "path": "61be88dd-4c6c-4359-a9ad-1a86bf0cb380",
          "options": {}
        },
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2.1,
        "position": [
          -912,
          1296
        ],
        "id": "16276234-39f3-4780-981d-69580c398db0",
        "name": "Webhook",
        "webhookId": "61be88dd-4c6c-4359-a9ad-1a86bf0cb380"
      },
      {
        "parameters": {
          "amount": 2
        },
        "type": "n8n-nodes-base.wait",
        "typeVersion": 1.1,
        "position": [
          -704,
          1120
        ],
        "id": "b4fea777-792e-4bec-88b2-cd8d76ab1d46",
        "name": "Wait",
        "webhookId": "2bb6d331-452c-4a05-bf0a-d7519576d999"
      },
      {
        "parameters": {
          "amount": 2
        },
        "type": "n8n-nodes-base.wait",
        "typeVersion": 1.1,
        "position": [
          -624,
          1408
        ],
        "id": "9800a8ff-5d4f-4e0f-a111-0ba29fdd8033",
        "name": "Wait1",
        "webhookId": "2bb6d331-452c-4a05-bf0a-d7519576d999"
      },
      {
        "parameters": {
          "url": "=https://api.telegram.org/bot7627053300:AAHV2mR4MH3GISWzJnPIO7U-2Cilen9xf-E/getFile",
          "sendQuery": true,
          "queryParameters": {
            "parameters": [
              {
                "name": "file_id",
                "value": "={{ $('Telegram Trigger').item.json.message.photo[2].file_id }}"
              }
            ]
          },
          "options": {}
        },
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          -112,
          384
        ],
        "id": "5a301477-cbec-43ba-b6bb-ba47ff124a20",
        "name": "Get File Path"
      },
      {
        "parameters": {
          "url": "=https://api.telegram.org/file/bot7627053300:AAHV2mR4MH3GISWzJnPIO7U-2Cilen9xf-E/{{ $json.result.file_path }}",
          "options": {
            "response": {
              "response": {
                "responseFormat": "file"
              }
            }
          }
        },
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          32,
          384
        ],
        "id": "486c376f-07e9-488c-9323-49526b435be5",
        "name": "Download File"
      },
      {
        "parameters": {
          "jsCode": "const binaryData = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst base64 = binaryData.toString('base64');\n\nreturn [{\n  json: {\n    ...items[0].json,\n    base64Image: base64\n  }\n}];"
        },
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          160,
          384
        ],
        "id": "33727a9d-1e3d-4e67-876b-f0586da18a5b",
        "name": "Code in JavaScript"
      },
      {
        "parameters": {
          "amount": 2
        },
        "type": "n8n-nodes-base.wait",
        "typeVersion": 1.1,
        "position": [
          -656,
          1600
        ],
        "id": "fdfef4ec-507e-43ac-8b67-b4f7a453ccfc",
        "name": "Wait2",
        "webhookId": "2bb6d331-452c-4a05-bf0a-d7519576d999"
      },
      {
        "parameters": {
          "httpMethod": "POST",
          "path": "62994864-7294-44cf-a294-2e1f7357d839",
          "responseMode": "responseNode",
          "options": {}
        },
        "type": "n8n-nodes-base.webhook",
        "typeVersion": 2.1,
        "position": [
          -912,
          1536
        ],
        "id": "82933ad1-e733-47bd-b773-dcf0a979488b",
        "name": "Webhook1",
        "webhookId": "62994864-7294-44cf-a294-2e1f7357d839"
      },
      {
        "parameters": {
          "operation": "getAll",
          "tableId": "profiles",
          "returnAll": true
        },
        "type": "n8n-nodes-base.supabase",
        "typeVersion": 1,
        "position": [
          192,
          3200
        ],
        "id": "b451b3db-d7b1-4111-a5be-90c2a61266c9",
        "name": "Get Config Sheet"
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict",
              "version": 1
            },
            "conditions": [
              {
                "id": "cond-username",
                "leftValue": "={{ $json.username }}",
                "rightValue": "={{ $('Webhook1').item.json.body.username }}",
                "operator": {
                  "type": "string",
                  "operation": "equals",
                  "singleValue": true
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "c2f485ac-7a1d-4b26-be9b-905d723f1d69",
        "name": "Filter by Username",
        "type": "n8n-nodes-base.filter",
        "typeVersion": 2,
        "position": [
          416,
          3200
        ]
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict",
              "version": 1
            },
            "conditions": [
              {
                "id": "cond-found",
                "leftValue": "={{ $json.username }}",
                "rightValue": "",
                "operator": {
                  "type": "string",
                  "operation": "notEmpty",
                  "singleValue": true
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "5f7a7363-aa46-46bb-a6c8-898513a49ff4",
        "name": "User Ditemukan?",
        "type": "n8n-nodes-base.if",
        "typeVersion": 2,
        "position": [
          640,
          3200
        ]
      },
      {
        "parameters": {
          "method": "POST",
          "url": "https://kjrmubvtdphuarrknijl.supabase.co/functions/v1/reset-password",
          "sendHeaders": true,
          "headerParameters": {
            "parameters": [
              {
                "name": "Authorization",
                "value": "<redacted-credential>"
              },
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          },
          "sendBody": true,
          "specifyBody": "json",
          "jsonBody": "{ \"username\": \"={{ $json.username }}\" }",
          "options": {}
        },
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [
          848,
          3088
        ],
        "id": "74370863-fe31-4cd8-acea-1e8cd7653953",
        "name": "Kirim Reset Password"
      },
      {
        "parameters": {
          "chatId": "={{ $json.chat_id }}",
          "text": "=\ud83d\udd10 *Reset Password Arvifund*\n\nHalo {{ $json.username }},\nLink reset password telah dikirim ke email kamu.\n\n_Cek inbox atau folder spam ya!_ \ud83d\udce7",
          "additionalFields": {
            "parse_mode": "Markdown"
          }
        },
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1.2,
        "position": [
          1088,
          3088
        ],
        "id": "f8db4d96-d4d1-4292-a15e-fcc649b3a9df",
        "name": "Kirim via Telegram",
        "webhookId": "9d9a9ab4-1f71-485f-b146-c331bfc22db3"
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ JSON.stringify({ ok: true, msg: 'Password telah dikirim ke Telegram kamu.' }) }}",
          "options": {
            "responseHeaders": {
              "entries": [
                {
                  "name": "Access-Control-Allow-Origin",
                  "value": "*"
                },
                {
                  "name": "Content-Type",
                  "value": "application/json"
                }
              ]
            }
          }
        },
        "id": "6fa7021c-e400-461f-8ba1-d16e7cd21bc5",
        "name": "Response OK",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1,
        "position": [
          1328,
          3024
        ]
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ JSON.stringify({ ok: false, msg: 'Username \"' + $('Webhook2').item.json.body.username + '\" tidak ditemukan.' }) }}",
          "options": {
            "responseHeaders": {
              "entries": [
                {
                  "name": "Access-Control-Allow-Origin",
                  "value": "*"
                },
                {
                  "name": "Content-Type",
                  "value": "application/json"
                }
              ]
            }
          }
        },
        "id": "a1e2a791-4127-448a-8eb7-30523a43c2f0",
        "name": "Response Not Found",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1,
        "position": [
          848,
          3312
        ]
      },
      {
        "parameters": {
          "respondWith": "json",
          "responseBody": "={{ JSON.stringify({ ok: false, msg: 'Terjadi kesalahan saat mengirim Telegram.' }) }}",
          "options": {
            "responseHeaders": {
              "entries": [
                {
                  "name": "Access-Control-Allow-Origin",
                  "value": "*"
                },
                {
                  "name": "Content-Type",
                  "value": "application/json"
                }
              ]
            }
          }
        },
        "id": "cee018dc-c9d8-4c00-a5a1-6131eacbac6d",
        "name": "Response Error",
        "type": "n8n-nodes-base.respondToWebhook",
        "typeVersion": 1,
        "position": [
          1072,
          3312
        ]
      },
      {
        "parameters": {
          "jsCode": "// Build laporan hari ini\nconst now = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\nconst pad = n => String(n).padStart(2, '0');\nconst todayISO = now.getFullYear() + '-' + pad(now.getMonth()+1) + '-' + pad(now.getDate());\nconst todayLabel = pad(now.getDate()) + '/' + pad(now.getMonth()+1) + '/' + now.getFullYear();\n\nconst BULAN = ['Januari','Februari','Maret','April','Mei','Juni',\n               'Juli','Agustus','September','Oktober','November','Desember'];\nconst bulan = BULAN[now.getMonth()];\nconst tahun = String(now.getFullYear());\n\nconst chatId    = $input.first().json.message.chat.id;\nconst firstName = $input.first().json.message.from.first_name || 'User';\n\nconst GAS_URL = 'https://script.google.com/macros/s/AKfycbxTgawdxMBhnJd_gy1Obl1yiAUId0NYWM55L50N-Wx4eeR4KbnRhZm6rIK8UUiwPLQE/exec';\nconst url = `${GAS_URL}?action=laporan&periodeStart=${todayISO}&periodeEnd=${todayISO}`;\n\nreturn [{\n  json: {\n    url,\n    chatId,\n    firstName,\n    mode        : 'hari',\n    periodeStart: todayISO,\n    periodeEnd  : todayISO,\n    periodeLabel: 'Hari ini, ' + todayLabel,\n    bulan,\n    tahun\n  }\n}];"
        },
        "id": "96e8c5ba-aae8-42b3-a365-9981f4ac5830",
        "name": "Build Report Hari Ini",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          784,
          1200
        ]
      },
      {
        "parameters": {
          "url": "={{ $json.url }}",
          "options": {}
        },
        "id": "50e28d0b-c7ea-45f9-bc41-4809c9ba5efe",
        "name": "HTTP Request Hari Ini",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4,
        "position": [
          992,
          1200
        ]
      },
      {
        "parameters": {
          "jsCode": "// Format laporan hari ini dari data GAS\nconst data     = $input.first().json;\nconst prevNode = $('Build Report Hari Ini').first().json;\n\nconst chatId    = prevNode.chatId;\nconst firstName = prevNode.firstName;\nconst label     = prevNode.periodeLabel;\n\nconst fmt = n => 'Rp ' + Number(n || 0).toLocaleString('id-ID');\n\n// Data dari GAS endpoint\nconst totalPemasukan   = data.totalPemasukan   || 0;\nconst totalPengeluaran = data.totalPengeluaran || 0;\nconst saldo            = data.saldo            || (totalPemasukan - totalPengeluaran);\nconst byKategori       = data.byKategori       || {};\nconst byBank           = data.byBank           || {};\nconst txTerakhir       = data.transaksiTerakhir || [];\n\n// Baris per kategori\nconst katRows = Object.entries(byKategori)\n  .sort((a,b) => b[1]-a[1])\n  .map(([k,v]) => `  \u2022 ${k}: ${fmt(v)}`)\n  .join('\\n') || '  (tidak ada)';\n\n// Baris per bank\nconst bankRows = Object.entries(byBank)\n  .sort((a,b) => b[1]-a[1])\n  .map(([k,v]) => `  \u2022 ${k}: ${fmt(v)}`)\n  .join('\\n') || '  (tidak ada)';\n\n// 5 transaksi terakhir\nconst txRows = txTerakhir.slice(0,5)\n  .map(t => `  ${t.tanggal} | ${t.toko} | ${fmt(t.nilai)}`)\n  .join('\\n') || '  (tidak ada transaksi hari ini)';\n\nconst msg = `\ud83d\udcca *Laporan Hari Ini*\n*${label}*\n\n\ud83d\udcb0 *Ringkasan*\n\u250c Pemasukan   : ${fmt(totalPemasukan)}\n\u251c Pengeluaran : ${fmt(totalPengeluaran)}\n\u2514 Saldo       : ${saldo >= 0 ? '\u2705' : '\ud83d\udd34'} ${fmt(saldo)}\n\n\ud83d\udcc2 *Per Kategori*\n${katRows}\n\n\ud83c\udfe6 *Per Bank*\n${bankRows}\n\n\ud83e\uddfe *Transaksi Terakhir*\n${txRows}\n\n_ArviFund \u2022 ${label}_`;\n\nreturn [{\n  json: {\n    chatId,\n    firstName,\n    message: msg\n  }\n}];"
        },
        "id": "fca689e4-0571-4f0a-bfd8-8277b4cd05b3",
        "name": "Format Laporan Hari Ini",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          1184,
          1200
        ]
      },
      {
        "parameters": {
          "chatId": "={{ $json.chatId }}",
          "text": "={{ $json.message }}",
          "additionalFields": {
            "parse_mode": "Markdown"
          }
        },
        "id": "505b18d9-08fb-4d1a-8939-d549fccbb273",
        "name": "Kirim Laporan Hari Ini",
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1,
        "position": [
          1392,
          1200
        ],
        "webhookId": "f34a1533-6c9d-4b51-8051-f1653c781a88"
      },
      {
        "parameters": {
          "rule": {
            "interval": [
              {
                "field": "cronExpression",
                "expression": "0 16 * * *"
              }
            ]
          }
        },
        "id": "404c5451-9c48-4e2e-bc5d-59a1cc4e05e4",
        "name": "Cron Backup Periode",
        "type": "n8n-nodes-base.scheduleTrigger",
        "typeVersion": 1,
        "position": [
          -704,
          880
        ]
      },
      {
        "parameters": {
          "jsCode": "// Cek apakah hari ini adalah hari terakhir periode gaji\n// Hari terakhir periode = (tanggal gajian - 1)\n// Contoh: gajian tgl 25 \u2192 hari terakhir = tgl 24\n\nconst now = new Date(new Date().toLocaleString('en-US', { timeZone: 'Asia/Jakarta' }));\nconst pad = n => String(n).padStart(2, '0');\nconst todayDate = now.getDate();\nconst todayMonth = now.getMonth();\nconst todayYear = now.getFullYear();\n\nconst BULAN = ['Januari','Februari','Maret','April','Mei','Juni',\n               'Juli','Agustus','September','Oktober','November','Desember'];\nconst BULAN_SHORT = ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Agu','Sep','Okt','Nov','Des'];\n\n// Ambil override dari environment variable atau default 25\n// Override bisa ditambahkan sebagai variable n8n jika perlu\nconst payDate = parseInt($vars?.PAY_DATE || '25') || 25;\nconst endDate = payDate - 1; // Hari terakhir periode = tanggal gajian - 1\n\nconst isEndOfPeriod = todayDate === endDate;\n\n// Hitung range periode yang baru berakhir\n// Periode: dari tgl payDate bulan lalu s/d tgl (payDate-1) bulan ini\nconst periodEnd   = new Date(todayYear, todayMonth, endDate);\nconst periodStart = new Date(todayYear, todayMonth - 1, payDate);\n\nconst fmtISO = d => d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate());\nconst fmtLabel = d => d.getDate() + ' ' + BULAN_SHORT[d.getMonth()] + ' ' + d.getFullYear();\n\n// Label periode untuk nama file dan folder\n// Format: \"25 Apr - 24 Mei 2026\"\nconst periodeLabel = fmtLabel(periodStart) + ' - ' + fmtLabel(periodEnd);\nconst periodeYear  = String(todayYear);\n// Nama folder bulan: \"Mei 2026\"\nconst folderBulan  = BULAN[todayMonth] + ' ' + todayYear;\n// Nama file: \"ArviFund_25Apr-24Mei2026.xlsx\"\nconst safeLabel = periodeLabel.replace(/\\s+/g, '').replace(/-/g, '-');\nconst fileName  = 'ArviFund_' + periodStart.getDate() + BULAN_SHORT[periodStart.getMonth()] +\n                  '-' + periodEnd.getDate() + BULAN_SHORT[periodEnd.getMonth()] +\n                  periodEnd.getFullYear() + '.xlsx';\n\nreturn [{\n  json: {\n    isEndOfPeriod,\n    todayDate,\n    endDate,\n    payDate,\n    periodeStart : fmtISO(periodStart),\n    periodeEnd   : fmtISO(periodEnd),\n    periodeLabel,\n    periodeYear,\n    folderBulan,\n    fileName,\n    gasUrl: $vars?.GAS_URL || 'https://script.google.com/macros/s/AKfycbxTgawdxMBhnJd_gy1Obl1yiAUId0NYWM55L50N-Wx4eeR4KbnRhZm6rIK8UUiwPLQE/exec'\n  }\n}];"
        },
        "id": "664b0dd6-e164-4120-97bf-d6151af9cfa5",
        "name": "Cek Akhir Periode",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          -400,
          96
        ]
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict",
              "version": 1
            },
            "conditions": [
              {
                "id": "backup-if-cond-01",
                "leftValue": "={{ $json.isEndOfPeriod }}",
                "rightValue": true,
                "operator": {
                  "type": "boolean",
                  "operation": "true",
                  "singleValue": true
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "e20f5640-2786-4abc-ba04-eae2b19ba350",
        "name": "Apakah Akhir Periode?",
        "type": "n8n-nodes-base.if",
        "typeVersion": 2,
        "position": [
          -208,
          96
        ]
      },
      {
        "parameters": {
          "url": "={{ $('Cek Akhir Periode').first().json.gasUrl + '?action=laporan&periodeStart=' + $('Cek Akhir Periode').first().json.periodeStart + '&periodeEnd=' + $('Cek Akhir Periode').first().json.periodeEnd }}",
          "options": {}
        },
        "id": "c0ef13cb-f976-4fe6-8094-43334e5ec00e",
        "name": "Fetch Data Periode",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4,
        "position": [
          0,
          0
        ]
      },
      {
        "parameters": {
          "jsCode": "// Build Excel file menggunakan SheetJS (xlsx)\n// n8n sudah include library xlsx secara built-in via $binary\n\nconst meta    = $('Cek Akhir Periode').first().json;\nconst data    = $input.first().json;\n\nconst periodeLabel = meta.periodeLabel;\nconst fileName     = meta.fileName;\n\nconst fmt = n => Number(n || 0).toLocaleString('id-ID');\n\n// \u2500\u2500 Helper: buat array rows \u2500\u2500\nconst byKategori    = data.byKategori    || {};\nconst byBank        = data.byBank        || {};\nconst byUser        = data.byUser        || {};\nconst txTerakhir    = data.transaksiTerakhir || [];\n\n// Sheet 1: Ringkasan\nconst ringkasanRows = [\n  ['ArviFund \u2014 Laporan Keuangan', '', ''],\n  ['Periode', periodeLabel, ''],\n  ['', '', ''],\n  ['RINGKASAN', '', ''],\n  ['Total Pemasukan',   data.totalPemasukan   || 0, ''],\n  ['Total Pengeluaran', data.totalPengeluaran || 0, ''],\n  ['Saldo Akhir',       data.saldo            || 0, ''],\n  ['Total Tarik Tunai', data.totalTarikTunai  || 0, ''],\n  ['', '', ''],\n  ['PER KATEGORI', '', ''],\n  ['Kategori', 'Total', ''],\n  ...Object.entries(byKategori).sort((a,b) => b[1]-a[1]).map(([k,v]) => [k, v, '']),\n  ['', '', ''],\n  ['PER BANK', '', ''],\n  ['Bank', 'Total', ''],\n  ...Object.entries(byBank).sort((a,b) => b[1]-a[1]).map(([k,v]) => [k, v, '']),\n  ['', '', ''],\n  ['PER USER', '', ''],\n  ['User', 'Total Pengeluaran', ''],\n  ...Object.entries(byUser).sort((a,b) => b[1]-a[1]).map(([k,v]) => [k, v, '']),\n];\n\n// Sheet 2: Detail Transaksi\nconst detailHeaders = ['Tanggal', 'Toko / Sumber', 'Uraian', 'Kategori', 'Metode', 'Bank', 'User', 'Nilai'];\nconst detailRows = [\n  detailHeaders,\n  ...txTerakhir.map(t => [\n    t.tanggal || '-',\n    t.toko    || '-',\n    t.uraian  || '-',\n    t.kategori|| '-',\n    t.metode  || '-',\n    t.bank    || '-',\n    t.user    || '-',\n    t.nilai   || 0\n  ])\n];\n\n// \u2500\u2500 Build workbook dengan SheetJS \u2500\u2500\nconst XLSX = require('xlsx');\n\nconst wb = XLSX.utils.book_new();\n\n// Sheet Ringkasan\nconst wsRingkasan = XLSX.utils.aoa_to_sheet(ringkasanRows);\nwsRingkasan['!cols'] = [{wch:25}, {wch:20}, {wch:10}];\nXLSX.utils.book_append_sheet(wb, wsRingkasan, 'Ringkasan');\n\n// Sheet Detail\nconst wsDetail = XLSX.utils.aoa_to_sheet(detailRows);\nwsDetail['!cols'] = [\n  {wch:12},{wch:25},{wch:30},{wch:20},{wch:12},{wch:12},{wch:12},{wch:15}\n];\nXLSX.utils.book_append_sheet(wb, wsDetail, 'Detail Transaksi');\n\n// Export ke buffer\nconst buf = XLSX.write(wb, { type: 'buffer', bookType: 'xlsx' });\nconst base64 = Buffer.from(buf).toString('base64');\n\nreturn [{\n  json: {\n    fileName,\n    periodeLabel,\n    periodeYear  : meta.periodeYear,\n    folderBulan  : meta.folderBulan,\n    periodeStart : meta.periodeStart,\n    periodeEnd   : meta.periodeEnd\n  },\n  binary: {\n    data: {\n      data        : base64,\n      mimeType    : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n      fileName    : fileName,\n      fileExtension: 'xlsx'\n    }\n  }\n}];"
        },
        "id": "e720c9d2-00ee-4d77-a8fd-0aebe2e38104",
        "name": "Build Excel Backup",
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [
          208,
          0
        ]
      },
      {
        "parameters": {
          "resource": "folder",
          "operation": "search"
        },
        "id": "d3c864b8-f2e9-4a45-8df5-83bfe706b624",
        "name": "Cari Folder Tahun",
        "type": "n8n-nodes-base.googleDrive",
        "typeVersion": 3,
        "position": [
          400,
          0
        ]
      },
      {
        "parameters": {
          "conditions": {
            "options": {
              "caseSensitive": true,
              "leftValue": "",
              "typeValidation": "strict",
              "version": 2
            },
            "conditions": [
              {
                "id": "folder-cond-01",
                "leftValue": "={{ $json.id }}",
                "rightValue": "",
                "operator": {
                  "type": "string",
                  "operation": "isNotEmpty"
                }
              }
            ],
            "combinator": "and"
          },
          "options": {}
        },
        "id": "bd1f5cba-b929-4126-93a2-ddd47ba4bbce",
        "name": "Folder Tahun Ada?",
        "type": "n8n-nodes-base.if",
        "typeVersion": 2,
        "position": [
          608,
          0
        ]
      },
      {
        "parameters": {
          "resource": "folder",
          "operation": "createFolder"
        },
        "id": "22342b28-996f-43e0-8639-6a0c78432108",
        "name": "Buat Folder Tahun",
        "type": "n8n-nodes-base.googleDrive",
        "typeVersion": 3,
        "position": [
          800,
          96
        ]
      },
      {
        "parameters": {
          "mode": "chooseBranch"
        },
        "id": "5642e42a-722a-4f3c-b0db-a666ae32040f",
        "name": "Merge Folder ID",
        "type": "n8n-nodes-base.merge",
        "typeVersion": 3,
        "position": [
          1008,
          0
        ]
      },
      {
        "parameters": {
          "name": "={{ $('Build Excel Backup').first().json.fileName }}",
          "driveId": {
            "__rl": true,
            "mode": "list",
            "value": "My Drive"
          },
          "folderId": {
            "__rl": true,
            "mode": "id",
            "value": "={{ $json.id }}"
          },
          "options": {}
        },
        "id": "9f7b76b6-acde-4549-bcc0-421a4437ec22",
        "name": "Upload Excel ke GDrive",
        "type": "n8n-nodes-base.googleDrive",
        "typeVersion": 3,
        "position": [
          1200,
          0
        ]
      },
      {
        "parameters": {
          "chatId": "={{ $vars.ADMIN_CHAT_ID }}",
          "text": "={{ '\u2705 *Backup Periode Berhasil!*\\n\\n\ud83d\udcc1 File: ' + $(\\'Build Excel Backup\\').first().json.fileName + '\\n\ud83d\udcc5 Periode: ' + $(\\'Build Excel Backup\\').first().json.periodeLabel + '\\n\ud83d\udcbe Tersimpan di Google Drive folder ArviFund-' + $(\\'Build Excel Backup\\').first().json.periodeYear }}",
          "additionalFields": {
            "parse_mode": "Markdown"
          }
        },
        "id": "e90b3903-ce74-45eb-a768-87b1690756e5",
        "name": "Notif Backup Berhasil",
        "type": "n8n-nodes-base.telegram",
        "typeVersion": 1,
        "position": [
          1408,
          0
        ],
        "webhookId": "7b5abbe5-4d4e-4888-a328-7b784b9b7d49"
      }
    ],
    "connections": {
      "Switch": {
        "main": [
          [
            {
              "node": "Get File Path",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "HTTP Request",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Telegram7",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Telegram5",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Build Report Hari Ini",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Parse Command",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Edit Fields",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Get a file",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Analisa gambar": {
        "main": [
          [
            {
              "node": "If",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "If": {
        "main": [
          [
            {
              "node": "Respon error",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Google Sheets",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Google Sheets": {
        "main": [
          [
            {
              "node": "Telegram1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Edit Fields": {
        "main": [
          [
            {
              "node": "AI INPUT DATA TEXT",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "If2": {
        "main": [
          [
            {
              "node": "Tarik Tunai",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Expenses",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Telegram Trigger": {
        "main": [
          [
            {
              "node": "Wait",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Switch1": {
        "main": [
          [
            {
              "node": "If2",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Income",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Telegram8",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Tarik Tunai": {
        "main": [
          [
            {
              "node": "Telegram",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Expenses": {
        "main": [
          [
            {
              "node": "Telegram3",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Income": {
        "main": [
          [
            {
              "node": "Telegram6",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "HTTP Request": {
        "main": [
          [
            {
              "node": "AI Agent1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "AI Agent1": {
        "main": [
          [
            {
              "node": "Telegram4",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Get a file": {
        "main": [
          [
            {
              "node": "Transcribe a recording",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Transcribe a recording": {
        "main": [
          [
            {
              "node": "Edit Fields1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Google Gemini Chat Model2": {
        "ai_languageModel": [
          [
            {
              "node": "AI INPUT VOICE NOTE",
              "type": "ai_languageModel",
              "index": 0
            }
          ]
        ]
      },
      "Edit Fields1": {
        "main": [
          [
            {
              "node": "AI INPUT VOICE NOTE",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Simple Memory": {
        "ai_memory": [
          [
            {
              "node": "AI Agent1",
              "type": "ai_memory",
              "index": 0
            }
          ]
        ]
      },
      "Simple Memory1": {
        "ai_memory": [
          [
            {
              "node": "AI INPUT DATA TEXT",
              "type": "ai_memory",
              "index": 0
            }
          ]
        ]
      },
      "Simple Memory2": {
        "ai_memory": [
          [
            {
              "node": "AI INPUT VOICE NOTE",
              "type": "ai_memory",
              "index": 0
            }
          ]
        ]
      },
      "OpenRouter Chat Model": {
        "ai_languageModel": [
          [
            {
              "node": "AI Agent1",
              "type": "ai_languageModel",
              "index": 0
            }
          ]
        ]
      },
      "OpenRouter Chat Model1": {
        "ai_languageModel": [
          [
            {
              "node": "AI INPUT DATA TEXT",
              "type": "ai_languageModel",
              "index": 0
            }
          ]
        ]
      },
      "AI INPUT DATA TEXT": {
        "main": [
          [
            {
              "node": "Switch1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "AI INPUT VOICE NOTE": {
        "main": [
          [
            {
              "node": "Switch1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Cek Ada Data": {
        "main": [
          [
            {
              "node": "Agregasi dan Render HTML",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Kirim Error No Data",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Cek PDF Berhasil": {
        "main": [
          [
            {
              "node": "Ubah nama dokumen",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Kirim Error PDF",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "HTML to PDF": {
        "main": [
          [
            {
              "node": "Cek PDF Berhasil",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Agregasi dan Render HTML": {
        "main": [
          [
            {
              "node": "HTML to PDF",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Merge Data": {
        "main": [
          [
            {
              "node": "Cek Ada Data",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Read Income": {
        "main": [
          [
            {
              "node": "Merge Data",
              "type": "main",
              "index": 1
            }
          ]
        ]
      },
      "Read Expenses": {
        "main": [
          [
            {
              "node": "Merge Data",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Telegram Processing": {
        "main": [
          [
            {
              "node": "Read Income",
              "type": "main",
              "index": 0
            },
            {
              "node": "Read Expenses",
              "type": "main",
              "index": 0
            },
            {
              "node": "Merge Data",
              "type": "main",
              "index": 2
            }
          ]
        ]
      },
      "Parse Command": {
        "main": [
          [
            {
              "node": "Edit Fields2",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Send a message": {
        "main": [
          [
            {
              "node": "Kirim PDF ke Telegram",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Edit Fields2": {
        "main": [
          [
            {
              "node": "Merge Data1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Ubah nama dokumen": {
        "main": [
          [
            {
              "node": "Send a message",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Parse Command (Webhook)": {
        "main": [
          [
            {
              "node": "Edit Fields (Webhook)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Edit Fields (Webhook)": {
        "main": [
          [
            {
              "node": "Merge Data1",
              "type": "main",
              "index": 1
            }
          ]
        ]
      },
      "Merge Data1": {
        "main": [
          [
            {
              "node": "Edit Fields (Webhook)1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Edit Fields (Webhook)1": {
        "main": [
          [
            {
              "node": "Telegram Processing",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Webhook": {
        "main": [
          [
            {
              "node": "Wait1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Wait": {
        "main": [
          [
            {
              "node": "Switch",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Wait1": {
        "main": [
          [
            {
              "node": "Parse Command (Webhook)",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Get File Path": {
        "main": [
          [
            {
              "node": "Download File",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Download File": {
        "main": [
          [
            {
              "node": "Code in JavaScript",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Code in JavaScript": {
        "main": [
          [
            {
              "node": "Analisa gambar",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Wait2": {
        "main": [
          [
            {
              "node": "Get Config Sheet",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Webhook1": {
        "main": [
          [
            {
              "node": "Wait2",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Get Config Sheet": {
        "main": [
          [
            {
              "node": "Filter by Username",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Filter by Username": {
        "main": [
          [
            {
              "node": "User Ditemukan?",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "User Ditemukan?": {
        "main": [
          [
            {
              "node": "Kirim Reset Password",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Response Not Found",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Kirim via Telegram": {
        "main": [
          [
            {
              "node": "Response OK",
              "type": "main",
              "index": 0
            },
            {
              "node": "Response Error",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Response Error": {
        "main": [
          [
            {
              "node": "Response Error",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Build Report Hari Ini": {
        "main": [
          [
            {
              "node": "HTTP Request Hari Ini",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "HTTP Request Hari Ini": {
        "main": [
          [
            {
              "node": "Format Laporan Hari Ini",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Format Laporan Hari Ini": {
        "main": [
          [
            {
              "node": "Kirim Laporan Hari Ini",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Cron Backup Periode": {
        "main": [
          [
            {
              "node": "Cek Akhir Periode",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Cek Akhir Periode": {
        "main": [
          [
            {
              "node": "Apakah Akhir Periode?",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Apakah Akhir Periode?": {
        "main": [
          [
            {
              "node": "Fetch Data Periode",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Fetch Data Periode": {
        "main": [
          [
            {
              "node": "Build Excel Backup",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Build Excel Backup": {
        "main": [
          [
            {
              "node": "Cari Folder Tahun",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Cari Folder Tahun": {
        "main": [
          [
            {
              "node": "Folder Tahun Ada?",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Folder Tahun Ada?": {
        "main": [
          [
            {
              "node": "Merge Folder ID",
              "type": "main",
              "index": 0
            }
          ],
          [
            {
              "node": "Buat Folder Tahun",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Buat Folder Tahun": {
        "main": [
          [
            {
              "node": "Merge Folder ID",
              "type": "main",
              "index": 1
            }
          ]
        ]
      },
      "Merge Folder ID": {
        "main": [
          [
            {
              "node": "Upload Excel ke GDrive",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Upload Excel ke GDrive": {
        "main": [
          [
            {
              "node": "Notif Backup Berhasil",
              "type": "main",
              "index": 0
            }
          ]
        ]
      },
      "Kirim Reset Password": {
        "main": [
          [
            {
              "node": "Kirim via Telegram",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    }
  },
  "tags": [],
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "parentFolderId": null,
  "scopes": [
    "workflow:create",
    "workflow:delete",
    "workflow:execute",
    "workflow:execute-chat",
    "workflow:list",
    "workflow:move",
    "workflow:publish",
    "workflow:read",
    "workflow:share",
    "workflow:unpublish",
    "workflow:unshare",
    "workflow:update",
    "workflow:updateRedactionSetting"
  ],
  "canExecute": true
}