#!/bin/env python3 import argparse import csv import datetime import decimal import email.message import getpass import pathlib import subprocess import tempfile import jinja2 import triad.client class Env(object): jinja2_env: jinja2.Environment args: argparse.Namespace session: triad.client.Session def fee_invoice(env: Env): csvreader = csv.DictReader(env.args.csvfile, fieldnames=env.args.csv_fields) csvwriter = csv.DictWriter(env.args.output, fieldnames=csvreader.fieldnames + ["message_id"]) csvwriter.writeheader() invoicing_client = triad.client.InvoicingClient.from_session(env.session) template = env.jinja2_env.get_template("beitrag.eml") for member in csvreader: invoice = invoicing_client.get_invoice_by_id(member['invoice_id']) year = invoice.invoice_date or env.args.year months = [datetime.date(year.year, month, 1) for month in range(1, 13)] email_address = member.get('email') or invoice.party.email try: sepa_mandate = invoice.party.reception_direct_debits[0] except IndexError: sepa_mandate = None else: sepa_mandate = sepa_mandate.sepa_mandate template_args = { 'monthly_fee': decimal.Decimal(member.get('monthly_fee') or '0.0'), "yearly_fee": decimal.Decimal(member.get('yearly_fee') or '0.0'), 'invoice': invoice, 'sepa_mandate': sepa_mandate, 'payments': [], 'sender': env.args.email_sender, "year": year, "fee_months": [datetime.date(year.year, month, 1) for month in range(1, 13)] } msg = email.message.EmailMessage() msg["From"] = env.args.email_from msg["To"] = email_address msg["Date"] = env.args.email_date or datetime.datetime.now() msg["Subject"] = f"Beitragsrechnung {year.year} ({invoice.number})" msg["Message-Id"] = email.utils.make_msgid(domain="verein.ccchb.de") member['message_id'] = msg["Message-Id"] msg.set_content(template.render(**template_args)) with (env.args.output_dir / member['message_id']).open("wb") as mailfile: mailfile.write(bytes(msg)) csvwriter.writerow(member) def sepa_mandate(env: Env): csvreader = csv.DictReader(env.args.csvfile, fieldnames=env.args.csv_fields) csvwriter = csv.DictWriter(env.args.output, fieldnames=csvreader.fieldnames + ["message_id"]) csvwriter.writeheader() client = triad.client.BaseClient.from_session(env.session) template = env.jinja2_env.get_template("sepa_mandat.eml") for mandate in csvreader: email_address= mandate.get('email') if 'sepa_mandate_ref' in mandate: sepa_mandate = client.get_model('account.payment.sepa.mandate').find([('identification', '=', mandate['sepa_mandate_ref'])]) party = sepa_mandate.party elif 'party_id' in mandate: party = client.get_model('party.party').find([('code', '=', mandate['party_id'])])[0] email_address = email_address or party.email sepa_mandate = party.reception_direct_debits[0].sepa_mandate else: continue template_args = { "party": party, "sepa_mandate": sepa_mandate } msg = email.message.EmailMessage() msg["From"] = env.args.email_from msg["To"] = email_address msg["Date"] = env.args.email_date or datetime.datetime.now() msg["Subject"] = f"SEPA-Lastschriftmandat MREF: {sepa_mandate.identification}" msg["Message-Id"] = email.utils.make_msgid(domain="verein.ccchb.de") mandate['message_id'] = msg["Message-Id"] msg.set_content(template.render(**template_args)) if env.args.pdf: msg.add_attachment(sepa_mandate_build_pdf(env, sepa_mandate), 'application', 'pdf', filename='SEPA-Lastschriftmandat.pdf') with (env.args.output_dir / mandate['message_id']).open("wb") as mailfile: mailfile.write(bytes(msg)) csvwriter.writerow(mandate) def sepa_mandate_build_pdf(env, sepa_mandate): with tempfile.TemporaryDirectory() as tempdir: tempdir_path = pathlib.Path(tempdir) jobname = sepa_mandate.identification with (tempdir_path / (jobname + "-settings.tex")).open("w") as fh: fh.write(f''' \\csdef{{sepa-m001}}{{{sepa_mandate.identification}}} \\csdef{{sepa-m006}}{{Wiederkehrende Zahlung}} \\csdef{{sepa-p001}}{{{sepa_mandate.party.name}}} \\csdef{{sepa-d001}}{{{sepa_mandate.account_number.number}}} \\csdef{{sepa-m008}}{{{sepa_mandate.signature_date}}} \\csdef{{sepa-m009}}{{\\emph{{entfällt}}}} \\csdef{{sepa-d002a}}{{}} \\csdef{{sepa-d002b}}{{}} \\csdef{{sepa-p005}}{{}} \\csdef{{sepa-remarks}}{{Dieses Mandat ist eine Abschrift des dem Zahlungsempfänger vorliegenden, originalen SEPA-Lastschriftmandats.\\\\ Erstellt am {datetime.date.today()}}} ''') compile_latex(jobname, env.args.latex_template, tempdir) with (tempdir_path / (jobname + ".pdf")).open("rb") as fh: return fh.read() def compile_latex(jobname, texfile, directory, debug=True): latexmk_args = ["latexmk", "-lualatex", str(texfile), f"-jobname={jobname}"] if not debug: latexmk_args.append("-silent") subprocess.run(latexmk_args, cwd=directory) def main(): parser = argparse.ArgumentParser() parser.add_argument("-U", "--uri", help="Tryton URI") parser.add_argument("-u", "--username", help="Tryton username") parser.add_argument("-p", "--password", help="Tryton password") parser.add_argument("-O", "--output-dir", help="email output directory", default=".", type=pathlib.Path) parser.add_argument("-T", "--template-dir", help="email template directory", default="mail_templates") # Email arguments parser.add_argument('--email-sender', help='email signature sender') parser.add_argument("--email-from", help="email from header") parser.add_argument("--email-date", help="email date header") subparsers = parser.add_subparsers() fee_invoice_parser = subparsers.add_parser("fee-invoice") fee_invoice_parser.add_argument("-o", "--output", type=argparse.FileType("w"), default='-') fee_invoice_parser.add_argument("-F", "--csv-fields", type=lambda f: f.split(","), help="csv fields") fee_invoice_parser.add_argument("-y", "--year", type=lambda y: datetime.date(int(y), 1, 1)) fee_invoice_parser.add_argument("--months", help="Beitragsmonate", type=lambda m: m.split(","), default="1,2,3,4,5,6,7,8,9,10,11,12") fee_invoice_parser.add_argument("csvfile", type=argparse.FileType("r"), default="-", nargs="?") fee_invoice_parser.set_defaults(func=fee_invoice) sepa_mandate_parser = subparsers.add_parser('sepa-mandate') sepa_mandate_parser.add_argument('-o', '--output', type=argparse.FileType("w"), default='-') sepa_mandate_parser.add_argument('-F', '--csv-fields', type=lambda f: f.split(','), help='csv fields') sepa_mandate_parser.add_argument('--pdf', action='store_true', help='build pdf attachment', default=False) sepa_mandate_parser.add_argument('--no-pdf', action='store_false', dest='pdf', help='don\'t build pdf attachment') sepa_mandate_parser.add_argument('--latex-template', help='latex template in $TEXINPUTS') sepa_mandate_parser.add_argument('csvfile', type=argparse.FileType('r'), default='-', nargs='?') sepa_mandate_parser.set_defaults(func=sepa_mandate) args = parser.parse_args() env = Env() env.jinja2_env = jinja2.Environment( loader=jinja2.FileSystemLoader(args.template_dir) ) env.args = args if not args.uri: args.uri = input("URI for Tryton: ") if not args.username: args.username = input("Username for Tryton: ") if not args.password: args.password = getpass.getpass(f"Password for Tryton user `{args.username}': ") with triad.client.Session.start(args.uri, args.username, args.password) as session: env.session = session args.func(env) main()