Tuesday, August 25, 2026

Automation of entries of shopping in Gnucash on Endless OS with the help of Gemini.

 When I buy something and make payment sms immediately comes on mobile. I enter the transactions in Gnucash on Laptop with Endless OS manually. I asked Gemini how to automate this. Gemini replied that it does not have access to my sms messages but I can install an SMS Forwarder which can be configured with IP address of my Laptop. But the IP address should be static. I changed IP of my Wifi on Laptop with static IP 192,168.29.3 and configured DHCP in Jio Fiber modem to have start IP as 192.168.29.10. I already have static IP of 192,168.29.2 on Wifi Range extender modem.

The sms forwarder is configured for Webhook URL http://192.168.29.3:5000/sms

I generally use my ICICI Coral Rupay credit card and ICICI Savings Bank account for every day local and online shopping. The  sms has ICICIT as sender. I configured this sender.

On Laptop I installed Flask by following command:

python3 -m pip install Flask --break-system-packages

I saved file sms_to_qif.py with following script

from flask import Flask, request
import re
from datetime import datetime

app = Flask(__name__)

# --- CATEGORY MAPPING ---
# Put your merchant names in ALL LOWERCASE here.
# Ensure the right side perfectly matches your GnuCash expense accounts.
CATEGORY_MAP = {
    "SURJAN": "Expenses:Groceries:Vegetables",
    "zomato": "Expenses:Dining",
    "annapurna marathi": "Expenses:Dining",
    "swiggy": "Expenses:Dining",
    "idli & more": "Expenses:Dining",
    "VAIBHAV KIRANA": "Expenses:Groceries:Milk, Curd, Ghee",
    "jio": "Expenses:Broadband",
    "aruna": "Current Assets:Loan to Aruna",
    "innovati": "Expenses:Shopping" # Added based on your Rupay test
}

# The script checks these in order from top to bottom.
 # --- CONFIGURATION RULES ---
ACCOUNT_RULES = [
    # 1. RULES FOR SALAIYA ACCOUNT (XX030)
    {
        "name": "ICICI Savings Salaiya - EXPENSE",
        "keyword": "XX030",
        "gnucash_account": "Assets:Current Assets:ICICI Bank Salaiya",
        "regex": r'debited for Rs\s*(\d+(?:\.\d{1,2})?).*?;\s*(.+?)\s+credited',
        "is_credit": False
    },
    {
        "name": "ICICI Savings Salaiya - INCOME",
        "keyword": "XX030",
        "gnucash_account": "Assets:Current Assets:ICICI Bank Salaiya",
        "regex": r'Acct XX030 is credited with Rs\s*(\d+(?:\.\d{1,2})?).*?(?:from|by)\s+([A-Za-z0-9\s]+)',
        "is_credit": True
    },

    # 2. RULES FOR 853 ACCOUNT (XX853)
    {
        "name": "ICICI Savings 853 - EXPENSE",
        "keyword": "XX853", 
        "gnucash_account": "Assets:Current Assets:ICICI Bank", 
        "regex": r'debited for Rs\s*(\d+(?:\.\d{1,2})?).*?;\s*(.+?)\s+credited',
        "is_credit": False
    },
    {
        "name": "ICICI Savings 853 - INCOME",
        "keyword": "XX853", 
        "gnucash_account": "Assets:Current Assets:ICICI Bank",
        "regex": r'Acct XX853 is credited with Rs\s*(\d+(?:\.\d{1,2})?).*?(?:from|by)\s+([A-Za-z0-9\s]+)',
        "is_credit": True
    },

    # 3. RULE FOR RUPAY CREDIT CARD (XX2000)
    {
        "name": "ICICI Credit Card - EXPENSE",
        "keyword": "XX2000",
        "gnucash_account": "Liabilities:Credit Cards:ICICI Rupay", 
        "regex": r'debited for INR\s*(\d+(?:\.\d{1,2})?).*?for\s+(.+?)\.\s*To dispute',
        "is_credit": False
    },

    # 4. GENERIC FALLBACK (Catches anything else)
    {
        "name": "Generic Fallback",
        "keyword": "",  
        "gnucash_account": "Imbalance-INR", 
        "regex": r'(?:Rs\.?|INR)\s*(\d+(?:\.\d{1,2})?).*?(?:to|at|for)\s+([A-Za-z0-9\s-]+?)(?:\.|\son)',
        "is_credit": False
    }
]

@app.route('/sms', methods=['POST'])
def receive_sms():
    data = request.json or {}
    
    # Safely get text and sender, accounting for different app payload formats
    sms_text = data.get('text', data.get('body', ''))
    sender = data.get('sender', data.get('from', ''))

    print(f"\nReceived SMS from {sender}: {sms_text}")
    date_str = datetime.now().strftime("%d/%m/%Y")

    for rule in ACCOUNT_RULES:
        # Check if the keyword is in the SMS text OR the Sender name
        if rule["keyword"].lower() in sms_text.lower() or rule["keyword"].lower() in sender.lower():
            
            match = re.search(rule["regex"], sms_text, re.IGNORECASE)
            
            if match:
                amount = match.group(1)
                
                if len(match.groups()) >= 2:
                    merchant = match.group(2).strip()
                else:
                    merchant = "Unknown Payee"
                
                # --- AUTO-CATEGORIZATION ENGINE ---
                category = "Expenses:Uncategorized" 
                merchant_lower = merchant.lower()
                
                for key, mapped_category in CATEGORY_MAP.items():
                    if key in merchant_lower:
                        category = mapped_category
                        break  
                
                gnucash_account = rule["gnucash_account"]
                sign = "" if rule.get("is_credit", False) else "-"
                
                # Format for QIF with Account and Category flags
                qif_entry = f"\n!Account\nN{gnucash_account}\n^\n!Type:Bank\nD{date_str}\nT{sign}{amount}\nP{merchant}\nM{sms_text}\nL{category}\n^\n"
                
                with open("gnucash_import.qif", "a") as file:
                    file.write(qif_entry)
                    
                print(f"✅ Saved to {rule['name']}: {merchant} - Rs. {sign}{amount} ({category})")
                return "Transaction Saved", 200
                
    print("❌ No matching rule found or regex failed to extract data.")
    return "Ignored", 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

then I started the server on Laptop with following command

python3 sms_to_qif.py

Now whenever the sms comes on mobile it comes on Laptop and saved as  gnucash_import.gif

I can import the transaction on Gnucash

By the way I am not a Coder the above script and entire guidance is given by Gemini.

The system is in testing face and after a few days it will be totally automated without manually importing the file. 

 

 

No comments:

Automation of entries of shopping in Gnucash on Endless OS with the help of Gemini.

  When I buy something and make payment sms immediately comes on mobile. I enter the transactions in Gnucash on Laptop with Endless OS manua...