Skip to content

Commit

Permalink
feat: 2nd instalment due date into csv (#3635)
Browse files Browse the repository at this point in the history
* feat: 2nd instalment due date into csv

* chore: change "pruned" to "talpa" for clarity

* fix: use instalment_after_recoveries property
  • Loading branch information
rikuke authored Dec 5, 2024
1 parent a86b9f3 commit f9bbaf9
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 45 deletions.
72 changes: 62 additions & 10 deletions backend/benefit/applications/services/applications_csv_report.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import decimal
import logging
from datetime import datetime
from datetime import date, datetime
from typing import List

from django.conf import settings
Expand All @@ -15,6 +15,7 @@
get_organization_type,
nested_queryset_attr,
)
from calculator.models import Instalment

LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -110,16 +111,16 @@ def __init__(self, applications, prune_sensitive_data=False):

def query_instalment_by_number(
self, application: Application, number: int
) -> decimal.Decimal:
) -> Instalment:
"""Return the actual payable amount of the currently accepted and due instalment"""
try:
instalment = application.calculation.instalments.get(
due_date__lte=timezone.now().date(),
instalment_number=number,
)
return instalment.amount_paid
return instalment
except ObjectDoesNotExist:
LOGGER.error(
LOGGER.info(
f"Valid payable Instalment not found for application {application.application_number}"
)
except MultipleObjectsReturned:
Expand All @@ -128,11 +129,48 @@ def query_instalment_by_number(
{application.application_number}, there should be only one"
)

def get_instalment_1(self, application: Application) -> decimal.Decimal:
return self.query_instalment_by_number(application, 1)
def get_instalment_1_amount(self, application: Application) -> decimal.Decimal:
"""
Return the amount that was granted for the first instalment.
"""
instalment = self.query_instalment_by_number(application, 1)
if instalment and instalment.amount:
return instalment.amount
else:
return None

def get_instalment_2(self, application: Application) -> decimal.Decimal:
return self.query_instalment_by_number(application, 2)
def get_instalment_2_amount_after_recoveries(
self, application: Application
) -> decimal.Decimal:
"""
Return the actual amount that is payable on the second instalment,
after possible alterations have been deductet.
"""
instalment = self.query_instalment_by_number(application, 2)
if instalment and instalment.amount_after_recoveries:
return instalment.amount_after_recoveries
else:
return None

def get_instalment_2_amount(self, application: Application) -> decimal.Decimal:
"""
Return the amount that was granted for the second instalment.
"""
instalment = self.query_instalment_by_number(application, 2)
if instalment and instalment.amount:
return instalment.amount
else:
return None

def get_instalment_2_due_date(self, application: Application) -> date:
"""
Return the due date of the second instalment.
"""
instalment = self.query_instalment_by_number(application, 2)
if instalment:
return instalment.due_date
else:
return ""

@property
def CSV_COLUMNS(self) -> List[CsvColumn]:
Expand Down Expand Up @@ -326,10 +364,24 @@ def CSV_COLUMNS(self) -> List[CsvColumn]:

if settings.PAYMENT_INSTALMENTS_ENABLED:
columns.append(
csv_default_column("Maksuerä 1", self.get_instalment_1),
csv_default_column(
"Myönnetty maksuerä 1", self.get_instalment_1_amount
),
)
columns.append(
csv_default_column(
"Myönnetty maksuerä 2", self.get_instalment_2_amount
),
)
columns.append(
csv_default_column(
"Maksettu maksuerä 2", self.get_instalment_2_amount_after_recoveries
),
)
columns.append(
csv_default_column("Maksuerä 2", self.get_instalment_2),
csv_default_column(
"Maksuerän 2 maksupäivä", self.get_instalment_2_due_date
),
)
# Include all the application rows in the same line for easier processing
for idx in range(self.MAX_AHJO_ROWS):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,24 @@ def CSV_COLUMNS(self):

if settings.PAYMENT_INSTALMENTS_ENABLED:
columns.append(
csv_default_column("Maksuerä 1", self.get_instalment_1),
csv_default_column(
"Myönnetty maksuerä 1", self.get_instalment_1_amount
),
)
columns.append(
csv_default_column("Maksuerä 2", self.get_instalment_2),
csv_default_column(
"Myönnetty maksuerä 2", self.get_instalment_2_amount
),
)
columns.append(
csv_default_column(
"Maksettu maksuerä 2", self.get_instalment_2_amount_after_recoveries
),
)
columns.append(
csv_default_column(
"Maksuerän 2 maksupäivä", self.get_instalment_2_due_date
),
)

return columns
Expand Down
5 changes: 3 additions & 2 deletions backend/benefit/applications/services/talpa_csv_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def get_relevant_instalment_amount(
status=InstalmentStatus.ACCEPTED,
due_date__lte=timezone.now().date(),
)
return instalment.amount_paid
return instalment.amount_after_recoveries
except ObjectDoesNotExist:
LOGGER.error(
f"Valid payable Instalment not found for application {application.application_number}"
Expand All @@ -40,7 +40,8 @@ def get_relevant_instalment_amount(
f"Multiple payable Instalments found for application \
{application.application_number}, there should be only one"
)
return application.calculation.calculated_benefit_amount
else:
return application.calculation.calculated_benefit_amount

@property
def CSV_COLUMNS(self):
Expand Down
6 changes: 2 additions & 4 deletions backend/benefit/applications/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def applications_csv_service_with_one_application(applications_csv_service):


@pytest.fixture
def pruned_applications_csv_service():
def talpa_applications_csv_service():
# retrieve the objects through the default manager so that annotations are added
application1 = DecidedApplicationFactory(application_number=100001)
application2 = DecidedApplicationFactory(application_number=100002)
Expand All @@ -194,9 +194,7 @@ def pruned_applications_csv_service():


@pytest.fixture
def pruned_applications_csv_service_with_one_application(
applications_csv_service, application_batch
):
def talpa_applications_csv_service_with_one_application(application_batch):
application1 = application_batch.applications.all().first()

return TalpaCsvService(Application.objects.filter(pk=application1.pk), True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,14 +603,14 @@ def test_write_application_alterations_csv_file(
(True,),
],
)
def test_pruned_applications_csv_output(
pruned_applications_csv_service_with_one_application, instalments_enabled, settings
def test_talpa_applications_csv_output(
talpa_applications_csv_service_with_one_application, instalments_enabled, settings
):
settings.PAYMENT_INSTALMENTS_ENABLED = instalments_enabled

instalment_amount = decimal.Decimal("123.45")
application = (
pruned_applications_csv_service_with_one_application.get_applications()[0]
talpa_applications_csv_service_with_one_application.get_applications()[0]
)
if instalments_enabled:
application.calculation.instalments.all().delete()
Expand All @@ -624,7 +624,7 @@ def test_pruned_applications_csv_output(
)

csv_lines = split_lines_at_semicolon(
pruned_applications_csv_service_with_one_application.get_csv_string()
talpa_applications_csv_service_with_one_application.get_csv_string()
)
# Assert that there are 18 column headers in the pruned CSV
assert len(csv_lines[0]) == 18
Expand Down
91 changes: 68 additions & 23 deletions backend/benefit/applications/tests/test_talpa_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,59 +36,104 @@ def test_talpa_lines(applications_csv_service):
)


def test_talpa_csv_cell_list_lines_generator(pruned_applications_csv_service):
def test_talpa_csv_cell_list_lines_generator(talpa_applications_csv_service):
check_csv_cell_list_lines_generator(
pruned_applications_csv_service, expected_row_count_with_header=3
talpa_applications_csv_service, expected_row_count_with_header=3
)


def test_talpa_csv_string_lines_generator(pruned_applications_csv_service):
def test_talpa_csv_string_lines_generator(talpa_applications_csv_service):
check_csv_string_lines_generator(
pruned_applications_csv_service, expected_row_count_with_header=3
talpa_applications_csv_service, expected_row_count_with_header=3
)


def test_talpa_csv_output(pruned_applications_csv_service_with_one_application):
@pytest.mark.parametrize(
"instalments_enabled, number_of_instalments",
[
(False, 1),
(True, 1),
(True, 2),
],
)
def test_talpa_csv_output(
talpa_applications_csv_service_with_one_application,
instalments_enabled,
number_of_instalments,
settings,
):
settings.PAYMENT_INSTALMENTS_ENABLED = instalments_enabled
application = (
talpa_applications_csv_service_with_one_application.applications.first()
)
application.calculation.instalments.all().delete()

if instalments_enabled:
for i in range(number_of_instalments):
status = InstalmentStatus.ACCEPTED
due_date = datetime.now(timezone.utc).date()
if i == 1:
status = InstalmentStatus.WAITING
due_date = timezone.now() + timedelta(days=181)

Instalment.objects.create(
calculation=application.calculation,
amount=decimal.Decimal("123.45"),
instalment_number=i + 1,
status=status,
due_date=due_date,
)

csv_lines = split_lines_at_semicolon(
pruned_applications_csv_service_with_one_application.get_csv_string()
talpa_applications_csv_service_with_one_application.get_csv_string()
)
# BOM at the beginning of the file
assert csv_lines[0][0] == '\ufeff"Hakemusnumero"'
csv_columns = iter(pruned_applications_csv_service_with_one_application.CSV_COLUMNS)
csv_columns = iter(talpa_applications_csv_service_with_one_application.CSV_COLUMNS)
next(csv_columns, None) # Skip the first element

for idx, col in enumerate(csv_columns, start=1):
assert csv_lines[0][idx] == f'"{col.heading}"'

assert (
int(csv_lines[1][0])
== pruned_applications_csv_service_with_one_application.applications.first().application_number
== talpa_applications_csv_service_with_one_application.applications.first().application_number
)

if instalments_enabled:
wanted_instalment = application.calculation.instalments.get(
status=InstalmentStatus.ACCEPTED,
due_date__lte=timezone.now().date(),
)
assert (
decimal.Decimal(csv_lines[1][8])
== wanted_instalment.amount_after_recoveries
)


def test_talpa_csv_non_ascii_characters(
pruned_applications_csv_service_with_one_application,
talpa_applications_csv_service_with_one_application,
):
application = (
pruned_applications_csv_service_with_one_application.applications.first()
talpa_applications_csv_service_with_one_application.applications.first()
)
application.company_name = "test äöÄÖtest"
application.save()
csv_lines = split_lines_at_semicolon(
pruned_applications_csv_service_with_one_application.get_csv_string()
talpa_applications_csv_service_with_one_application.get_csv_string()
)
assert csv_lines[1][3] == '"test äöÄÖtest"' # string is quoted


def test_talpa_csv_delimiter(pruned_applications_csv_service_with_one_application):
def test_talpa_csv_delimiter(talpa_applications_csv_service_with_one_application):
application = (
pruned_applications_csv_service_with_one_application.applications.first()
talpa_applications_csv_service_with_one_application.applications.first()
)
application.company_name = "test;12"
application.save()
assert (
';"test;12";'
in pruned_applications_csv_service_with_one_application.get_csv_string()
in talpa_applications_csv_service_with_one_application.get_csv_string()
)


Expand All @@ -100,13 +145,13 @@ def test_talpa_csv_delimiter(pruned_applications_csv_service_with_one_applicatio
],
)
def test_talpa_csv_decimal(
pruned_applications_csv_service_with_one_application,
talpa_applications_csv_service_with_one_application,
settings,
instalments_enabled,
):
settings.PAYMENT_INSTALMENTS_ENABLED = instalments_enabled
application = (
pruned_applications_csv_service_with_one_application.applications.first()
talpa_applications_csv_service_with_one_application.applications.first()
)
if instalments_enabled:
application.calculation.instalments.all().delete()
Expand All @@ -123,34 +168,34 @@ def test_talpa_csv_decimal(
application.calculation.save()

csv_lines = split_lines_at_semicolon(
pruned_applications_csv_service_with_one_application.get_csv_string()
talpa_applications_csv_service_with_one_application.get_csv_string()
)
assert csv_lines[1][8] == "123.45"


def test_talpa_csv_date(pruned_applications_csv_service_with_one_application):
def test_talpa_csv_date(talpa_applications_csv_service_with_one_application):
application = (
pruned_applications_csv_service_with_one_application.get_applications().first()
talpa_applications_csv_service_with_one_application.get_applications().first()
)
now = datetime.now(timezone.utc)
application.batch.decision_date = now
application.batch.save()
csv_lines = split_lines_at_semicolon(
pruned_applications_csv_service_with_one_application.get_csv_string()
talpa_applications_csv_service_with_one_application.get_csv_string()
)
assert csv_lines[1][12] == f'"{now.strftime("%Y-%m-%d")}"'


def test_write_talpa_csv_file(
pruned_applications_csv_service_with_one_application, tmp_path
talpa_applications_csv_service_with_one_application, tmp_path
):
application = (
pruned_applications_csv_service_with_one_application.applications.first()
talpa_applications_csv_service_with_one_application.applications.first()
)
application.company_name = "test äöÄÖtest"
application.save()
output_file = tmp_path / "output.csv"
pruned_applications_csv_service_with_one_application.write_csv_file(output_file)
talpa_applications_csv_service_with_one_application.write_csv_file(output_file)
with open(output_file, encoding="utf-8-sig") as f:
contents = f.read()
assert contents.startswith('"Hakemusnumero";"Työnantajan tyyppi"')
Expand Down

0 comments on commit f9bbaf9

Please sign in to comment.