From c1dd508d856abd987fa98c0d3e99cf7d0c1fc4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Wed, 4 Dec 2024 15:23:17 +0200 Subject: [PATCH 1/3] feat: 2nd instalment due date into csv --- .../services/applications_csv_report.py | 57 +++++++++++++++---- .../applications_power_bi_csv_report.py | 18 +++++- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/backend/benefit/applications/services/applications_csv_report.py b/backend/benefit/applications/services/applications_csv_report.py index f29683e93b..b82d5c9a53 100644 --- a/backend/benefit/applications/services/applications_csv_report.py +++ b/backend/benefit/applications/services/applications_csv_report.py @@ -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 @@ -15,6 +15,7 @@ get_organization_type, nested_queryset_attr, ) +from calculator.models import Instalment LOGGER = logging.getLogger(__name__) @@ -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: @@ -128,11 +129,33 @@ 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: + instalment = self.query_instalment_by_number(application, 1) + if instalment and instalment.amount: + return instalment.amount + else: + return None + + def get_instalment_2_amount_paid(self, application: Application) -> decimal.Decimal: + instalment = self.query_instalment_by_number(application, 2) + if instalment and instalment.amount_paid: + return instalment.amount_paid + else: + return None + + def get_instalment_2_amount(self, application: Application) -> decimal.Decimal: + instalment = self.query_instalment_by_number(application, 2) + if instalment and instalment.amount_paid: + return instalment.amount_paid + else: + return None - def get_instalment_2(self, application: Application) -> decimal.Decimal: - return self.query_instalment_by_number(application, 2) + def get_instalment_2_due_date(self, application: Application) -> date: + instalment = self.query_instalment_by_number(application, 2) + if instalment: + return instalment.due_date + else: + return "" @property def CSV_COLUMNS(self) -> List[CsvColumn]: @@ -326,10 +349,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_paid + ), ) 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): diff --git a/backend/benefit/applications/services/applications_power_bi_csv_report.py b/backend/benefit/applications/services/applications_power_bi_csv_report.py index d77a7acda9..2b6ef5f917 100644 --- a/backend/benefit/applications/services/applications_power_bi_csv_report.py +++ b/backend/benefit/applications/services/applications_power_bi_csv_report.py @@ -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_paid + ), + ) + columns.append( + csv_default_column( + "Maksuerän 2 maksupäivä", self.get_instalment_2_due_date + ), ) return columns From e44b8dd405e2620d9581d4715e5861c1906189d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Thu, 5 Dec 2024 09:50:55 +0200 Subject: [PATCH 2/3] chore: change "pruned" to "talpa" for clarity --- .../benefit/applications/tests/conftest.py | 5 +- .../tests/test_applications_report.py | 8 ++-- .../tests/test_talpa_integration.py | 46 +++++++++---------- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/backend/benefit/applications/tests/conftest.py b/backend/benefit/applications/tests/conftest.py index 92a116e01f..6ba2a8267c 100755 --- a/backend/benefit/applications/tests/conftest.py +++ b/backend/benefit/applications/tests/conftest.py @@ -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) @@ -194,8 +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() diff --git a/backend/benefit/applications/tests/test_applications_report.py b/backend/benefit/applications/tests/test_applications_report.py index 4fce68ed25..e6a8440f70 100644 --- a/backend/benefit/applications/tests/test_applications_report.py +++ b/backend/benefit/applications/tests/test_applications_report.py @@ -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() @@ -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 diff --git a/backend/benefit/applications/tests/test_talpa_integration.py b/backend/benefit/applications/tests/test_talpa_integration.py index 83c05d7d00..6dc836a4b0 100644 --- a/backend/benefit/applications/tests/test_talpa_integration.py +++ b/backend/benefit/applications/tests/test_talpa_integration.py @@ -36,25 +36,25 @@ 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): +def test_talpa_csv_output(talpa_applications_csv_service_with_one_application): 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): @@ -62,33 +62,33 @@ def test_talpa_csv_output(pruned_applications_csv_service_with_one_application): 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 ) 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() ) @@ -100,13 +100,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() @@ -123,34 +123,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"') From 7928889bdf117357690d9835965a9e8959c8ac19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Thu, 5 Dec 2024 10:01:25 +0200 Subject: [PATCH 3/3] fix: use instalment_after_recoveries property --- .../services/applications_csv_report.py | 27 ++++++++--- .../applications_power_bi_csv_report.py | 2 +- .../services/talpa_csv_service.py | 5 +- .../benefit/applications/tests/conftest.py | 3 +- .../tests/test_talpa_integration.py | 47 ++++++++++++++++++- 5 files changed, 72 insertions(+), 12 deletions(-) diff --git a/backend/benefit/applications/services/applications_csv_report.py b/backend/benefit/applications/services/applications_csv_report.py index b82d5c9a53..48c18e8b70 100644 --- a/backend/benefit/applications/services/applications_csv_report.py +++ b/backend/benefit/applications/services/applications_csv_report.py @@ -130,27 +130,42 @@ def query_instalment_by_number( ) 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_amount_paid(self, application: Application) -> decimal.Decimal: + 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_paid: - return instalment.amount_paid + 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_paid: - return instalment.amount_paid + 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 @@ -360,7 +375,7 @@ def CSV_COLUMNS(self) -> List[CsvColumn]: ) columns.append( csv_default_column( - "Maksettu maksuerä 2", self.get_instalment_2_amount_paid + "Maksettu maksuerä 2", self.get_instalment_2_amount_after_recoveries ), ) columns.append( diff --git a/backend/benefit/applications/services/applications_power_bi_csv_report.py b/backend/benefit/applications/services/applications_power_bi_csv_report.py index 2b6ef5f917..b18fabce3a 100644 --- a/backend/benefit/applications/services/applications_power_bi_csv_report.py +++ b/backend/benefit/applications/services/applications_power_bi_csv_report.py @@ -128,7 +128,7 @@ def CSV_COLUMNS(self): ) columns.append( csv_default_column( - "Maksettu maksuerä 2", self.get_instalment_2_amount_paid + "Maksettu maksuerä 2", self.get_instalment_2_amount_after_recoveries ), ) columns.append( diff --git a/backend/benefit/applications/services/talpa_csv_service.py b/backend/benefit/applications/services/talpa_csv_service.py index 6d6c158d15..5724158daf 100644 --- a/backend/benefit/applications/services/talpa_csv_service.py +++ b/backend/benefit/applications/services/talpa_csv_service.py @@ -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}" @@ -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): diff --git a/backend/benefit/applications/tests/conftest.py b/backend/benefit/applications/tests/conftest.py index 6ba2a8267c..6070d46cc1 100755 --- a/backend/benefit/applications/tests/conftest.py +++ b/backend/benefit/applications/tests/conftest.py @@ -194,8 +194,7 @@ def talpa_applications_csv_service(): @pytest.fixture -def talpa_applications_csv_service_with_one_application(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) diff --git a/backend/benefit/applications/tests/test_talpa_integration.py b/backend/benefit/applications/tests/test_talpa_integration.py index 6dc836a4b0..d276cbb03d 100644 --- a/backend/benefit/applications/tests/test_talpa_integration.py +++ b/backend/benefit/applications/tests/test_talpa_integration.py @@ -48,7 +48,42 @@ def test_talpa_csv_string_lines_generator(talpa_applications_csv_service): ) -def test_talpa_csv_output(talpa_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( talpa_applications_csv_service_with_one_application.get_csv_string() ) @@ -65,6 +100,16 @@ def test_talpa_csv_output(talpa_applications_csv_service_with_one_application): == 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( talpa_applications_csv_service_with_one_application,