🏦 ATM System - Visual Diagrams & Flow Charts

1️⃣ Program Initialization Flow

PROGRAM START
    │
    ├─ Python imports all modules
    │  ├─ sqlite3
    │  ├─ os
    │  ├─ re
    │  ├─ hashlib
    │  ├─ datetime
    │  ├─ getpass
    │  └─ typing, contextlib
    │
    ├─ Load all constants
    │  ├─ DB_FILE = "users.db"
    │  ├─ MIN_BALANCE = 500
    │  ├─ DEFAULT_DAILY_WITHDRAW_LIMIT = 20000
    │  └─ ... (11 more)
    │
    ├─ Define all functions (no execution yet)
    │
    └─ Reach: if __name__ == "__main__"
       │
       ├─ Print initialization message
       │
       ├─ Call init_db()
       │  ├─ Open SQLite connection
       │  ├─ CREATE TABLE users
       │  ├─ CREATE TABLE transactions
       │  ├─ CREATE TABLE settings
       │  ├─ INSERT default settings
       │  └─ CREATE receipts folder
       │
       ├─ Print success messages
       │  ├─ "Database initialized successfully"
       │  ├─ "Receipts folder created"
       │  ├─ "Default Admin Password: SecureAdmin@2025"
       │  └─ "Change it immediately via Admin Panel"
       │
       └─ Call main_menu()
          │
          └─ Infinite loop
             ├─ Display menu (1-4)
             ├─ Get user choice
             └─ Execute corresponding function
                ├─ "1" → login()
                ├─ "2" → create_account()
                ├─ "3" → admin_menu()
                └─ "4" → Exit (break)

2️⃣ Login Process (Complete Flow)

LOGIN() FUNCTION
    │
    ├─ INPUT: account_number
    │
    ├─ FETCH: user = get_user(account_number)
    │
    ├─ IF NOT FOUND:
    │  └─ PRINT: "Account not found"
    │  └─ RETURN: (None, None)
    │
    ├─ IF FOUND BUT LOCKED:
    │  └─ PRINT: "Account locked due to multiple failed attempts"
    │  └─ RETURN: (None, None)
    │
    ├─ PIN VERIFICATION LOOP (max 3 times):
    │  │
    │  ├─ ITERATION 1:
    │  │  ├─ INPUT: pin = getpass("Enter 4-digit PIN: ")
    │  │  ├─ VALIDATE: isdigit() AND len == 4
    │  │  │  └─ If invalid: increment_failed_attempts(), continue
    │  │  ├─ HASH: pin_hash = hash_pin(pin)
    │  │  ├─ COMPARE: pin_hash == user['pin_hash']
    │  │  │  ├─ If MATCH ✅:
    │  │  │  │  ├─ PRINT: "Login successful"
    │  │  │  │  ├─ CALL: reset_failed_attempts(user)
    │  │  │  │  ├─ FETCH: fresh user data
    │  │  │  │  └─ RETURN: (account_number, user_dict)
    │  │  │  └─ If NO MATCH ❌:
    │  │  │     ├─ PRINT: "Incorrect PIN"
    │  │  │     ├─ CALL: increment_failed_attempts(user)
    │  │  │     ├─ RELOAD: user = get_user(account_number)
    │  │  │     ├─ remaining = 3 - user['failed_attempts']
    │  │  │     └─ PRINT: f"Attempts remaining: {remaining}"
    │  │
    │  ├─ ITERATION 2: (same as above)
    │  │  ├─ If correct: RETURN (account, user) ✅
    │  │  └─ If wrong: failed_attempts = 2, remaining = 1
    │  │
    │  └─ ITERATION 3: (same as above)
    │     ├─ If correct: RETURN (account, user) ✅
    │     └─ If wrong: 
    │        ├─ failed_attempts = 3
    │        ├─ locked = 1 (AUTO-LOCK)
    │        └─ PRINT: "Account locked due to multiple failed attempts"
    │
    └─ AFTER LOOP (all 3 failed):
       └─ RETURN: (None, None)

3️⃣ Account Creation (Step-by-Step)

