Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 2nd instalment due date into csv #3635

Merged
merged 3 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading