Enhancements
This commit is contained in:
+152
-40
@@ -1,18 +1,28 @@
|
||||
import sustApp.models.models as models
|
||||
import models.models as models
|
||||
from models.database import engine, SessionLocal
|
||||
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.pdfgen import canvas
|
||||
from segno import helpers
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter
|
||||
from reportlab.platypus import Table, TableStyle
|
||||
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
|
||||
|
||||
import datetime
|
||||
import locale
|
||||
|
||||
import os
|
||||
from config_loader import load_config
|
||||
from salutation_helpers import get_gender_salutation, get_pronouns
|
||||
|
||||
cfg = load_config()
|
||||
year = cfg['year']
|
||||
yearly_fee = cfg['yearly_fee']
|
||||
|
||||
MM_TO_POINTS = 72 / 25.4 # conversion factor
|
||||
offset_2mm = 2 * MM_TO_POINTS
|
||||
offset_1mm = MM_TO_POINTS
|
||||
|
||||
def print_multiple_lines(lines: int, text):
|
||||
cnt = 1
|
||||
@@ -25,27 +35,31 @@ def create_payment_slip(member: models.Members = None,
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
salutation = "Deine"
|
||||
salutation2 = "Dein"
|
||||
salutation3 = "Dich"
|
||||
salutation4 = "Solltest Du"
|
||||
salutation5 = "sende"
|
||||
# pronouns = get_pronouns(is_plural=False)
|
||||
# salutation = pronouns['possessive']
|
||||
# salutation2 = pronouns['neutral']
|
||||
# salutation3 = pronouns['accusative']
|
||||
# salutation4 = pronouns['conditional']
|
||||
# salutation5 = pronouns['verb']
|
||||
|
||||
subfolder = ""
|
||||
|
||||
w, h = A4
|
||||
|
||||
db = SessionLocal()
|
||||
|
||||
# create epc qr-code
|
||||
if not member is None:
|
||||
qrcode = helpers.make_epc_qr(name='Sportunion St. Thomas',
|
||||
iban='AT493443700000113407',
|
||||
amount=15*member.member_count,
|
||||
amount=yearly_fee*member.member_count,
|
||||
reference=f'{year}-4732-{member.id:03}-{member.member_count}')
|
||||
qrcode.save("epc_qr.png", scale=2)
|
||||
|
||||
subfolder = "mail" if member.member_email else ""
|
||||
fullfilepath = os.path.join(os.path.expanduser("~"),"sust","payment_slips",subfolder,f"{member.member_zip}_payment_slip_member{member.id}.pdf")
|
||||
fullfilepath = os.path.join(os.path.expanduser("~"),"sust",f"payment_slips_{year}",subfolder,f"{member.member_zip}_payment_slip_member{member.id}.pdf")
|
||||
else:
|
||||
fullfilepath = os.path.join(os.path.expanduser("~"),"sust","payment_slips",subfolder,f"4732_payment_slip_blanko.pdf")
|
||||
fullfilepath = os.path.join(os.path.expanduser("~"),"sust",f"payment_slips_{year}",subfolder,f"4732_payment_slip_blanko.pdf")
|
||||
|
||||
# Ensure the directory exists
|
||||
os.makedirs(os.path.dirname(fullfilepath), exist_ok=True)
|
||||
@@ -53,10 +67,12 @@ def create_payment_slip(member: models.Members = None,
|
||||
c = canvas.Canvas(fullfilepath, pagesize=A4)
|
||||
|
||||
if not member is None:
|
||||
c.drawImage(image="epc_qr.png", x=500, y=40) # epc qr
|
||||
c.drawImage(image="epc_qr.png", x=500-offset_2mm, y=40+offset_2mm) # epc qr
|
||||
#c.drawImage(image="Sportunion quer 4c.png", x=(w-180), y=(h-100), width=150, height=50, mask='auto')
|
||||
image_path = os.path.join(os.path.dirname(__file__), "Sportunion quer 4c.png")
|
||||
c.drawImage(image=image_path, x=(w-180), y=(h-100), width=150, height=50, mask='auto')
|
||||
c.drawImage(image=os.path.join(os.path.dirname(__file__), "qr-code_scannen_v3.png"), x=(w-160), y=(h-570), width=140, height=140, mask='auto')
|
||||
|
||||
c.setFont("Helvetica", 9)
|
||||
text = c.beginText(60, h - 150)
|
||||
|
||||
@@ -68,24 +84,30 @@ def create_payment_slip(member: models.Members = None,
|
||||
text.textLine(f"{member.member_street} {member.member_streetnumber}")
|
||||
text.textLine(f"{member.member_zip} {member.member_city}")
|
||||
print_multiple_lines(5, text)
|
||||
|
||||
if member.member_count > 1:
|
||||
salutation = "eure"
|
||||
salutation2 = "euer"
|
||||
salutation3 = "euch"
|
||||
salutation4 = "Solltet ihr"
|
||||
salutation5 = "sendet"
|
||||
text.textLine(f"Liebe {member.member_firstname_spouse} und lieber {member.member_firstname}!")
|
||||
pronouns = get_pronouns(is_plural=True)
|
||||
salutation = pronouns['possessive']
|
||||
salutation2 = pronouns['neutral']
|
||||
salutation3 = pronouns['accusative']
|
||||
salutation4 = pronouns['conditional']
|
||||
salutation5 = pronouns['verb']
|
||||
|
||||
# Get spouse's salutation based on their gender
|
||||
spouse_salutation = get_gender_salutation(str(member.spouse.member_gender), capitalize=False)
|
||||
member_salutation = get_gender_salutation(str(member.member_gender), capitalize=False)
|
||||
|
||||
text.textLine(f"{spouse_salutation.capitalize()} {member.spouse.member_firstname} und {member_salutation} {member.member_firstname}!")
|
||||
else:
|
||||
salutation = "Deine"
|
||||
salutation2 = "Dein"
|
||||
salutation3 = "Dich"
|
||||
salutation4 = "Solltest Du"
|
||||
salutation5 = "sende"
|
||||
if member.member_gender == "female":
|
||||
salutation_gender = "liebe"
|
||||
else:
|
||||
salutation_gender = "lieber"
|
||||
text.textLine(f"{salutation_gender.capitalize()} {member.member_firstname}!")
|
||||
pronouns = get_pronouns(is_plural=False)
|
||||
salutation = pronouns['possessive']
|
||||
salutation2 = pronouns['neutral']
|
||||
salutation3 = pronouns['accusative']
|
||||
salutation4 = pronouns['conditional']
|
||||
salutation5 = pronouns['verb']
|
||||
|
||||
member_salutation = get_gender_salutation(str(member.member_gender), capitalize=True)
|
||||
text.textLine(f"{member_salutation} {member.member_firstname}!")
|
||||
else:
|
||||
print_multiple_lines(10, text)
|
||||
|
||||
@@ -95,7 +117,7 @@ def create_payment_slip(member: models.Members = None,
|
||||
text.textLines(f"Vielen Dank für {salutation} Treue!\n\n{salutation2.capitalize()} Beitrag ist gut angelegt, denn der Sportverein St. Thomas bietet für jede(n) etwas:")
|
||||
print_multiple_lines(2, text)
|
||||
text.textLines("* Skigymnastik\n* Radfahren\n* Stockschiessen\n* Pilates\n* Tagesskifahrt\n* uvam")
|
||||
print_multiple_lines(5, text)
|
||||
print_multiple_lines(2, text)
|
||||
if not forMail:
|
||||
text.textLines(f"Wir möchten das Zustellen des Mitgliedsbeitrags-Zahlscheines zuküntig auf Mail umstellen, daher bitten wir {salutation3},\nuns eine Mail mit dem Betreff \"Zahlschein\" an unsere Mail-Adresse sv.stthomas@gmx.at zu senden!")
|
||||
else:
|
||||
@@ -104,23 +126,29 @@ def create_payment_slip(member: models.Members = None,
|
||||
text.textLines(f"{salutation2.capitalize()} Sportverein-Team!")
|
||||
c.drawText(text)
|
||||
|
||||
text = c.beginText(60, h - 500)
|
||||
text = c.beginText(60, h - 520)
|
||||
c.setFont("Helvetica", 7)
|
||||
text.textLines(f"Auf Grund der DSGVO müssen wir darauf hinweisen, dass wir außer Name, Adresse, Mail-Adresse\nzum Zwecke der Einebung des Mitgliedsbeitrags, keine persönlichen Daten unserer Mitglieder führen!\nDiese Daten werden von uns nicht an Dritte weitergegeben!\nWeiters informieren wir darüber, dass Fotos, die bei Veranstaltungen oder Aktivitäten entstehen,\nauf unserer Homepage oder in Zeitungen veröffentlicht werden können.\n{salutation4} gegen die Speicherung {salutation}r Daten oder die Veröffentlichung diverser Foto sein, {salutation5} uns bitte eine Mail.")
|
||||
c.drawText(text)
|
||||
|
||||
if forMail:
|
||||
text = c.beginText(60, h - 580)
|
||||
c.setFont("Helvetica", 7)
|
||||
text.textLines(f"IBAN: AT49 3443 7000 0011 3407\nBIC: RZOOAT2L437\nBank: Raiffeisenbank Prambachkirchen, Bankstelle Waizenkirchen")
|
||||
c.drawText(text)
|
||||
|
||||
# payment slip details
|
||||
text = c.beginText(11, 75)
|
||||
text = c.beginText(11 - offset_2mm, 75 + offset_1mm)
|
||||
c.setFont("Helvetica", 8)
|
||||
if not member is None:
|
||||
text.textLine(f"Beitrag gesamt: {locale.format_string('%.2f', 15 * member.member_count)}")
|
||||
text.textLine(f"Beitrag gesamt: {locale.format_string('%.2f', yearly_fee * member.member_count)}")
|
||||
text.textLine(f"Mitgliedsnummer: 4732-{member.id:03}")
|
||||
else:
|
||||
text.textLine(f"Beitrag gesamt: {locale.format_string('%.2f', 15)}")
|
||||
text.textLine(f"Beitrag gesamt: {locale.format_string('%.2f', yearly_fee)}")
|
||||
text.textLine(f"Mitgliedsnummer: 4732-NEU")
|
||||
c.drawText(text)
|
||||
|
||||
text = c.beginText(188, 103)
|
||||
text = c.beginText(188 - offset_2mm, 103 + offset_2mm)
|
||||
c.setFont("Helvetica", 8)
|
||||
if not member is None:
|
||||
text.textLine(f"Bei Online-Zahlung bitte die Zahlungsreferenz {year}-4732-{member.id:03}-{member.member_count} eintragen")
|
||||
@@ -132,12 +160,96 @@ def create_payment_slip(member: models.Members = None,
|
||||
c.drawText(text)
|
||||
|
||||
c.setFont("Helvetica", 12)
|
||||
c.drawString(77, 50, f"{year}")
|
||||
c.drawString(300, 120, f"{year}")
|
||||
|
||||
c.drawString(300 - offset_2mm, 120 + offset_1mm, f"{year}") # Zahlungsanweisung: Jahr
|
||||
c.drawString(77 - offset_2mm, 50 + offset_1mm, f"{year}") # Auftragsbestätigung: Jahr
|
||||
c.setFont("Helvetica", 10)
|
||||
c.drawString(188, h - 696, f'{year}-4732-{member.id:03}-{member.member_count}') # Zahlungsreferenz Zahlungsanweisung
|
||||
c.drawString(11, 120, f'{year}-4732-{member.id:03}-{member.member_count}') # Zahlungsreferenz Auftragsbestätigung
|
||||
c.drawString(130, h - 694, (locale.format_string('%.2f', 15 * member.member_count))) # Betrag Auftragsbestätigung
|
||||
c.drawString(w - 30, 170, (locale.format_string('%.2f', 15 * member.member_count))) # Betrag Zahlungsanweisung
|
||||
c.drawString(188 - offset_2mm, h - 696 + offset_1mm, f'{year}-4732-{member.id:03}-{member.member_count}') # Zahlungsanweisung: Zahlungsreferenz
|
||||
c.drawString(w - 30 - offset_2mm, 170 + offset_2mm, (locale.format_string('%.2f', yearly_fee * member.member_count))) # Zahlungsanweisung: Betrag
|
||||
c.drawString(11 - offset_2mm, 120 + offset_2mm, f'{year}-4732-{member.id:03}-{member.member_count}') # Auftragsbestätigung: Zahlungsreferenz
|
||||
c.drawString(130 - offset_2mm, h - 694 + offset_1mm, (locale.format_string('%.2f', yearly_fee * member.member_count))) # Auftragsbestätigung: Betrag
|
||||
|
||||
#TODO: config-switch
|
||||
# print payment-history
|
||||
payments = db.query(models.MembersPaidYears).filter(
|
||||
models.MembersPaidYears.id == member.id).first()
|
||||
if payments:
|
||||
# View returns 'X' for paid, 'N' for not applicable, NULL for not paid
|
||||
paid_2024 = payments.paid_2024 == 'X'
|
||||
paid_2025 = payments.paid_2025 == 'X'
|
||||
paid_2026 = payments.paid_2026 == 'X'
|
||||
|
||||
# Determine display status
|
||||
status_2024 = '✓' if paid_2024 else ('kein Mitglied' if payments.paid_2024 == 'N' else '✗')
|
||||
status_2025 = '✓' if paid_2025 else ('kein Mitglied' if payments.paid_2025 == 'N' else '✗')
|
||||
status_2026 = '✓' if paid_2026 else ('kein Mitglied' if payments.paid_2026 == 'N' else '✗')
|
||||
else:
|
||||
status_2024 = '✗'
|
||||
status_2025 = '✗'
|
||||
status_2026 = '✗'
|
||||
|
||||
data = [
|
||||
[f'{salutation.capitalize()} Beitragshistorie', ''],
|
||||
['2024', '2025', '2026'],
|
||||
[status_2024, status_2025, status_2026],
|
||||
]
|
||||
|
||||
# 2. Create the Table object
|
||||
table = Table(data, colWidths=[100, 100], rowHeights=[16, 13, 13])
|
||||
|
||||
# Apply styling - use string alignment constants
|
||||
style = TableStyle([
|
||||
# Span first row across all columns
|
||||
('SPAN', (0, 0), (2, 0)),
|
||||
|
||||
# Title row styling
|
||||
('ALIGN', (0, 0), (2, 0), 'CENTER'), # Changed from TA_CENTER to 'CENTER'
|
||||
('VALIGN', (0, 0), (2, 0), 'MIDDLE'),
|
||||
('FONTNAME', (0, 0), (2, 0), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 0), (2, 0), 11),
|
||||
('BACKGROUND', (0, 0), (2, 0), colors.HexColor("#168ef1")),
|
||||
('TEXTCOLOR', (0, 0), (2, 0), colors.whitesmoke),
|
||||
('BOTTOMPADDING', (0, 0), (2, 0), 8),
|
||||
('TOPPADDING', (0, 0), (2, 0), 8),
|
||||
|
||||
# Header row styling (2024, 2025)
|
||||
('ALIGN', (0, 1), (2, 1), 'CENTER'), # Changed from TA_CENTER to 'CENTER'
|
||||
('VALIGN', (0, 1), (2, 1), 'MIDDLE'),
|
||||
('FONTNAME', (0, 1), (2, 1), 'Helvetica-Bold'),
|
||||
('FONTSIZE', (0, 1), (2, 1), 9),
|
||||
('BACKGROUND', (0, 1), (2, 1), colors.HexColor('#e8e8e8')),
|
||||
('BOTTOMPADDING', (0, 1), (2, 1), 6),
|
||||
('TOPPADDING', (0, 1), (2, 1), 6),
|
||||
|
||||
# Data row styling
|
||||
('ALIGN', (0, 2), (2, 2), 'CENTER'), # Changed from TA_CENTER to 'CENTER'
|
||||
('VALIGN', (0, 2), (2, 2), 'MIDDLE'),
|
||||
('FONTNAME', (0, 2), (2, 2), 'Helvetica'),
|
||||
('FONTSIZE', (0, 2), (2, 2), 10),
|
||||
('BACKGROUND', (0, 2), (2, 2), colors.white),
|
||||
('BOTTOMPADDING', (0, 2), (2, 2), 6),
|
||||
('TOPPADDING', (0, 2), (2, 2), 6),
|
||||
|
||||
# Color cells based on payment status
|
||||
# Green for ✓, Red for ✗, Grey for N (not applicable)
|
||||
('TEXTCOLOR', (0, 2), (0, 2),
|
||||
colors.HexColor('#006400') if status_2024 == '✓'
|
||||
else (colors.HexColor('#808080') if status_2024 == 'kein Mitglied' else colors.HexColor('#dc143c'))),
|
||||
('TEXTCOLOR', (1, 2), (1, 2),
|
||||
colors.HexColor('#006400') if status_2025 == '✓'
|
||||
else (colors.HexColor('#808080') if status_2025 == 'kein Mitglied' else colors.HexColor('#dc143c'))),
|
||||
('TEXTCOLOR', (2, 2), (2, 2),
|
||||
colors.HexColor('#006400') if status_2026 == '✓'
|
||||
else (colors.HexColor('#808080') if status_2026 == 'kein Mitglied' else colors.HexColor('#dc143c'))),
|
||||
|
||||
# Grid styling
|
||||
('GRID', (0, 0), (2, -1), 0.5, colors.grey),
|
||||
])
|
||||
table.setStyle(style)
|
||||
|
||||
# 4. Wrap and Draw on the Canvas
|
||||
# You must call wrap() to calculate the size before drawing
|
||||
width, height = table.wrapOn(c, 0, 0)
|
||||
table.drawOn(c, 60, h - 500) # (canvas, x, y) coordinate from bottom-left
|
||||
|
||||
db.close()
|
||||
c.save()
|
||||
Reference in New Issue
Block a user