vereinsgwrschtl/tryton-scripts/beitragsrechnungen.py
2024-10-04 11:57:49 +00:00

143 lines
6.5 KiB
Python

#!/bin/env python3
import argparse
import csv
import datetime
import decimal
import getpass
import sys
import triad.client
argparser = argparse.ArgumentParser(description='Beitragsrechnungen für ein Jahr erzeugen')
argparser.add_argument('-u', '--username', help='Tryton-Benutzername', default='fgrimpen')
argparser.add_argument('-p', '--password', help='Tryton-Passwort')
argparser.add_argument('-U', '--url', help='Tryton-URL', default='https://kasse.verein.ccchb.de/tryton/')
argparser.add_argument('--reference-date', help='Referenzdatum für Zahlungsbedingung', type=datetime.date.fromisoformat)
argparser.add_argument('--claims-account', help='Forderungskonto', default='12001')
argparser.add_argument('--fee-account', help='Beitragskonto', default='40000')
argparser.add_argument('--default-payment-term', default='1m')
argparser.add_argument("-F", "--csv-fields", type=lambda f: f.split(","), help="csv fields")
argparser.add_argument('-n', '--dry-run', action='store_true')
argparser.add_argument("-o", "--output", help="output file", type=argparse.FileType("w"), default="-")
argparser.add_argument("--update", help="update invoices", action="store_true")
argparser.add_argument("--months", help="Beitragsmonate", type=lambda m: m.split(","), default="1,2,3,4,5,6,7,8,9,10,11,12")
argparser.add_argument('year', help='Beitragsjahr', type=lambda s: datetime.date(int(s), 1, 1))
argparser.add_argument('csvfile', nargs='?', type=argparse.FileType('r'), default='-')
args = argparser.parse_args()
if not args.password:
args.password = getpass.getpass(f"Password for {args.username}@{args.url}: ")
def calculate_reference_date(date):
today = datetime.date.today()
if today >= date:
date = today
while date.day >= 10:
date += datetime.timedelta(days=1)
while date.day != 15:
date += datetime.timedelta(days=1)
return date
if not args.reference_date:
args.reference_date = calculate_reference_date(args.year)
args.months = [datetime.date(args.year.year, int(m), 1) for m in args.months]
csvreader = csv.DictReader(args.csvfile, fieldnames=args.csv_fields)
csvwriter = csv.DictWriter(args.output, fieldnames=csvreader.fieldnames + ["invoice_id"])
csvwriter.writeheader()
PAYMENT_TERMS_MAPPING = [
({'1m'}, 'Beitrag monatlich/jährlich'),
({'3m'}, 'Beitrag quartalsweise/jährlich'),
({'6m'}, 'Beitrag halbjährlich/jährlich'),
({'1y', '12m'}, 'Beitrag jährlich/jährlich')
]
def load_payment_terms(session, invoicing_client):
ret = {}
for keys, payment_term_name in PAYMENT_TERMS_MAPPING:
payment_term = invoicing_client.PaymentTerm.find([('name', '=', payment_term_name)])[0]
ret.update({key: payment_term for key in keys})
return ret
def create_invoice(party, payment_term, monthly_fee, months, yearly_fee, claims_account, fee_account, invoicing_client, reference_date, year, invoice_id, update):
if update:
invoice = invoicing_client.Invoice(int(invoice_id))
else:
invoice = invoicing_client.Invoice()
invoice.type = 'out'
invoice.party = party
invoice.description = f"Beiträge {year.year}"
invoice.account = claims_account
invoice.payment_term = payment_term
invoice.payment_term_date = reference_date
if update:
for line in invoice.lines:
invoice.lines.remove(line)
if monthly_fee:
for month in months:
invoice_line = invoicing_client.InvoiceLine()
invoice_line.description = f"Beitrag {month.year}-{month.month}"
invoice_line.account = fee_account
invoice_line.quantity = decimal.Decimal(1)
invoice_line.unit_price = decimal.Decimal(monthly_fee)
invoice.lines.append(invoice_line)
if yearly_fee:
invoice_line = invoicing_client.InvoiceLine()
invoice_line.description = f"Jahresbeitrag {year.year}"
invoice_line.account = fee_account
invoice_line.quantity = decimal.Decimal(1)
invoice_line.unit_price = decimal.Decimal(yearly_fee)
invoice.lines.append(invoice_line)
invoice.state = 'draft'
return invoice
with triad.client.Session.start(args.url, args.username, args.password) as session:
party_client = triad.client.PartyClient.from_session(session)
invoicing_client = triad.client.InvoicingClient.from_session(session)
accounting_client = triad.client.AccountingClient.from_session(session)
claims_account = accounting_client.get_account_by_code(args.claims_account)
print(f"Claims account: {claims_account.code} {claims_account.name}", file=sys.stderr)
fee_account = accounting_client.get_account_by_code(args.fee_account)
print(f'Fee account: {fee_account.code} {fee_account.name}', file=sys.stderr)
payment_terms = load_payment_terms(invoicing_client, invoicing_client)
for key, payment_term in payment_terms.items():
print(f"Payment term {key}: {payment_term.name}", file=sys.stderr)
print(f'Fee year: {args.year}', file=sys.stderr)
print(f'Reference date: {args.reference_date}', file=sys.stderr)
for member in csvreader:
party = party_client.get_party_by_code(member['party_id'])
print(f"Party: {party.code} {party.name}", file=sys.stderr)
try:
payment_term = payment_terms[member['payment_term']]
except KeyError:
payment_term = payment_terms[args.default_payment_term]
print(f"\tPayment term: {payment_term.name}", file=sys.stderr)
try:
monthly_fee = decimal.Decimal(member['monthly_fee'])
except:
print(f"\tInvalid monthly fee: {member['monthly_fee']}", file=sys.stderr)
monthly_fee = None
else:
print(f"\tMonthly fee: {monthly_fee}", file=sys.stderr)
try:
yearly_fee = decimal.Decimal(member['yearly_fee'])
except:
print(f'\tInvalid yearly fee: {member['yearly_fee']}', file=sys.stderr)
yearly_fee = None
else:
print(f'\tYearly fee: {yearly_fee}', file=sys.stderr)
invoice = create_invoice(party, payment_term, monthly_fee, args.months, yearly_fee, claims_account, fee_account, invoicing_client, args.reference_date, args.year, member.get('invoice_id'), args.update)
if not args.dry_run:
invoice.save()
print(f'\tInvoice: {invoice.id} {invoice.description}', file=sys.stderr)
for line in invoice.lines:
print(f'\t\tLine: {line.description} {line.unit_price}', file=sys.stderr)
print(f'\tTotal amount: {invoice.total_amount}', file=sys.stderr)
member['invoice_id'] = invoice.id
csvwriter.writerow(member)