CREATE_ACCOUNT() FUNCTION
    │
    ├─ STEP 1: INPUT NAME
    │  │
    │  ├─ INPUT: name = input("Enter your name: ")
    │  ├─ CLEAN: name = " ".join(name.strip().split())
    │  │          "  john   doe  " → "john doe"
    │  ├─ VALIDATE: is_valid, sanitized, error = sanitize_name(name)
    │  │  ├─ Check length: 2-50 characters
    │  │  ├─ Check format: ^[A-Za-z\s]+$ (letters and spaces only)
    │  │  └─ If invalid: PRINT error, LOOP
    │  │  └─ If valid: PROCEED
    │  │
    │  └─ SUCCESS: name = "John Doe"
    │
    ├─ STEP 2: INPUT & VALIDATE PIN
    │  │
    │  ├─ Loop until valid:
    │  │  ├─ INPUT: pin = getpass("Set a 4-digit numeric PIN: ")
    │  │  ├─ INPUT: confirm = getpass("Confirm PIN: ")
    │  │  │
    │  │  ├─ Check 1: pin == confirm
    │  │  │  └─ If not: PRINT "do not match", continue
    │  │  │
    │  │  ├─ Check 2: validate_pin_strength(pin)
    │  │  │  ├─ Must be exactly 4 digits: len(pin) == 4
    │  │  │  ├─ Must be numeric: pin.isdigit()
    │  │  │  ├─ Not repeating: len(set(pin)) != 1
    │  │  │  ├─ Not ascending: not (1→2→3→4)
    │  │  │  ├─ Not descending: not (4→3→2→1)
    │  │  │  └─ If invalid: PRINT policy, continue
    │  │  │
    │  │  └─ If all valid: BREAK
    │  │
    │  └─ SUCCESS: pin = "1357" (hashed later)
    │
    ├─ STEP 3: INPUT INITIAL DEPOSIT
    │  │
    │  ├─ Loop until valid:
    │  │  ├─ INPUT: amount = float(input(...))
    │  │  ├─ CHECK: amount >= MIN_BALANCE (500)
    │  │  │  └─ If less: PRINT minimum required, continue
    │  │  └─ If valid: BREAK
    │  │
    │  └─ SUCCESS: amount = 5000
    │
    ├─ STEP 4: CREATE DATABASE RECORD
    │  │
    │  ├─ GENERATE: account_number = "1000000000"
    │  ├─ HASH: pin_hash = hash_pin("1357")
    │  ├─ INSERT into users table:
    │  │  ├─ account_number: "1000000000"
    │  │  ├─ name: "John Doe"
    │  │  ├─ pin_hash: "a1b2c3d4e5..."
    │  │  ├─ balance: 5000.00
    │  │  ├─ locked: 0
    │  │  ├─ failed_attempts: 0
    │  │  ├─ created_at: "2025-12-31T14:30:45"
    │  │  └─ last_withdraw_date: NULL
    │  │
    │  └─ SUCCESS: Account created with ID 1000000000
    │
    ├─ STEP 5: RECORD INITIAL DEPOSIT
    │  │
    │  └─ INSERT INTO transactions:
    │     ├─ account_number: "1000000000"
    │     ├─ type: "DEPOSIT"
    │     ├─ amount: 5000.00
    │     ├─ datetime: "2025-12-31T14:30:45"
    │     └─ balance_after: 5000.00
    │
    ├─ STEP 6: GENERATE RECEIPT
    │  │
    │  └─ CREATE FILE: "receipt_1000000000_31122025_143045_1.txt"
    │     ├─ Contains: account (masked), date, amount, balance
    │     └─ SAVE TO: receipts/ folder
    │
    └─ STEP 7: DISPLAY SUCCESS MESSAGE
       │
       └─ Print:
          ├─ "ACCOUNT CREATED SUCCESSFULLY"
          ├─ "Your account number is: 1000000000"
          ├─ "Name: John Doe"
          ├─ "Initial Balance: ₹5000.00"
          ├─ "Receipt saved: receipt_1000000000_31122025_143045_1.txt"
          └─ "IMPORTANT: Remember your account number and PIN!"

4️⃣ Withdrawal with ALL Validations

