Files
fjccv-receipts/generate_receipt.py
Krystie 149fafb834 Fix filename format: change from LNNN_YYYY to YYYY_LNNN
Also fixed assignment error where year and number were swapped.
2026-05-25 03:17:03 -07:00

270 lines
9.0 KiB
Python
Executable File

#!/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())