#!/usr/bin/env python3 """ FJCCV Donation Receipt Generator Generates professional PDF receipts for church donations. """ import argparse import os import sys from pathlib import Path from datetime import datetime try: from reportlab.lib.pagesizes import letter from reportlab.lib.units import inch from reportlab.pdfgen import canvas from reportlab.lib.colors import HexColor from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont except ImportError: print("ERROR: reportlab not installed. Run: pip3 install reportlab --break-system-packages") sys.exit(1) # FJCCV brand colors FJCCV_PURPLE = HexColor('#6A0DAD') # Deep purple # Church info CHURCH_NAME = "Fellowship of Jesus Christ in California Valley" CHURCH_EIN = "33-4360898" BIBLE_VERSE = '"Each one must do just as he has purposed in his heart, not grudgingly or under compulsion, for God loves a cheerful giver."' BIBLE_REF = "2 Corinthians 9:7 (NASB 1995)" def register_fonts(base_dir: Path) -> bool: """Register FJCCV Serif font family if available""" fonts_dir = base_dir / "assets" fonts = { 'FJCCVSerif': 'FJCCVSerif-Regular.ttf', 'FJCCVSerif-Bold': 'FJCCVSerif-Bold.ttf', 'FJCCVSerif-Italic': 'FJCCVSerif-Italic.ttf', 'FJCCVSerif-BoldItalic': 'FJCCVSerif-BoldItalic.ttf', } all_found = True for font_name, font_file in fonts.items(): font_path = fonts_dir / font_file if font_path.exists(): try: pdfmetrics.registerFont(TTFont(font_name, str(font_path))) except Exception as e: print(f"Warning: Could not register {font_name}: {e}") all_found = False else: print(f"Warning: Font file not found: {font_file}") all_found = False return all_found def generate_receipt(donor: str, amount: float, date_str: str, donation_number: str, output_path: Path, transaction: str = None, payment_method: str = None, base_dir: Path = None) -> bool: """Generate a donation receipt PDF""" # Setup fonts fonts_available = False if base_dir: fonts_available = register_fonts(base_dir) if fonts_available: font_regular = 'FJCCVSerif' font_bold = 'FJCCVSerif-Bold' font_italic = 'FJCCVSerif-Italic' font_bold_italic = 'FJCCVSerif-BoldItalic' else: print("Using fallback fonts (Times-Roman)") font_regular = 'Times-Roman' font_bold = 'Times-Bold' font_italic = 'Times-Italic' font_bold_italic = 'Times-BoldItalic' # Create PDF c = canvas.Canvas(str(output_path), pagesize=letter) width, height = letter # Add letterhead if available if base_dir: letterhead_path = base_dir / "assets" / "fjccv_letterhead.png" if letterhead_path.exists(): # Full width letterhead at top c.drawImage(str(letterhead_path), 0, height - 1.5*inch, width=width, height=1.5*inch, preserveAspectRatio=True) y_position = height - 2.5*inch else: print("Warning: Letterhead image not found, skipping") y_position = height - 1.5*inch else: y_position = height - 1.5*inch # Title c.setFont(font_bold, 24) c.setFillColor(FJCCV_PURPLE) c.drawCentredString(width/2, y_position, "Donation Receipt") y_position -= 0.7*inch # Reset to black for body text c.setFillColor('black') # Receipt details c.setFont(font_regular, 12) left_margin = 1.25*inch line_height = 0.3*inch # Date c.drawString(left_margin, y_position, "Date:") c.setFont(font_bold, 12) c.drawString(left_margin + 2*inch, y_position, date_str) y_position -= line_height # Donation number c.setFont(font_regular, 12) c.drawString(left_margin, y_position, "Donation Number:") c.setFont(font_bold, 12) c.drawString(left_margin + 2*inch, y_position, donation_number) y_position -= line_height # Donor name c.setFont(font_regular, 12) c.drawString(left_margin, y_position, "Received from:") c.setFont(font_bold, 12) c.drawString(left_margin + 2*inch, y_position, donor) y_position -= line_height # Amount c.setFont(font_regular, 12) c.drawString(left_margin, y_position, "Amount:") c.setFont(font_bold, 14) c.drawString(left_margin + 2*inch, y_position, f"${amount:,.2f}") y_position -= line_height # Payment method (if provided) if payment_method: c.setFont(font_regular, 12) c.drawString(left_margin, y_position, "Payment Method:") c.setFont(font_bold, 12) c.drawString(left_margin + 2*inch, y_position, payment_method) y_position -= line_height # Transaction ID (if provided) if transaction: c.setFont(font_regular, 12) c.drawString(left_margin, y_position, "Transaction ID:") c.setFont(font_regular, 10) c.drawString(left_margin + 2*inch, y_position, transaction) y_position -= line_height y_position -= 0.3*inch # Bible verse (centered, italic, wrapped, purple) c.setFont(font_bold_italic, 11) c.setFillColor(FJCCV_PURPLE) # Wrap the verse manually for better readability verse_lines = [ '"Each one must do just as he has purposed in his heart,', 'not grudgingly or under compulsion,', 'for God loves a cheerful giver."', f"— {BIBLE_REF}" ] for line in verse_lines: c.drawCentredString(width/2, y_position, line) y_position -= 0.25*inch # Reset to black for remaining text c.setFillColor('black') y_position -= 0.3*inch # Tax statement c.setFont(font_regular, 10) tax_lines = [ f"{CHURCH_NAME} is a 508(c)(1)(a) church organization.", "No goods or services were provided in exchange for this donation.", "This receipt may be used for tax deduction purposes as allowed by law." ] for line in tax_lines: c.drawCentredString(width/2, y_position, line) y_position -= 0.22*inch y_position -= 0.5*inch # Closing c.setFont(font_regular, 11) c.drawString(left_margin, y_position, "Sincerely,") y_position -= 0.25*inch c.setFont(font_bold, 11) c.drawString(left_margin, y_position, CHURCH_NAME) y_position -= 0.2*inch c.setFont(font_regular, 10) c.drawString(left_margin, y_position, f"EIN: {CHURCH_EIN}") # Footer with generation timestamp c.setFont(font_regular, 8) c.setFillColor('gray') footer_text = f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" c.drawCentredString(width/2, 0.5*inch, footer_text) # Save PDF c.save() return True def main(): parser = argparse.ArgumentParser(description='Generate FJCCV donation receipt PDF') parser.add_argument('--donor', required=True, help='Full name of donor') parser.add_argument('--amount', required=True, type=float, help='Donation amount (numeric)') parser.add_argument('--date', required=True, help='Date of donation (MM/DD/YY)') parser.add_argument('--donation-number', required=True, help='Donation number (LDDDDDD-YYYY)') parser.add_argument('--transaction', help='Transaction ID (optional)') parser.add_argument('--payment-method', help='Payment method (optional)') parser.add_argument('--output-dir', default=os.path.expanduser('~/fjccv-receipts'), help='Output directory for PDF (default: ~/fjccv-receipts)') args = parser.parse_args() # Determine base directory (where this script lives) script_dir = Path(__file__).parent base_dir = script_dir.parent # Go up to skill root # Create output directory output_dir = Path(args.output_dir) output_dir.mkdir(parents=True, exist_ok=True) # Generate filename in format YYYY_LNNN.pdf (e.g., 2026_K001001) parts = args.donation_number.rsplit('-', 1) if len(parts) == 2: number, year = parts filename = f"{year}_{number}.pdf" else: filename = f"{args.donation_number}.pdf" output_path = output_dir / filename # Generate receipt try: success = generate_receipt( donor=args.donor, amount=args.amount, date_str=args.date, donation_number=args.donation_number, output_path=output_path, transaction=args.transaction, payment_method=args.payment_method, base_dir=base_dir ) if success: print(f"✅ Receipt generated successfully:") print(f" {output_path}") return 0 else: print("❌ Failed to generate receipt") return 1 except Exception as e: print(f"❌ Error generating receipt: {e}") import traceback traceback.print_exc() return 1 if __name__ == '__main__': sys.exit(main())