WITHDRAW(account_number) FUNCTION
    │
    ├─ FETCH: user, settings, atm_cash, daily_limit
    │
    ├─ INPUT VALIDATION:
    │  └─ amount = float(input(...))
    │  └─ If <= 0: PRINT "Must be positive", RETURN
    │
    ├─ VALIDATION 1: MINIMUM BALANCE CHECK
    │  │
    │  └─ Calculate: remaining = balance - amount
    │     │
    │     ├─ IF remaining >= MIN_BALANCE ✅:
    │     │  └─ PASS → continue to next validation
    │     │
    │     └─ IF remaining < MIN_BALANCE ❌:
    │        ├─ max_withdrawable = balance - MIN_BALANCE
    │        ├─ PRINT: "Cannot withdraw. Must maintain ₹500"
    │        ├─ PRINT: f"You can withdraw up to: ₹{max_withdrawable}"
    │        └─ RETURN
    │
    ├─ VALIDATION 2: DAILY LIMIT CHECK
    │  │
    │  ├─ FETCH: today_withdrawn = get_today_withdrawn_amount(account)
    │  └─ Calculate: total = today_withdrawn + amount
    │     │
    │     ├─ IF total <= daily_limit ✅:
    │     │  └─ PASS → continue to next validation
    │     │
    │     └─ IF total > daily_limit ❌:
    │        ├─ remaining = daily_limit - today_withdrawn
    │        ├─ PRINT: "Daily limit exceeded"
    │        ├─ PRINT: f"Daily limit: ₹{daily_limit}"
    │        ├─ PRINT: f"Already withdrawn: ₹{today_withdrawn}"
    │        ├─ PRINT: f"Remaining today: ₹{remaining}"
    │        └─ RETURN
    │
    ├─ VALIDATION 3: ATM CASH CHECK
    │  │
    │  ├─ FETCH: atm_cash from settings
    │  └─ Compare: amount vs atm_cash
    │     │
    │     ├─ IF amount <= atm_cash ✅:
    │     │  └─ PASS → execute withdrawal
    │     │
    │     └─ IF amount > atm_cash ❌:
    │        ├─ PRINT: "ATM does not have enough cash"
    │        ├─ PRINT: f"ATM available: ₹{atm_cash}"
    │        └─ RETURN
    │
    └─ EXECUTE WITHDRAWAL (All validations passed ✅):
       │
       ├─ BEGIN DATABASE TRANSACTION:
       │  │
       │  ├─ UPDATE 1: user balance
       │  │  ├─ new_balance = balance - amount
       │  │  ├─ UPDATE users SET balance = new_balance
       │  │  ├─ UPDATE users SET last_withdraw_date = TODAY
       │  │  └─ WHERE account_number = ?
       │  │
       │  ├─ UPDATE 2: ATM cash pool
       │  │  ├─ new_atm_cash = atm_cash - amount
       │  │  ├─ UPDATE settings SET atm_cash_pool = new_atm_cash
       │  │  └─ WHERE id = 1
       │  │
       │  ├─ INSERT: transaction record
       │  │  ├─ account_number: ?
       │  │  ├─ type: "WITHDRAW"
       │  │  ├─ amount: amount
       │  │  ├─ datetime: NOW
       │  │  └─ balance_after: new_balance
       │  │
       │  ├─ GENERATE: receipt file
       │  │
       │  └─ AUTO-COMMIT (if all succeed) ✅
       │     OR AUTO-ROLLBACK (if any fails) ❌
       │
       ├─ SUCCESS OUTPUT:
       │  ├─ PRINT: "✅ ₹{amount} Withdrawn successfully"
       │  ├─ PRINT: f"New balance: ₹{new_balance}"
       │  ├─ PRINT: f"ATM Cash Remaining: ₹{new_atm_cash}"
       │  └─ PRINT: f"Receipt saved: {filename}"
       │
       └─ ERROR OUTPUT (if exception):
          ├─ PRINT: "❌ Transaction failed and rolled back: {error}"
          └─ PRINT: "Your balance and ATM cash remain unchanged"
          
          (Database state: NO CHANGES made)

5️⃣ Transaction Rollback Protection

DATABASE TRANSACTION WITH ROLLBACK

SCENARIO A: ALL STEPS SUCCEED ✅
───────────────────────────────

