Skip to content
This repository has been archived by the owner on Jul 18, 2024. It is now read-only.

Commit

Permalink
Merge pull request #92 from moneymeets/feature/personio-review
Browse files Browse the repository at this point in the history
fix(datev): use nicer idiom for join
  • Loading branch information
zyv authored Dec 1, 2023
2 parents f62d7b5 + d1f87d0 commit b76cf6e
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 41 deletions.
2 changes: 0 additions & 2 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
Expand Down
84 changes: 46 additions & 38 deletions sepacetamol/views/datev.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from django.shortcuts import render
from django.utils.encoding import smart_str
from openpyxl.reader.excel import load_workbook
from pydantic import BaseModel, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator


def float_to_german(value: float) -> str:
Expand All @@ -29,18 +29,22 @@ def date_to_datev(d: date) -> str:
return d.strftime("%Y%m%d")


DEFAULT_CONFIG = ConfigDict(
populate_by_name=True,
validate_default=True,
validate_assignment=True,
frozen=True,
)


PositiveInt = Annotated[int, Field(gt=0)]


class DatevSettings(BaseModel):
consultant_number: PositiveInt
client_numer: PositiveInt

class Config:
populate_by_name = True
validate_default = True
validate_assignment = True
frozen = True
model_config = DEFAULT_CONFIG


class DatevModel(BaseModel):
Expand All @@ -50,11 +54,12 @@ def model_dump_csv(self) -> str:
datev_writer.writerow(self.model_dump().values())
return "\n".join(unquote_empty_csv_strings(line) for line in output.getvalue().splitlines())

class Config:
populate_by_name = True
validate_default = True
validate_assignment = True
frozen = True
model_config = ConfigDict(
populate_by_name=True,
validate_default=True,
validate_assignment=True,
frozen=True,
)


class DatevHeader(DatevModel):
Expand Down Expand Up @@ -199,6 +204,30 @@ def model_dump_csv_header(self) -> str:
return output.getvalue().strip()


def get_datev_booking_from_personio(row) -> tuple[date, DatevBooking]:
(datum, _, _, umsatz, sh, _, _, gegenkonto, konto, belegfeld_1, buchungstext, *_) = row

datum = datetime.strptime(datum, "%d.%m.%Y").replace(tzinfo=ZoneInfo("Europe/Berlin")).date()

soll_haben = "S" if not sh else sh

if umsatz < 0:
soll_haben = "H" if soll_haben == "S" else "S"

return (
datum,
DatevBooking(
umsatz=float_to_german(abs(umsatz)),
soll_haben_kz=soll_haben,
konto=konto,
gegenkonto=gegenkonto,
belegdatum=datum.strftime("%d%m"),
belegfeld_1=belegfeld_1,
buchungstext=buchungstext,
),
)


def convert_personio_to_datev(request) -> HttpResponse:
datev_settings = DatevSettings(
consultant_number=request.POST["consultant-number"],
Expand All @@ -210,31 +239,8 @@ def convert_personio_to_datev(request) -> HttpResponse:
except Exception as e:
raise ValueError("Personio file could not be loaded, please check the format") from e

def get_booking(row) -> tuple[date, DatevBooking]:
(datum, _, _, umsatz, sh, _, _, gegenkonto, konto, belegfeld_1, buchungstext, *_) = row

datum = datetime.strptime(datum, "%d.%m.%Y").replace(tzinfo=ZoneInfo("Europe/Berlin")).date()

soll_haben = "S" if not sh else sh

if umsatz < 0:
soll_haben = "H" if soll_haben == "S" else "S"

return (
datum,
DatevBooking(
umsatz=float_to_german(abs(umsatz)),
soll_haben_kz=soll_haben,
konto=konto,
gegenkonto=gegenkonto,
belegdatum=datum.strftime("%d%m"),
belegfeld_1=belegfeld_1,
buchungstext=buchungstext,
),
)

bookings = [
get_booking([value.strip() if isinstance(value, str) else value for value in row])
get_datev_booking_from_personio([value.strip() if isinstance(value, str) else value for value in row])
for row in worksheet.iter_rows(min_row=3, values_only=True)
if any(row)
]
Expand All @@ -261,9 +267,11 @@ def get_booking(row) -> tuple[date, DatevBooking]:
target_filename = f"EXTF_Personio-{datum.strftime('%Y-%m')}.csv"

contents = "\r\n".join(
[header.model_dump_csv()]
+ [first_booking.model_dump_csv_header()]
+ [booking.model_dump_csv() for _, booking in bookings],
(
header.model_dump_csv(),
first_booking.model_dump_csv_header(),
*(booking.model_dump_csv() for _, booking in bookings),
),
).encode("cp1252")

response = HttpResponse(content_type="text/csv")
Expand Down
42 changes: 41 additions & 1 deletion sepacetamol/views/test_datev.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from datetime import date
from unittest import TestCase

from sepacetamol.views.datev import DatevBooking, DatevHeader, date_to_datev, float_to_german, unquote_empty_csv_strings
from sepacetamol.views.datev import (
DatevBooking,
DatevHeader,
date_to_datev,
float_to_german,
get_datev_booking_from_personio,
unquote_empty_csv_strings,
)


class TestDatev(TestCase):
Expand Down Expand Up @@ -65,3 +72,36 @@ def test_booking_serialization(self):

self.assertEqual(output_csv_header.strip(), input_obj.model_dump_csv_header().strip())
self.assertEqual(unquote_empty_csv_strings(output_csv_line.strip()), input_obj.model_dump_csv().strip())

def test_personio_to_datev_booking(self):
mock_row = (
"01.06.2023",
None,
None,
123456.78,
"H",
None,
None,
"4120",
"1755",
"202306",
"Festbezug Gehaelter",
None,
None,
)

self.assertEqual(
(
date(2023, 6, 1),
DatevBooking(
umsatz="123456,78",
soll_haben_kz="H",
konto=1755,
gegenkonto=4120,
belegdatum="0106",
belegfeld_1="202306",
buchungstext="Festbezug Gehaelter",
),
),
get_datev_booking_from_personio(mock_row),
)

0 comments on commit b76cf6e

Please sign in to comment.