#!/usr/bin/env python3 """ QBO Account ID Lookup Script ============================ This script connects to QuickBooks Online and retrieves all accounts with their IDs, names, types, and other details. Usage: python get_account_ids.py [--type TYPE] [--search NAME] [--export FILE] Examples: python get_account_ids.py # List all accounts python get_account_ids.py --type Bank # List only Bank accounts python get_account_ids.py --search Checking # Search by name python get_account_ids.py --export accounts.csv # Export to CSV """ import os import sys import json import argparse from pathlib import Path from datetime import datetime # Add parent directory to path to import from src script_dir = Path(__file__).parent sys.path.insert(0, str(script_dir)) from src.config.settings import Settings from src.api.qbo_client import QBOClient def get_accounts(client, account_type=None, search_term=None): """ Fetch accounts from QBO with optional filtering. Args: client: QBOClient instance account_type: Filter by account type (Bank, Expense, Income, etc.) search_term: Search accounts by name Returns: List of account dictionaries """ # Build query query = "SELECT * FROM Account" conditions = [] if account_type: conditions.append(f"AccountType = '{account_type}'") if search_term: conditions.append(f"Name LIKE '%{search_term}%'") if conditions: query += " WHERE " + " AND ".join(conditions) query += " ORDERBY Name" # Execute query response = client._make_request('GET', 'query', params={'query': query}) if response and 'QueryResponse' in response: return response['QueryResponse'].get('Account', []) return [] def format_account_table(accounts): """Format accounts as a readable table.""" if not accounts: return "No accounts found." # Define columns headers = ['ID', 'Name', 'Type', 'SubType', 'AcctNum', 'Balance', 'Active'] # Calculate column widths widths = { 'ID': max(len('ID'), max(len(str(a.get('Id', ''))) for a in accounts)), 'Name': max(len('Name'), min(40, max(len(str(a.get('Name', ''))) for a in accounts))), 'Type': max(len('Type'), max(len(str(a.get('AccountType', ''))) for a in accounts)), 'SubType': max(len('SubType'), max(len(str(a.get('AccountSubType', ''))) for a in accounts)), 'AcctNum': max(len('AcctNum'), max(len(str(a.get('AcctNum', ''))) for a in accounts)), 'Balance': max(len('Balance'), 12), 'Active': max(len('Active'), 6), } # Build header header_row = " | ".join(h.ljust(widths[h]) for h in headers) separator = "-+-".join("-" * widths[h] for h in headers) lines = [header_row, separator] # Build data rows for account in accounts: name = str(account.get('Name', ''))[:40] balance = account.get('CurrentBalance', 0) balance_str = f"${balance:,.2f}" if balance else "-" row = [ str(account.get('Id', '')).ljust(widths['ID']), name.ljust(widths['Name']), str(account.get('AccountType', '')).ljust(widths['Type']), str(account.get('AccountSubType', '')).ljust(widths['SubType']), str(account.get('AcctNum', '')).ljust(widths['AcctNum']), balance_str.rjust(widths['Balance']), ('Yes' if account.get('Active', True) else 'No').ljust(widths['Active']), ] lines.append(" | ".join(row)) return "\n".join(lines) def export_to_csv(accounts, filepath): """Export accounts to CSV file.""" import csv headers = ['Id', 'Name', 'FullyQualifiedName', 'AccountType', 'AccountSubType', 'AcctNum', 'CurrentBalance', 'Active', 'Classification', 'Description'] with open(filepath, 'w', newline='', encoding='utf-8') as f: writer = csv.DictWriter(f, fieldnames=headers, extrasaction='ignore') writer.writeheader() for account in accounts: writer.writerow(account) print(f"Exported {len(accounts)} accounts to {filepath}") def export_to_json(accounts, filepath): """Export accounts to JSON file.""" with open(filepath, 'w', encoding='utf-8') as f: json.dump(accounts, f, indent=2) print(f"Exported {len(accounts)} accounts to {filepath}") def print_account_types(accounts): """Print summary of account types.""" type_counts = {} for account in accounts: acct_type = account.get('AccountType', 'Unknown') type_counts[acct_type] = type_counts.get(acct_type, 0) + 1 print("\nAccount Types Summary:") print("-" * 30) for acct_type, count in sorted(type_counts.items()): print(f" {acct_type}: {count}") print("-" * 30) print(f" Total: {len(accounts)}") def main(): parser = argparse.ArgumentParser( description='Fetch Account IDs from QuickBooks Online', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Account Types: Bank - Bank accounts (checking, savings) Accounts Receivable - Customer receivables Other Current Asset - Other current assets Fixed Asset - Property, equipment, etc. Other Asset - Other long-term assets Accounts Payable - Vendor payables Credit Card - Credit card accounts Other Current Liability - Other current liabilities Long Term Liability - Long-term debt Equity - Owner's equity accounts Income - Revenue accounts Cost of Goods Sold - COGS accounts Expense - Expense accounts Other Income - Other income Other Expense - Other expenses Examples: %(prog)s --type Bank %(prog)s --type Expense --search Office %(prog)s --export accounts.csv %(prog)s --type "Accounts Payable" """ ) parser.add_argument('--type', '-t', dest='account_type', help='Filter by account type (e.g., Bank, Expense, Income)') parser.add_argument('--search', '-s', dest='search_term', help='Search accounts by name') parser.add_argument('--export', '-e', dest='export_file', help='Export to file (CSV or JSON based on extension)') parser.add_argument('--summary', action='store_true', help='Show account type summary') parser.add_argument('--json', action='store_true', help='Output as JSON') parser.add_argument('--verbose', '-v', action='store_true', help='Show detailed account information') args = parser.parse_args() # Initialize settings and client print("=" * 60) print("QBO Account ID Lookup") print("=" * 60) try: settings = Settings() credentials = settings.get_credentials() if not credentials.access_token: print("\nError: Not connected to QuickBooks Online.") print("Please connect via the web application first.") sys.exit(1) client = QBOClient(credentials) if not client.is_authenticated: print("\nError: Authentication failed or token expired.") print("Please reconnect via the web application.") sys.exit(1) # Get company info company_info = client.get_company_info() print(f"\nConnected to: {company_info.get('CompanyName', 'Unknown')}") print(f"Environment: {credentials.environment}") print("-" * 60) # Fetch accounts print(f"\nFetching accounts...") if args.account_type: print(f" Filter by type: {args.account_type}") if args.search_term: print(f" Search term: {args.search_term}") accounts = get_accounts(client, args.account_type, args.search_term) print(f"\nFound {len(accounts)} account(s)") print("-" * 60) if not accounts: print("No accounts match your criteria.") sys.exit(0) # Output results if args.json: print(json.dumps(accounts, indent=2)) elif args.verbose: for i, account in enumerate(accounts, 1): print(f"\n[{i}] {account.get('Name')}") print(f" ID: {account.get('Id')}") print(f" Type: {account.get('AccountType')} / {account.get('AccountSubType', '-')}") print(f" Number: {account.get('AcctNum', '-')}") print(f" Balance: ${account.get('CurrentBalance', 0):,.2f}") print(f" Active: {account.get('Active', True)}") if account.get('Description'): print(f" Description: {account.get('Description')}") else: print(format_account_table(accounts)) # Show summary if requested if args.summary: print_account_types(accounts) # Export if requested if args.export_file: filepath = args.export_file if filepath.endswith('.json'): export_to_json(accounts, filepath) else: export_to_csv(accounts, filepath) print("\n" + "=" * 60) print("Done!") except Exception as e: print(f"\nError: {str(e)}") import traceback traceback.print_exc() sys.exit(1) if __name__ == '__main__': main()