BEGIN TRANSACTION
    │
    ├─ STEP 1: UPDATE users (balance)
    │  └─ Execution: ✅ SUCCESS
    │
    ├─ STEP 2: UPDATE settings (atm_cash)
    │  └─ Execution: ✅ SUCCESS
    │
    ├─ STEP 3: INSERT transactions
    │  └─ Execution: ✅ SUCCESS
    │
    └─ AUTO-COMMIT
       └─ All 3 changes saved permanently
          ├─ balance: 5000 → 4000 ✅
          ├─ atm_cash: 1000000 → 999000 ✅
          └─ transaction: inserted ✅


SCENARIO B: STEP 2 FAILS ❌
──────────────────────────

BEGIN TRANSACTION
    │
    ├─ STEP 1: UPDATE users (balance)
    │  └─ Execution: ✅ SUCCESS
    │
    ├─ STEP 2: UPDATE settings (atm_cash)
    │  └─ Execution: ❌ SQL SYNTAX ERROR
    │
    ├─ STEP 3: (NOT EXECUTED - chain stops)
    │
    └─ AUTO-ROLLBACK (because exception occurred)
       └─ All changes UNDONE
          ├─ balance: 5000 → 5000 (unchanged) ✅
          ├─ atm_cash: 1000000 → 1000000 (unchanged) ✅
          └─ transaction: not recorded ✅
          
       Result: DATABASE CONSISTENT (as if withdrawal never happened)


SCENARIO C: STEP 3 FAILS ❌
──────────────────────────

BEGIN TRANSACTION
    │
    ├─ STEP 1: UPDATE users (balance)
    │  └─ Execution: ✅ SUCCESS
    │
    ├─ STEP 2: UPDATE settings (atm_cash)
    │  └─ Execution: ✅ SUCCESS
    │
    ├─ STEP 3: INSERT transactions
    │  └─ Execution: ❌ FOREIGN KEY CONSTRAINT ERROR
    │
    └─ AUTO-ROLLBACK (because exception occurred)
       └─ All changes UNDONE (including steps 1 & 2)
          ├─ balance: 5000 → 5000 (UNDONE) ✅
          ├─ atm_cash: 1000000 → 1000000 (UNDONE) ✅
          └─ transaction: not recorded ✅

6️⃣ Daily Withdrawal Limit Reset

DAILY LIMIT LOGIC

SETUP:
├─ Daily Limit: ₹20,000 per user per calendar day
├─ Balance: ₹50,000
└─ Tracking: last_withdraw_date field in users table

DAY 1 (2025-12-31):
├─ 09:00 AM - Withdrawal 1: ₹5,000
│  ├─ last_withdraw_date: NULL (first withdrawal)
│  ├─ Query: SUM WHERE date = TODAY = ₹5,000
│  └─ Can withdraw? 5,000 <= 20,000 ✅
│     └─ Update: last_withdraw_date = "2025-12-31"
│
├─ 11:30 AM - Withdrawal 2: ₹8,000
│  ├─ last_withdraw_date: "2025-12-31" (today)
│  ├─ Query: SUM WHERE date = TODAY = ₹5,000 + ₹8,000 = ₹13,000
│  └─ Can withdraw? 13,000 <= 20,000 ✅
│
├─ 02:00 PM - Withdrawal 3: ₹4,000
│  ├─ last_withdraw_date: "2025-12-31" (today)
│  ├─ Query: SUM WHERE date = TODAY = ₹13,000 + ₹4,000 = ₹17,000
│  └─ Can withdraw? 17,000 <= 20,000 ✅
│
├─ 04:00 PM - Withdrawal 4: ₹5,000
│  ├─ last_withdraw_date: "2025-12-31" (today)
│  ├─ Query: SUM WHERE date = TODAY = ₹17,000 + ₹5,000 = ₹22,000
│  └─ Can withdraw? 22,000 <= 20,000 ❌
│     ├─ Message: "Daily limit exceeded"
│     ├─ Already withdrawn: ₹17,000
│     └─ Remaining: ₹3,000

 DAY 2 (2026-01-01):
├─ 08:00 AM - Check daily amount
│  ├─ last_withdraw_date: "2025-12-31" (yesterday)
│  ├─ today: "2026-01-01" (now)
│  ├─ last_withdraw_date != today → RESET
│  └─ Withdrawn today: ₹0 (RESET FOR NEW DAY)
│
└─ Withdrawal 1: ₹5,000
   ├─ Query: SUM WHERE date = TODAY = ₹5,000
   └─ Can withdraw? 5,000 <= 20,000 ✅
      └─ Update: last_withdraw_date = "2026-01-01"

