mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2025-12-30 15:49:44 -06:00
This commit addresses multiple issues with the Admin Settings page and improves PDF invoice logo embedding for better cross-platform reliability. ## Admin Settings UI - Missing Fields Fixed The Admin → Settings page was incomplete, showing only basic timer and regional settings. Added all missing sections: - User Management: Self-registration toggle with admin username note - Company Branding: Full company info fields (name, email, phone, website, address, tax ID, bank info) plus logo upload with preview - Invoice Defaults: Prefix, start number, payment terms, and notes - Backup Settings: Retention days and backup time configuration - Export Settings: CSV delimiter preference selector - Privacy & Analytics: Telemetry opt-in with detailed privacy information The backend was already handling these fields - this was purely a frontend template issue where form fields were missing. ## Analytics/Telemetry Preference Synchronization Fixed critical bug where analytics checkbox in Admin Settings only updated the database but not the InstallationConfig file that the telemetry system actually reads from. Changes now properly sync both systems: - On page load: Auto-sync database from InstallationConfig (source of truth) - On save: Update both database AND InstallationConfig simultaneously - Added logging for analytics preference changes - Updated UI references: Initial setup and Telemetry dashboard now point to Admin → Settings as the primary location - Added clear privacy information explaining what data is collected ## PDF Logo Embedding Enhancement Improved logo reliability in PDF invoices by switching from file:// URIs to base64 data URIs: - More reliable across platforms (Windows, Linux, macOS) - Works consistently in Docker containers - Self-contained (no filesystem path dependencies) - Automatic MIME type detection for all formats (PNG, JPG, GIF, SVG, WEBP) - Graceful fallback to file:// URI if base64 fails - Added comprehensive debug logging for troubleshooting ## Diagnostic Tools & Documentation - Created test_logo_pdf.py: Diagnostic script to identify logo issues - Created LOGO_PDF_TROUBLESHOOTING.md: Comprehensive troubleshooting guide - Enhanced error messages with debug output throughout logo processing - Added context passing fixes for PDF template rendering ## Files Changed ### Core Fixes - app/templates/admin/settings.html: Complete rewrite with all sections - app/routes/admin.py: InstallationConfig sync for analytics preference - app/static/uploads/logos/.gitkeep: Ensure logos directory tracked by git ### PDF Logo Enhancement - app/utils/pdf_generator.py: Base64 encoding + explicit context passing - app/utils/template_filters.py: get_logo_base64() helper with debug logging - app/templates/invoices/pdf_default.html: Base64 logo embedding ### Analytics Synchronization - app/templates/setup/initial_setup.html: Updated settings reference - app/templates/admin/telemetry.html: Cross-reference to Admin → Settings ### Documentation - docs/GETTING_STARTED.md: Updated to reflect actual UI behavior - test_logo_pdf.py: New diagnostic script - LOGO_PDF_TROUBLESHOOTING.md: New troubleshooting guide ## Testing Run diagnostic script to verify logo configuration:
195 lines
6.5 KiB
Python
195 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Debug script to test company logo in PDF generation
|
|
Run this to check if logo is properly configured and can be embedded in PDFs
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
# Add app to path
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from app import create_app
|
|
from app.models import Settings, Invoice
|
|
from app.utils.pdf_generator import InvoicePDFGenerator
|
|
|
|
def test_logo_setup():
|
|
"""Test if logo is properly configured"""
|
|
print("=" * 60)
|
|
print("LOGO CONFIGURATION TEST")
|
|
print("=" * 60)
|
|
|
|
app = create_app()
|
|
with app.app_context():
|
|
settings = Settings.get_settings()
|
|
|
|
print(f"\n1. Logo filename in database: {settings.company_logo_filename or 'NONE'}")
|
|
|
|
if not settings.company_logo_filename:
|
|
print(" ❌ NO LOGO UPLOADED")
|
|
print(" → Upload a logo in Admin → Settings → Company Branding")
|
|
return False
|
|
|
|
print(f" ✓ Logo filename found: {settings.company_logo_filename}")
|
|
|
|
logo_path = settings.get_logo_path()
|
|
print(f"\n2. Logo file path: {logo_path}")
|
|
|
|
if not logo_path:
|
|
print(" ❌ Could not determine logo path")
|
|
return False
|
|
|
|
if not os.path.exists(logo_path):
|
|
print(f" ❌ LOGO FILE DOES NOT EXIST AT: {logo_path}")
|
|
print(f" → Check if file exists in app/static/uploads/logos/")
|
|
return False
|
|
|
|
print(f" ✓ Logo file exists")
|
|
|
|
file_size = os.path.getsize(logo_path)
|
|
print(f"\n3. Logo file size: {file_size:,} bytes ({file_size/1024:.2f} KB)")
|
|
|
|
if file_size == 0:
|
|
print(" ❌ Logo file is empty")
|
|
return False
|
|
|
|
if file_size > 5 * 1024 * 1024: # 5MB
|
|
print(" ⚠️ Logo file is very large (>5MB). Consider optimizing.")
|
|
|
|
print(f" ✓ Logo file has content")
|
|
|
|
# Test base64 encoding
|
|
print(f"\n4. Testing base64 encoding...")
|
|
try:
|
|
from app.utils.template_filters import get_logo_base64
|
|
data_uri = get_logo_base64(logo_path)
|
|
|
|
if not data_uri:
|
|
print(" ❌ Base64 encoding failed (returned None)")
|
|
return False
|
|
|
|
if not data_uri.startswith('data:image/'):
|
|
print(f" ❌ Invalid data URI: {data_uri[:50]}...")
|
|
return False
|
|
|
|
encoded_size = len(data_uri)
|
|
print(f" ✓ Base64 encoding successful")
|
|
print(f" Data URI size: {encoded_size:,} bytes ({encoded_size/1024:.2f} KB)")
|
|
print(f" Data URI prefix: {data_uri[:50]}...")
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Error encoding logo: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
print("\n" + "=" * 60)
|
|
print("✓ LOGO CONFIGURATION IS CORRECT")
|
|
print("=" * 60)
|
|
return True
|
|
|
|
def test_pdf_generation():
|
|
"""Test PDF generation with logo"""
|
|
print("\n" + "=" * 60)
|
|
print("PDF GENERATION TEST")
|
|
print("=" * 60)
|
|
|
|
app = create_app()
|
|
with app.app_context():
|
|
# Get the most recent invoice
|
|
invoice = Invoice.query.order_by(Invoice.id.desc()).first()
|
|
|
|
if not invoice:
|
|
print("\n❌ NO INVOICES FOUND")
|
|
print(" → Create an invoice first to test PDF generation")
|
|
return False
|
|
|
|
print(f"\n1. Testing with Invoice #{invoice.invoice_number}")
|
|
print(f" Project: {invoice.project.name}")
|
|
print(f" Client: {invoice.client_name}")
|
|
|
|
try:
|
|
print(f"\n2. Generating PDF...")
|
|
generator = InvoicePDFGenerator(invoice)
|
|
pdf_bytes = generator.generate_pdf()
|
|
|
|
if not pdf_bytes:
|
|
print(" ❌ PDF generation returned no data")
|
|
return False
|
|
|
|
pdf_size = len(pdf_bytes)
|
|
print(f" ✓ PDF generated successfully")
|
|
print(f" PDF size: {pdf_size:,} bytes ({pdf_size/1024:.2f} KB)")
|
|
|
|
# Save PDF for inspection
|
|
output_file = 'test_invoice.pdf'
|
|
with open(output_file, 'wb') as f:
|
|
f.write(pdf_bytes)
|
|
|
|
print(f"\n3. PDF saved to: {output_file}")
|
|
print(f" → Open this file to check if logo appears")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("✓ PDF GENERATION SUCCESSFUL")
|
|
print("=" * 60)
|
|
print(f"\n📄 Check the file: {output_file}")
|
|
print(" Look for the logo in the top-left corner of the invoice")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"\n ❌ Error generating PDF: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
def main():
|
|
"""Run all tests"""
|
|
print("\n🔍 TimeTracker PDF Logo Diagnostic Tool\n")
|
|
|
|
logo_ok = test_logo_setup()
|
|
|
|
if logo_ok:
|
|
pdf_ok = test_pdf_generation()
|
|
|
|
if pdf_ok:
|
|
print("\n" + "=" * 60)
|
|
print("✅ ALL TESTS PASSED")
|
|
print("=" * 60)
|
|
print("\nIf the logo still doesn't appear in the PDF:")
|
|
print("1. Open test_invoice.pdf and check manually")
|
|
print("2. Check server logs for DEBUG messages when generating invoices")
|
|
print("3. Try uploading a different logo (simple PNG <1MB)")
|
|
print("4. Verify the logo works in the web UI first (Admin → Settings)")
|
|
return 0
|
|
else:
|
|
print("\n" + "=" * 60)
|
|
print("❌ PDF GENERATION FAILED")
|
|
print("=" * 60)
|
|
return 1
|
|
else:
|
|
print("\n" + "=" * 60)
|
|
print("❌ LOGO CONFIGURATION FAILED")
|
|
print("=" * 60)
|
|
print("\nPlease fix the logo configuration first:")
|
|
print("1. Login as admin")
|
|
print("2. Go to Admin → Settings")
|
|
print("3. Scroll to Company Branding section")
|
|
print("4. Upload a logo (PNG, JPG, GIF, SVG, or WEBP)")
|
|
print("5. Run this script again")
|
|
return 1
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
sys.exit(main())
|
|
except KeyboardInterrupt:
|
|
print("\n\nTest interrupted by user")
|
|
sys.exit(130)
|
|
except Exception as e:
|
|
print(f"\n\n❌ UNEXPECTED ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|