Verified Commit ddbc70a5 authored by Jakob Moser's avatar Jakob Moser
Browse files

Add first draft of payment order parser

parent 04b1ac15
Loading
Loading
Loading
Loading

muffin/__main__.py

0 → 100644
+22 −0
Original line number Diff line number Diff line
from pathlib import Path
import itertools
import sys

from .payment_order.PaymentOrderLoader import load_pdf

directory = Path(sys.argv[1])
payment_orders = [load_pdf(pdf) for pdf in directory.rglob("*.pdf")]


def group_by(iterable, key):
    return itertools.groupby(sorted(iterable, key=key), key)


grouped_by_budgetary_item = group_by(payment_orders, lambda po: po.budgetary_item)

sums = {
    budgetary_item: sum(po.amount_cent for po in payment_orders)
    for budgetary_item, payment_orders in grouped_by_budgetary_item
}

print(sums)
+40 −0
Original line number Diff line number Diff line
from dataclasses import dataclass


@dataclass(frozen=True, eq=False)
class PaymentOrder:
    """An order for the Finanzreferat to pay something on behalf of a council.

    It consists of an amount to be paid, details of the bank account to receive the
    payment as well as various details about the justification for the payment
    (as needed by organizational requirements).

    As long as everything is filed correctly, the Finanzreferat doesn't really have any
    other choice than paying, this is why this is called an order and not just a request.

    In German, you might call this “Zahlungsanweisung”, however, the official term used
    by the StuRa is “Finanzabrechnung” (“financial statement”).
    """

    petitioner_name: str  # Antragsteller
    petitioner_contact: str  # “Für Nachfragen: E-Mail, gerne Tel.-Nr.”

    reference: str  # Verwendungszweck
    amount_cent: int  # Betrag

    payee_name: str  # Zahlungsempfänger
    payee_iban: str  # IBAN
    payee_bic: str  # BIC

    budgetary_item: str  # Abzurechnen unter Posten

    @property
    def amount_euro(self) -> float:
        """Return the total amount in Euro (e.g. 133.70).

        This converts the total amount in Cent (e.g. 13370) to Euro, by dividing by 100.
        The true amount is always stored in Cent, and all calculations should be done
        using the amount in Cent, to avoid floating-point precision issues; but for
        presentation on the UI, this property might be more helpful.
        """
        return self.amount_cent / 100
+30 −0
Original line number Diff line number Diff line
from pathlib import Path

from pypdf import PdfReader

from .PaymentOrder import PaymentOrder


def to_amount_cent(amount_rendered: str) -> int:
    # TODO Improve this clunky heuristic
    return int(amount_rendered.replace(",", ""))


def load_pdf(pdf_path: Path | str) -> PaymentOrder:
    with PdfReader(pdf_path) as reader:
        fields = reader.get_fields()

    # TODO
    # for field, value in fields.items():
    #    print(field, "=", value)

    return PaymentOrder(
        petitioner_name=fields["Name"].value,
        petitioner_contact=fields["Kontaktdaten"].value,
        reference=fields["Text6"].value + "\n" + fields["Text7"].value,
        amount_cent=to_amount_cent(fields["Text8"].value),
        payee_name=fields["Text9"].value,
        payee_iban="DE" + fields["Text10"].value,
        payee_bic=fields["Text11"].value,
        budgetary_item=fields["Text12"].value,
    )
+0 −0

Empty file added.