7️⃣ PIN Validation Rules

PIN VALIDATION FLOWCHART

INPUT: "1234"
    │
    ├─ CHECK 1: Length
    │  └─ len("1234") == 4? ✅ PASS
    │
    ├─ CHECK 2: Numeric only
    │  └─ "1234".isdigit()? ✅ PASS
    │
    ├─ CHECK 3: Not repeating
    │  └─ set("1234") = {'1','2','3','4'}, len = 4
    │  └─ len(set) == 1? No, len = 4 ✅ PASS
    │
    ├─ CHECK 4: Not ascending
    │  └─ Is 1→2→3→4? Yes!
    │  └─ All consecutive increases? ✅ YES
    │  └─ ❌ FAIL (sequential)
    │
    └─ RESULT: INVALID ❌
       └─ Reason: "PIN cannot be sequential (e.g., 1234, 4321)"


INPUT: "1357"
    │
    ├─ CHECK 1: Length
    │  └─ len("1357") == 4? ✅ PASS
    │
    ├─ CHECK 2: Numeric only
    │  └─ "1357".isdigit()? ✅ PASS
    │
    ├─ CHECK 3: Not repeating
    │  └─ set("1357") = {'1','3','5','7'}, len = 4
    │  └─ len(set) == 1? No, len = 4 ✅ PASS
    │
    ├─ CHECK 4: Not ascending
    │  └─ Is 1→3→5→7? No! (1→3 is +2, not +1)
    │  └─ ✅ PASS
    │
    ├─ CHECK 5: Not descending
    │  └─ Is 7→5→3→1? (reversed) No!
    │  └─ ✅ PASS
    │
    └─ RESULT: VALID ✅


INPUT: "1111"
    │
    ├─ CHECK 1: Length
    │  └─ len("1111") == 4? ✅ PASS
    │
    ├─ CHECK 2: Numeric only
    │  └─ "1111".isdigit()? ✅ PASS
    │
    ├─ CHECK 3: Not repeating
    │  └─ set("1111") = {'1'}, len = 1
    │  └─ len(set) == 1? YES
    │  └─ ❌ FAIL (all repeating)
    │
    └─ RESULT: INVALID ❌
       └─ Reason: "PIN cannot have all repeating digits (e.g., 1111)"


INPUT: "4321"
    │
    ├─ CHECK 1: Length
    │  └─ len("4321") == 4? ✅ PASS
    │
    ├─ CHECK 2: Numeric only
    │  └─ "4321".isdigit()? ✅ PASS
    │
    ├─ CHECK 3: Not repeating
    │  └─ set("4321") = {'4','3','2','1'}, len = 4
    │  └─ ✅ PASS
    │
    ├─ CHECK 4: Not ascending
    │  └─ Is 4→3→2→1? No! (decreasing)
    │  └─ ✅ PASS
    │
    ├─ CHECK 5: Not descending
    │  └─ Is 4→3→2→1? Yes! (4-1=3, 3-1=2, 2-1=1)
    │  └─ All consecutive decreases? ✅ YES
    │  └─ ❌ FAIL (sequential descending)
    │
    └─ RESULT: INVALID ❌
       └─ Reason: "PIN cannot be sequential (e.g., 1234, 4321)"

8️⃣ Account Locking Mechanism

FAILED LOGIN ATTEMPTS PROGRESSION

Attempt 1:
├─ User enters wrong PIN
├─ CALL: increment_failed_attempts(user)
│  ├─ user["failed_attempts"]: 0 → 1
│  ├─ Check: 1 >= 3? No
│  └─ user["locked"]: 0 (unchanged)
├─ CALL: update_user(user)
│  └─ Database UPDATE completed
├─ RELOAD: user = get_user(acc)
├─ Calculate: remaining = 3 - 1 = 2
└─ MESSAGE: "Attempts remaining: 2"

Attempt 2:
├─ User enters wrong PIN again
├─ CALL: increment_failed_attempts(user)
│  ├─ user["failed_attempts"]: 1 → 2
│  ├─ Check: 2 >= 3? No
│  └─ user["locked"]: 0 (unchanged)
├─ CALL: update_user(user)
├─ RELOAD: user = get_user(acc)
├─ Calculate: remaining = 3 - 2 = 1
└─ MESSAGE: "Attempts remaining: 1"

