From b581dc3e1d6b5d55e3310a33d212bac64b23528c Mon Sep 17 00:00:00 2001 From: rikuke <33894149+rikuke@users.noreply.github.com> Date: Wed, 4 Dec 2024 08:55:46 +0200 Subject: [PATCH] feat: Hl 1502 csv changes (#3618) * feat: use instalment.amount_paid as sum * feat: add instalments to PowerBI csv * feat: add instalments to handler csv report --- .../services/applications_csv_report.py | 43 ++++++++++++++++++- .../applications_power_bi_csv_report.py | 9 ++++ .../services/talpa_csv_service.py | 4 +- .../benefit/applications/tests/conftest.py | 1 + .../tests/test_applications_report.py | 41 +++++++++++++++--- .../tests/test_talpa_integration.py | 1 + 6 files changed, 89 insertions(+), 10 deletions(-) diff --git a/backend/benefit/applications/services/applications_csv_report.py b/backend/benefit/applications/services/applications_csv_report.py index ac7b84f58a..f29683e93b 100644 --- a/backend/benefit/applications/services/applications_csv_report.py +++ b/backend/benefit/applications/services/applications_csv_report.py @@ -1,9 +1,14 @@ +import decimal +import logging from datetime import datetime from typing import List -from django.utils import translation +from django.conf import settings +from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist +from django.utils import timezone, translation from applications.enums import ApplicationBatchStatus, ApplicationOrigin, BenefitType +from applications.models import Application from applications.services.csv_export_base import ( CsvColumn, CsvExportBase, @@ -11,6 +16,8 @@ nested_queryset_attr, ) +LOGGER = logging.getLogger(__name__) + def csv_default_column(*args, **kwargs): # define a default value, as the application csv export needs to be able to handle @@ -101,6 +108,32 @@ def __init__(self, applications, prune_sensitive_data=False): self.export_notes = [] self.prune_sensitive_data = prune_sensitive_data + def query_instalment_by_number( + self, application: Application, number: int + ) -> decimal.Decimal: + """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 + except ObjectDoesNotExist: + LOGGER.error( + f"Valid payable Instalment not found for application {application.application_number}" + ) + except MultipleObjectsReturned: + LOGGER.error( + f"Multiple payable Instalments found for application \ + {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_2(self, application: Application) -> decimal.Decimal: + return self.query_instalment_by_number(application, 2) + @property def CSV_COLUMNS(self) -> List[CsvColumn]: calculated_benefit_amount = "calculation.calculated_benefit_amount" @@ -290,6 +323,14 @@ def CSV_COLUMNS(self) -> List[CsvColumn]: current_ahjo_row_field_getter("end_date"), ), ] + + if settings.PAYMENT_INSTALMENTS_ENABLED: + columns.append( + csv_default_column("Maksuerä 1", self.get_instalment_1), + ) + columns.append( + csv_default_column("Maksuerä 2", self.get_instalment_2), + ) # Include all the application rows in the same line for easier processing for idx in range(self.MAX_AHJO_ROWS): columns.extend( 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 3be5a7065f..d77a7acda9 100644 --- a/backend/benefit/applications/services/applications_power_bi_csv_report.py +++ b/backend/benefit/applications/services/applications_power_bi_csv_report.py @@ -1,6 +1,7 @@ from datetime import datetime from typing import Union +from django.conf import settings from django.utils import translation from applications.enums import ApplicationBatchStatus @@ -114,6 +115,14 @@ def CSV_COLUMNS(self): csv_default_column("Takaisinlaskutettu", self.get_alteration_amount), ] + if settings.PAYMENT_INSTALMENTS_ENABLED: + columns.append( + csv_default_column("Maksuerä 1", self.get_instalment_1), + ) + columns.append( + csv_default_column("Maksuerä 2", self.get_instalment_2), + ) + return columns def get_row_items(self): diff --git a/backend/benefit/applications/services/talpa_csv_service.py b/backend/benefit/applications/services/talpa_csv_service.py index 96be91d2e5..6d6c158d15 100644 --- a/backend/benefit/applications/services/talpa_csv_service.py +++ b/backend/benefit/applications/services/talpa_csv_service.py @@ -22,7 +22,7 @@ class TalpaCsvService(ApplicationsCsvService): def get_relevant_instalment_amount( self, application: Application ) -> decimal.Decimal: - """Return the amount of the currently accepted and due instalment""" + """Return the actual payable amount of the currently accepted and due instalment""" # TODO remove this flag when the feature is enabled ready for production if settings.PAYMENT_INSTALMENTS_ENABLED: try: @@ -30,7 +30,7 @@ def get_relevant_instalment_amount( status=InstalmentStatus.ACCEPTED, due_date__lte=timezone.now().date(), ) - return instalment.amount + return instalment.amount_paid except ObjectDoesNotExist: LOGGER.error( f"Valid payable Instalment not found for application {application.application_number}" diff --git a/backend/benefit/applications/tests/conftest.py b/backend/benefit/applications/tests/conftest.py index 4acc5ef266..023320fb30 100755 --- a/backend/benefit/applications/tests/conftest.py +++ b/backend/benefit/applications/tests/conftest.py @@ -189,6 +189,7 @@ def pruned_applications_csv_service_with_one_application( applications_csv_service, 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_applications_report.py b/backend/benefit/applications/tests/test_applications_report.py index 703734e9b9..4fce68ed25 100644 --- a/backend/benefit/applications/tests/test_applications_report.py +++ b/backend/benefit/applications/tests/test_applications_report.py @@ -1,3 +1,4 @@ +import decimal import io import os.path from collections import defaultdict @@ -31,6 +32,8 @@ from applications.tests.conftest import * # noqa from applications.tests.conftest import split_lines_at_semicolon from applications.tests.factories import DecidedApplicationFactory, DeMinimisAidFactory +from calculator.enums import InstalmentStatus +from calculator.models import Instalment from calculator.tests.factories import PaySubsidyFactory from common.tests.conftest import * # noqa from companies.tests.conftest import * # noqa @@ -593,15 +596,36 @@ def test_write_application_alterations_csv_file( assert str(alteration.recovery_amount) in contents +@pytest.mark.parametrize( + "instalments_enabled", + [ + (False,), + (True,), + ], +) def test_pruned_applications_csv_output( - pruned_applications_csv_service_with_one_application, + pruned_applications_csv_service_with_one_application, instalments_enabled, settings ): - csv_lines = split_lines_at_semicolon( - pruned_applications_csv_service_with_one_application.get_csv_string() - ) + settings.PAYMENT_INSTALMENTS_ENABLED = instalments_enabled + + instalment_amount = decimal.Decimal("123.45") application = ( pruned_applications_csv_service_with_one_application.get_applications()[0] ) + if instalments_enabled: + application.calculation.instalments.all().delete() + Instalment.objects.create( + calculation=application.calculation, + amount=instalment_amount, + amount_paid=instalment_amount, + instalment_number=1, + status=InstalmentStatus.ACCEPTED, + due_date=datetime.now(timezone.utc).date(), + ) + + csv_lines = split_lines_at_semicolon( + pruned_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 @@ -635,9 +659,12 @@ def test_pruned_applications_csv_output( assert csv_lines[1][5] == f'"{application.effective_company_street_address}"' assert csv_lines[1][6] == f'"{application.effective_company_postcode}"' assert csv_lines[1][7] == f'"{application.effective_company_city}"' - assert str(csv_lines[1][8]) == str( - application.calculation.calculated_benefit_amount - ) + if instalments_enabled: + assert str(csv_lines[1][8]) == str(instalment_amount) + else: + assert str(csv_lines[1][8]) == str( + application.calculation.calculated_benefit_amount + ) assert csv_lines[1][9] == f'"{application.batch.decision_maker_title}"' assert csv_lines[1][10] == f'"{application.batch.decision_maker_name}"' diff --git a/backend/benefit/applications/tests/test_talpa_integration.py b/backend/benefit/applications/tests/test_talpa_integration.py index 0fd7619395..36adce9bd5 100644 --- a/backend/benefit/applications/tests/test_talpa_integration.py +++ b/backend/benefit/applications/tests/test_talpa_integration.py @@ -113,6 +113,7 @@ def test_talpa_csv_decimal( Instalment.objects.create( calculation=application.calculation, amount=decimal.Decimal("123.45"), + amount_paid=decimal.Decimal("123.45"), instalment_number=1, status=InstalmentStatus.ACCEPTED, due_date=datetime.now(timezone.utc).date(),