swiftops-backend / scripts /test_invoice_generation.py
kamau1's picture
Implement full end-to-end invoice generation & public viewing system (migrations, models, schemas, services, routes)
93efe54
"""
Invoice Generation System - Test Script
This script tests the complete invoice generation flow:
1. Get available tickets
2. Generate invoice
3. View invoice (public)
4. Export CSV
5. Regenerate token
Usage:
python scripts/test_invoice_generation.py
"""
import requests
import json
import sys
from datetime import datetime
# Configuration
BASE_URL = "http://localhost:8000/api/v1"
AUTH_TOKEN = "YOUR_AUTH_TOKEN_HERE" # Replace with actual token
CONTRACTOR_ID = "YOUR_CONTRACTOR_ID" # Replace with actual UUID
CLIENT_ID = "YOUR_CLIENT_ID" # Replace with actual UUID
PROJECT_ID = "YOUR_PROJECT_ID" # Replace with actual UUID
HEADERS = {
"Authorization": f"Bearer {AUTH_TOKEN}",
"Content-Type": "application/json"
}
def print_section(title):
"""Print section header"""
print("\n" + "=" * 60)
print(f" {title}")
print("=" * 60)
def print_success(message):
"""Print success message"""
print(f"βœ… {message}")
def print_error(message):
"""Print error message"""
print(f"❌ {message}")
def print_info(message):
"""Print info message"""
print(f"ℹ️ {message}")
def test_get_available_tickets():
"""Test getting available tickets"""
print_section("1. Get Available Tickets")
try:
response = requests.get(
f"{BASE_URL}/invoices/available-tickets",
params={
"contractor_id": CONTRACTOR_ID,
"project_id": PROJECT_ID
},
headers=HEADERS
)
if response.status_code == 200:
data = response.json()
tickets = data.get("tickets", [])
print_success(f"Found {len(tickets)} available tickets")
if tickets:
print_info("Sample ticket:")
ticket = tickets[0]
print(f" - ID: {ticket['id']}")
print(f" - Name: {ticket.get('ticket_name', 'N/A')}")
print(f" - Type: {ticket['ticket_type']}")
print(f" - Images: {ticket['images_count']}")
print(f" - Sales Order: {ticket.get('sales_order_number', 'N/A')}")
return [t['id'] for t in tickets[:2]] # Return first 2 ticket IDs
else:
print_error("No available tickets found")
print_info("Create some completed tickets first:")
print(" UPDATE tickets SET status='completed', completed_at=NOW(), is_invoiced=false WHERE id='YOUR_TICKET_ID';")
return None
else:
print_error(f"Failed: {response.status_code}")
print(response.text)
return None
except Exception as e:
print_error(f"Exception: {str(e)}")
return None
def test_generate_invoice(ticket_ids):
"""Test generating invoice"""
print_section("2. Generate Invoice")
if not ticket_ids:
print_error("No ticket IDs provided")
return None
try:
payload = {
"contractor_id": CONTRACTOR_ID,
"client_id": CLIENT_ID,
"project_id": PROJECT_ID,
"ticket_ids": ticket_ids,
"title": f"Test Invoice - {datetime.now().strftime('%Y-%m-%d %H:%M')}",
"notes": "This is a test invoice generated by the test script"
}
response = requests.post(
f"{BASE_URL}/invoices/generate",
headers=HEADERS,
json=payload
)
if response.status_code == 200:
data = response.json()
print_success("Invoice generated successfully")
print(f" - Invoice ID: {data['invoice_id']}")
print(f" - Invoice Number: {data['invoice_number']}")
print(f" - Tickets: {data['tickets_count']}")
print(f" - Viewing Link: {data['viewing_link']}")
print(f" - CSV Link: {data['csv_download_link']}")
print(f" - Notification Created: {data['notification_created']}")
return data
else:
print_error(f"Failed: {response.status_code}")
print(response.text)
return None
except Exception as e:
print_error(f"Exception: {str(e)}")
return None
def test_view_invoice(viewing_link):
"""Test viewing invoice (public - no auth)"""
print_section("3. View Invoice (Public)")
if not viewing_link:
print_error("No viewing link provided")
return False
try:
# Extract token from link
token = viewing_link.split("token=")[1] if "token=" in viewing_link else None
if not token:
print_error("Could not extract token from link")
return False
response = requests.get(
f"{BASE_URL}/invoices/view",
params={"token": token}
)
if response.status_code == 200:
data = response.json()
invoice = data.get("invoice", {})
print_success("Invoice viewed successfully")
print(f" - Invoice Number: {invoice.get('invoice_number')}")
print(f" - Line Items: {len(invoice.get('line_items', []))}")
print(f" - Times Viewed: {data.get('times_viewed')}")
print(f" - Token Expires: {data.get('token_expires_at')}")
# Check if ticket details are enriched
line_items = invoice.get('line_items', [])
if line_items:
first_item = line_items[0]
if 'ticket_details' in first_item:
details = first_item['ticket_details']
print_info("Ticket details enriched:")
print(f" - Images: {details.get('images_count', 0)}")
print(f" - Completion Data: {bool(details.get('completion_data'))}")
print(f" - Location: {bool(details.get('work_location'))}")
return True
else:
print_error(f"Failed: {response.status_code}")
print(response.text)
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def test_export_csv(invoice_id):
"""Test CSV export"""
print_section("4. Export CSV")
if not invoice_id:
print_error("No invoice ID provided")
return False
try:
response = requests.get(
f"{BASE_URL}/invoices/{invoice_id}/export/csv",
headers=HEADERS
)
if response.status_code == 200:
# Save CSV to file
filename = f"invoice_{invoice_id}.csv"
with open(filename, "wb") as f:
f.write(response.content)
print_success(f"CSV exported successfully: {filename}")
# Show first few lines
lines = response.content.decode('utf-8').split('\n')
print_info("CSV Preview (first 3 lines):")
for i, line in enumerate(lines[:3]):
print(f" {i+1}: {line[:100]}...")
return True
else:
print_error(f"Failed: {response.status_code}")
print(response.text)
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def test_regenerate_token(invoice_id):
"""Test token regeneration"""
print_section("5. Regenerate Token")
if not invoice_id:
print_error("No invoice ID provided")
return False
try:
response = requests.post(
f"{BASE_URL}/invoices/{invoice_id}/regenerate-token",
headers=HEADERS,
json={"expires_in_days": 30}
)
if response.status_code == 200:
data = response.json()
print_success("Token regenerated successfully")
print(f" - New Link: {data['viewing_link']}")
print(f" - Expires At: {data['expires_at']}")
return True
else:
print_error(f"Failed: {response.status_code}")
print(response.text)
return False
except Exception as e:
print_error(f"Exception: {str(e)}")
return False
def main():
"""Run all tests"""
print("\n" + "πŸš€" * 30)
print(" Invoice Generation System - Test Script")
print("πŸš€" * 30)
# Check configuration
if AUTH_TOKEN == "YOUR_AUTH_TOKEN_HERE":
print_error("Please configure AUTH_TOKEN in the script")
sys.exit(1)
if CONTRACTOR_ID == "YOUR_CONTRACTOR_ID":
print_error("Please configure CONTRACTOR_ID in the script")
sys.exit(1)
# Run tests
ticket_ids = test_get_available_tickets()
if ticket_ids:
invoice_data = test_generate_invoice(ticket_ids)
if invoice_data:
test_view_invoice(invoice_data['viewing_link'])
test_export_csv(invoice_data['invoice_id'])
test_regenerate_token(invoice_data['invoice_id'])
# Summary
print_section("Test Summary")
print("βœ… All tests completed")
print("\nNext steps:")
print("1. Check notifications in the database")
print("2. Verify tickets are marked as invoiced")
print("3. Review audit logs")
print("4. Test the viewing link in a browser")
print("\n" + "πŸŽ‰" * 30 + "\n")
if __name__ == "__main__":
main()