Attempt 3 (WRONG PIN):
├─ User enters wrong PIN (3rd time)
├─ CALL: increment_failed_attempts(user)
│  ├─ user["failed_attempts"]: 2 → 3
│  ├─ Check: 3 >= 3? YES ✅
│  ├─ AUTO-LOCK: user["locked"] = True
│  └─ Database updated with locked = 1
├─ RELOAD: user = get_user(acc)
├─ Calculate: remaining = 3 - 3 = 0
└─ MESSAGE: "Account locked due to multiple failed attempts"
   └─ RETURN: (None, None)

Attempt 4+ (after locked):
├─ User tries to login
├─ FETCH: user = get_user(acc)
├─ CHECK: is_account_locked(user)?
│  └─ user["locked"] == 1? YES
├─ MESSAGE: "Account is locked due to multiple failed login attempts"
└─ RETURN: (None, None) immediately (no PIN attempt allowed)


ADMIN UNLOCK:
├─ Admin accesses Admin Panel
├─ Admin Panel → 1 (Unlock Account)
├─ Verify admin password
├─ Display all locked accounts
├─ Admin enters account number to unlock
├─ CALL: admin_unlock_account()
│  ├─ UPDATE users SET locked = 0, failed_attempts = 0
│  │  WHERE account_number = ?
│  └─ CALL: record_transaction(..., "ADMIN_UNLOCK", ...)
├─ Account status changes:
│  ├─ locked: 1 → 0
│  ├─ failed_attempts: 3 → 0
│  └─ Database updated
└─ USER CAN LOGIN AGAIN ✅

9️⃣ Admin Panel Menu Structure

ADMIN PANEL
    │
    ├─ 1. UNLOCK ACCOUNT
    │  ├─ Verify admin password
    │  ├─ List all locked accounts
    │  ├─ Unlock specific or all
    │  ├─ Record admin action
    │  └─ Return to admin menu
    │
    ├─ 2. VIEW ALL ACCOUNTS
    │  ├─ Verify admin password
    │  ├─ Query: SELECT account_number, name, balance, locked, failed_attempts
    │  ├─ Display in table format
    │  ├─ Show total count
    │  └─ Return to admin menu
    │
    ├─ 3. VIEW ATM CASH POOL
    │  ├─ Verify admin password
    │  ├─ Query: SELECT atm_cash_pool, daily_withdraw_limit
    │  ├─ Display amounts
    │  └─ Return to admin menu
    │
    ├─ 4. REFILL ATM CASH
    │  ├─ Verify admin password
    │  ├─ Show current ATM cash
    │  ├─ Input amount to add
    │  ├─ UPDATE settings: atm_cash_pool += amount
    │  ├─ Display new total
    │  └─ Return to admin menu
    │
    ├─ 5. VIEW RECENT TRANSACTIONS
    │  ├─ Verify admin password
    │  ├─ Input: how many to show (default 10)
    │  ├─ Query: SELECT * FROM transactions
    │  │          JOIN users
    │  │          ORDER BY id DESC LIMIT ?
    │  ├─ Display in table format
    │  └─ Return to admin menu
    │
    ├─ 6. CHANGE ADMIN PASSWORD
    │  ├─ Verify current admin password
    │  ├─ Input new password (min 8 chars)
    │  ├─ Confirm new password
    │  ├─ HASH: new_hash = hash_password(new_password)
    │  ├─ UPDATE settings: admin_password_hash = new_hash
    │  ├─ MESSAGE: "Password changed successfully"
    │  └─ Return to admin menu
    │
    └─ 7. BACK TO MAIN MENU
       └─ Exit admin menu

🔟 Complete User Journey

USER JOURNEY FLOWCHART

STEP 1: CREATE ACCOUNT
├─ Start ATM
├─ Main Menu → 2 (Create Account)
├─ Input: name = "John Doe"
├─ Input: PIN = "1357"
├─ Input: deposit = "5000"
├─ Database created:
│  └─ users: 1 row (1000000000, John Doe, ...)
│  └─ transactions: 1 row (DEPOSIT, 5000)
│  └─ Receipt file: receipt_1000000000_31122025_143045_1.txt
└─ Account ready: 1000000000

