188 lines
5.6 KiB
Python
188 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
QuickBooks Desktop Connection Test Script
|
|
Run this directly on your Windows machine to test the connection.
|
|
|
|
Usage:
|
|
python test_qbd_connection.py
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
|
|
print("=" * 60)
|
|
print("QuickBooks Desktop Connection Test")
|
|
print("=" * 60)
|
|
|
|
# Check platform
|
|
print(f"\n1. Platform: {sys.platform}")
|
|
if sys.platform != 'win32':
|
|
print(" ERROR: This script must be run on Windows!")
|
|
sys.exit(1)
|
|
print(" OK: Running on Windows")
|
|
|
|
# Check pywin32
|
|
print("\n2. Checking pywin32...")
|
|
try:
|
|
import pythoncom
|
|
from win32com.client import Dispatch
|
|
print(" OK: pywin32 is installed")
|
|
except ImportError as e:
|
|
print(f" ERROR: pywin32 not installed: {e}")
|
|
print(" Run: pip install pywin32")
|
|
sys.exit(1)
|
|
|
|
# Initialize COM
|
|
print("\n3. Initializing COM...")
|
|
try:
|
|
pythoncom.CoInitialize()
|
|
print(" OK: COM initialized")
|
|
except Exception as e:
|
|
print(f" ERROR: Failed to initialize COM: {e}")
|
|
sys.exit(1)
|
|
|
|
# Create RequestProcessor
|
|
print("\n4. Creating QBXMLRP2.RequestProcessor...")
|
|
try:
|
|
rp = Dispatch("QBXMLRP2.RequestProcessor")
|
|
print(" OK: RequestProcessor created")
|
|
except Exception as e:
|
|
print(f" ERROR: Failed to create RequestProcessor: {e}")
|
|
print("\n This usually means:")
|
|
print(" - QuickBooks Desktop is not installed")
|
|
print(" - QuickBooks SDK components are not registered")
|
|
print(" - Try: regsvr32 \"C:\\Program Files (x86)\\Common Files\\Intuit\\QuickBooks\\qbxmlrp2.dll\"")
|
|
pythoncom.CoUninitialize()
|
|
sys.exit(1)
|
|
|
|
# Open Connection
|
|
print("\n5. Opening connection (OpenConnection2)...")
|
|
try:
|
|
# Connection types:
|
|
# 1 = localQBD (local QuickBooks Desktop)
|
|
# 2 = remoteQBD (remote QuickBooks Desktop)
|
|
# 3 = localQBDLaunchUI (launch QB if not running)
|
|
rp.OpenConnection2("", "QBD Test Script", 1)
|
|
print(" OK: Connection opened")
|
|
except Exception as e:
|
|
print(f" ERROR: OpenConnection2 failed: {e}")
|
|
pythoncom.CoUninitialize()
|
|
sys.exit(1)
|
|
|
|
# Begin Session - THIS IS WHERE IT USUALLY HANGS
|
|
print("\n6. Beginning session (BeginSession)...")
|
|
print(" NOTE: If this hangs, check QuickBooks for an authorization dialog!")
|
|
print(" Also check: Edit > Preferences > Integrated Applications > Company Preferences")
|
|
print(" Make sure 'Don't allow any applications...' is UNCHECKED")
|
|
print("")
|
|
print(" Attempting BeginSession with timeout indicator...")
|
|
print(" (If you see dots appearing, the call is hanging)")
|
|
|
|
# Start time
|
|
start_time = time.time()
|
|
|
|
# We can't easily timeout a COM call, but we can show progress
|
|
import threading
|
|
|
|
stop_dots = False
|
|
def print_dots():
|
|
count = 0
|
|
while not stop_dots:
|
|
time.sleep(1)
|
|
count += 1
|
|
print(f" ... waiting {count}s", end='\r')
|
|
if count > 30:
|
|
print("\n WARNING: BeginSession is taking too long (>30s)")
|
|
print(" This usually means QuickBooks is waiting for user input")
|
|
print(" CHECK QUICKBOOKS FOR A DIALOG BOX!")
|
|
|
|
dot_thread = threading.Thread(target=print_dots)
|
|
dot_thread.daemon = True
|
|
dot_thread.start()
|
|
|
|
try:
|
|
# File modes:
|
|
# 0 = qbFileOpenDoNotCare
|
|
# 1 = qbFileOpenSingleUser
|
|
# 2 = qbFileOpenMultiUser
|
|
ticket = rp.BeginSession("", 0) # Empty string = currently open file
|
|
stop_dots = True
|
|
elapsed = time.time() - start_time
|
|
print(f"\n OK: Session started in {elapsed:.2f}s")
|
|
print(f" Session ticket: {ticket[:30]}...")
|
|
except Exception as e:
|
|
stop_dots = True
|
|
elapsed = time.time() - start_time
|
|
print(f"\n ERROR: BeginSession failed after {elapsed:.2f}s: {e}")
|
|
|
|
error_msg = str(e)
|
|
if "80040408" in error_msg:
|
|
print("\n DIAGNOSIS: QuickBooks is not running or no company file is open")
|
|
elif "80040416" in error_msg or "80040417" in error_msg:
|
|
print("\n DIAGNOSIS: Access denied - need to authorize in QuickBooks")
|
|
elif "80040422" in error_msg:
|
|
print("\n DIAGNOSIS: No company file is open")
|
|
elif "80040423" in error_msg:
|
|
print("\n DIAGNOSIS: Company file is in single-user mode by another user")
|
|
|
|
try:
|
|
rp.CloseConnection()
|
|
except:
|
|
pass
|
|
pythoncom.CoUninitialize()
|
|
sys.exit(1)
|
|
|
|
# If we got here, try a simple query
|
|
print("\n7. Testing with a simple Company query...")
|
|
try:
|
|
# Build a simple QBXML request
|
|
request = '''<?xml version="1.0" encoding="utf-8"?>
|
|
<?qbxml version="13.0"?>
|
|
<QBXML>
|
|
<QBXMLMsgsRq onError="stopOnError">
|
|
<CompanyQueryRq>
|
|
</CompanyQueryRq>
|
|
</QBXMLMsgsRq>
|
|
</QBXML>'''
|
|
|
|
response = rp.ProcessRequest(ticket, request)
|
|
print(" OK: Query executed successfully")
|
|
|
|
# Parse response to get company name
|
|
import xml.etree.ElementTree as ET
|
|
root = ET.fromstring(response)
|
|
company_name = root.find(".//CompanyName")
|
|
if company_name is not None:
|
|
print(f" Company Name: {company_name.text}")
|
|
|
|
except Exception as e:
|
|
print(f" ERROR: Query failed: {e}")
|
|
|
|
# End session
|
|
print("\n8. Ending session...")
|
|
try:
|
|
rp.EndSession(ticket)
|
|
print(" OK: Session ended")
|
|
except Exception as e:
|
|
print(f" WARNING: EndSession failed: {e}")
|
|
|
|
# Close connection
|
|
print("\n9. Closing connection...")
|
|
try:
|
|
rp.CloseConnection()
|
|
print(" OK: Connection closed")
|
|
except Exception as e:
|
|
print(f" WARNING: CloseConnection failed: {e}")
|
|
|
|
# Uninitialize COM
|
|
try:
|
|
pythoncom.CoUninitialize()
|
|
except:
|
|
pass
|
|
|
|
print("\n" + "=" * 60)
|
|
print("TEST COMPLETED SUCCESSFULLY!")
|
|
print("=" * 60)
|
|
print("\nQuickBooks Desktop connection is working properly.")
|
|
print("If the web app still doesn't work, the issue may be with")
|
|
print("Flask session handling or threading.") |