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)
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)
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!"
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)
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 ✅
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"
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)"
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 ✅
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
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
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! 📊✨