STEP 2: LOGIN
├─ Main Menu → 1 (Login)
├─ Input: account = "1000000000"
├─ Input: PIN = "1357"
├─ Validation: hash("1357") matches stored hash ✅
├─ Reset failed_attempts to 0
└─ Enter ATM Menu

STEP 3: CHECK BALANCE
├─ ATM Menu → 1 (Balance Inquiry)
├─ Fetch: balance = 5000.00
├─ Display: "Current balance: ₹5000.00"
├─ Record: transaction (BALANCE, 0, 5000)
└─ Return to ATM Menu

STEP 4: DEPOSIT MONEY
├─ ATM Menu → 2 (Deposit)
├─ Input: amount = "2000"
├─ Database transaction:
│  ├─ UPDATE users: balance 5000 → 7000
│  ├─ INSERT transactions: (DEPOSIT, 2000, 7000)
│  └─ AUTO-COMMIT
├─ Generate receipt
├─ Display: "New Balance: ₹7000.00"
└─ Return to ATM Menu

STEP 5: WITHDRAW MONEY
├─ ATM Menu → 3 (Withdraw)
├─ Input: amount = "1000"
├─ Validations:
│  ├─ Min balance: 7000 - 1000 = 6000 >= 500 ✅
│  ├─ Daily limit: 0 + 1000 <= 20000 ✅
│  └─ ATM cash: 1000000 >= 1000 ✅
├─ Database transaction:
│  ├─ UPDATE users: balance 7000 → 6000, last_withdraw_date = TODAY
│  ├─ UPDATE settings: atm_cash 1000000 → 999000
│  ├─ INSERT transactions: (WITHDRAW, 1000, 6000)
│  └─ AUTO-COMMIT
├─ Generate receipt
├─ Display: "New Balance: ₹6000.00"
└─ Return to ATM Menu

STEP 6: VIEW MINI STATEMENT
├─ ATM Menu → 4 (Mini Statement)
├─ Query: SELECT last 5 transactions
│  ├─ Transaction 5: DEPOSIT, 2000, balance 7000 (10:30 AM)
│  ├─ Transaction 4: WITHDRAW, 1000, balance 6000 (11:45 AM)
│  ├─ Transaction 3: BALANCE, 0, balance 6000 (11:42 AM)
│  ├─ Transaction 2: DEPOSIT, 5000, balance 5000 (09:15 AM)
│  └─ (first deposit when account created)
└─ Return to ATM Menu

STEP 7: LOGOUT
├─ ATM Menu → 6 (Logout)
├─ MESSAGE: "Logging out..."
├─ Return to Main Menu
└─ Can login again with same account

Data Types Usedy

STRING (TEXT):
├─ account_number: "1000000000"
├─ name: "John Doe"
├─ pin_hash: "03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f0dd..."
├─ tx_type: "WITHDRAW"
└─ datetime: "2025-12-31T14:32:10"

FLOAT (REAL):
├─ balance: 5000.50
├─ amount: 1000.00
├─ atm_cash_pool: 999000.00
└─ daily_withdraw_limit: 20000.00

INTEGER:
├─ account_number (as int internally): 1000000000
├─ locked: 0 or 1
├─ failed_attempts: 0-3
├─ transaction_id: 1, 2, 3, ...
└─ settings_id: 1

BOOLEAN (stored as INT):
├─ locked: 0 (False), 1 (True)
└─ is_account_locked: True, False

DICT (Python):
├─ user: {"account_number": "...", "name": "...", "balance": ...}
├─ settings: {"atm_cash_pool": ..., "daily_withdraw_limit": ...}
└─ Used for in-memory operations

TUPLE (Python):
├─ login(): return (account_number, user_dict)
├─ validate_pin_strength(): return (bool_valid, str_error)
└─ sanitize_name(): return (bool_valid, str_name, str_error)

OPTIONAL:
├─ get_user(): Optional[Dict[str, Any]] (dict or None)
├─ login(): Tuple[Optional[str], Optional[Dict]] (both or None)
└─ Used for nullable values

This visual guide complements the code explanation with diagrams and flowcharts! 📊✨