Browse Source

added scripts for travel forms

master
Holger Frey 1 week ago
parent
commit
62671952fd
  1. BIN
      work_helpers/Vorlage UK-Abschlag, v2023-01.pdf
  2. 230
      work_helpers/fill_forms.py

BIN
work_helpers/Vorlage UK-Abschlag, v2023-01.pdf

Binary file not shown.

230
work_helpers/fill_forms.py

@ -0,0 +1,230 @@ @@ -0,0 +1,230 @@
import click
from datetime import datetime
import pathlib
import sys
import shutil
import pandas as pd
import warnings
import re
from PyPDFForm import FormWrapper, PdfWrapper
warnings.filterwarnings("ignore")
Pathlike = pathlib.Path | str
WINHOME = pathlib.Path("/mnt/c/Users/Holgi/")
DESKTOP = WINHOME / "Desktop"
TODAY = datetime.now().strftime("%Y-%m-%d")
def _iso_date_to_german(date: str) -> str:
return ".".join(reversed(date.split("-")))
def _search_files(
folder: Pathlike, partial_name: str, suffix: str
) -> list[pathlib.Path]:
parent = pathlib.Path(folder)
if not suffix.startswith("."):
suffix = f".{suffix}"
files = (item for item in parent.iterdir() if item.is_file())
non_hidden = (item for item in files if not item.stem.startswith("."))
non_tempfile = (item for item in files if not item.stem.startswith("~"))
types = (
item for item in non_tempfile if item.suffix.lower() == suffix.lower()
)
return [item for item in types if partial_name.lower() in item.stem.lower()]
def _get_latest_file(
folder: Pathlike, partial_name: str, suffix: str
) -> pathlib.Path | None:
results = _search_files(folder, partial_name, suffix)
if not results:
return None
creation_times = [item.stat().st_ctime for item in results]
by_creation_time = sorted(zip(creation_times, results))
newest_with_time = by_creation_time[-1] # latest entry
return newest_with_time[1] # the path entry of the tuple
def _get_form_path(partial_name: str) -> pathlib.Path:
own_parent = pathlib.Path(__file__).parent
matches = _search_files(own_parent, partial_name, ".pdf")
if len(matches) == 1:
return matches[0]
counts = len(matches)
msg = f"Found {counts} matching pdf forms for '{partial_name}'"
raise IOError(msg)
def _get_unique(data: pd.DataFrame, column: str) -> str | int | float:
uniques = data[column].unique()
if len(uniques) != 1:
msg = f"Found multiple unique values for '{column}'"
raise ValueError(msg)
return uniques[0]
def _extract_travel_number(data: pd.DataFrame, column: str) -> str:
belege = data[column]
travel_nr = belege.apply(
lambda x: re.search("(5\d{7,8})", x.replace(" ", ""))
)
match_result = travel_nr[travel_nr.first_valid_index()]
return match_result[0]
@click.command()
@click.argument(
"form", type=click.Path(exists=True, file_okay=True, dir_okay=False)
)
@click.option(
"-o",
"--output",
default=None,
help="Output file path, defaults to desktop folder",
)
def inspect(form, output):
form = pathlib.Path(form)
if not output:
new_name = f"inspected {form.stem}{form.suffix}"
output = DESKTOP / new_name
preview_stream = PdfWrapper(str(form)).preview
with output.open("wb+") as output_stream:
output_stream.write(preview_stream)
def payments():
downloads = WINHOME / "Downloads"
forms = DESKTOP / "Formulare"
latest_export = _get_latest_file(downloads, "Buchungen_SAP", ".xlsx")
if not latest_export:
sys.exit("Could not find an SuperX export file, aborting.")
fields = ["BelegNr", "VorgängerBelegNr", "Kostenstelle", "Fonds", "Projekt"]
converters = {field: str for field in fields}
data = pd.read_excel(latest_export, skiprows=3, converters=converters)
travel_nr = _extract_travel_number(data, "BelegNr")
mask = data["Werttyp"] == "Zahlung"
payments = data[mask].copy()
summary = (
payments.groupby("BelegNr")
.agg({"Betrag": "sum", "BuDat": "first"})
.reset_index()
.sort_values("BelegNr")
)
try:
cost_center = _get_unique(payments, "Kostenstelle")
fonds = _get_unique(payments, "Fonds")
project = _get_unique(payments, "Projekt")
if not project or len(project) <= 4:
project = ""
except ValueError as e:
sys.exit(str(e))
form_data = {
"Projekt": project,
"Kostenstelle": cost_center,
"Mittelbindung": travel_nr,
"Fonds": fonds,
}
print(f"Projekt: {project}")
print(f"Kostenstelle: {cost_center}")
print(f"Mittelbindung: {travel_nr}")
print(f"Fonds: {fonds}")
print("")
print(f" Datum Betrag SuperX")
print("")
for i, row in summary.iterrows():
index = i + 1
form_data.update(
{
f"Datum{index}": row["BuDat"],
f"BelegNr aus SuberX{index}": row["BelegNr"],
# no field "Euro{index}"
# the automatic form calculation would not work.
}
)
betrag = f"{row['Betrag']:0.2f}".replace(".", ",")
print(f" {row['BuDat']} {betrag:>7} {row['BelegNr']}")
source_path = forms / "Vorlage UK-Abschlag, v2023-01.pdf"
destination_path = DESKTOP / f"{TODAY} UK-Abschlag.pdf"
form = FormWrapper(str(source_path))
filled = form.fill(form_data, flatten=False)
destination_path.write_bytes(filled.read())
@click.command()
@click.option("-l", "--search_last_name", prompt=True, required=True)
@click.option("-d", "--iso_date", prompt=True, required=True)
@click.option("-p", "--place", prompt=True, required=True)
def prepare_payments(search_last_name: str, iso_date: str, place: str):
forms_path = DESKTOP / "Formulare"
templates_path = forms_path / "vorbereitet UK-As"
templates = _search_files(templates_path, f" {search_last_name}", ".pdf")
if len(templates) == 0:
sys.exit(
f"Could not find a UK-A template for search '{search_last_name}'"
)
if len(templates) > 1:
sys.exit(
f"Found multiple UK-A templates for search '{search_last_name}'"
)
template = templates[0]
rest, first_name = template.stem.rsplit(",", maxsplit=1)
*_, last_name = rest.split()
first_name = first_name.strip()
last_name = last_name.strip()
first_last = f"{first_name} {last_name}"
last_first = f"{last_name}, {first_name}"
travel_short = f"{last_first}, {place}"
travel_name = f"{iso_date} {travel_short}"
date = _iso_date_to_german(iso_date)
folder = DESKTOP / f"{travel_name} (abgerechnet)"
folder.mkdir()
rk_path = folder / f" {travel_short}, Reisekostenabrechnung.txt"
rk_path.write_text(rk_path.stem)
uka_hint_path = (
folder / f" UK-A {last_first}, Dienstreise {place}, Schlusszahlung.txt"
)
content = "\t".join(
[
_iso_date_to_german(TODAY),
first_last,
f"Schlusszahlung Dienstreise {place}, {date}",
]
)
uka_hint_path.write_text(content)
source_path = forms_path / "Vorlage UK-Abschlag, v2023-01.pdf"
destination_path = folder / f"{TODAY} {travel_short}, UK-Abschlag.pdf"
shutil.copy(source_path, destination_path)
form = FormWrapper(str(template))
form_data = {
"Verwendungszweck": f"Schlusszahlung Dienstreise nach {place}",
"Begründung": f"Schlusszahlung Dienstreise {first_last} nach {place} am {date}",
"Datum_Feststellung": _iso_date_to_german(TODAY),
"Datum_Anordnung": _iso_date_to_german(TODAY),
}
filled = form.fill(form_data, flatten=False)
uka_path = folder / f"{TODAY} {travel_short}, UK-A Schlusszahlung.pdf"
uka_path.write_bytes(filled.read())
Loading…
Cancel
Save