From 52c707a99d7fbec9905b56de65ce4b07f609e410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Fri, 29 Nov 2024 12:57:42 +0200 Subject: [PATCH 1/7] fix: update instalments to accepted after decision --- .../management/commands/send_ahjo_requests.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/backend/benefit/applications/management/commands/send_ahjo_requests.py b/backend/benefit/applications/management/commands/send_ahjo_requests.py index 46e004db1f..c64f281681 100644 --- a/backend/benefit/applications/management/commands/send_ahjo_requests.py +++ b/backend/benefit/applications/management/commands/send_ahjo_requests.py @@ -3,6 +3,7 @@ from datetime import datetime from typing import Dict, List, Union +from django.conf import settings from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.core.management.base import BaseCommand from django.db.models import QuerySet @@ -32,6 +33,8 @@ send_open_case_request_to_ahjo, update_application_summary_record_in_ahjo, ) +from calculator.enums import InstalmentStatus +from calculator.models import Instalment LOGGER = logging.getLogger(__name__) @@ -224,8 +227,9 @@ def _print_results( def _handle_details_request_success( self, application: Application, response_dict: Dict ) -> str: - """Extract the details from the dict and update the application batch with them and also - with the p2p settings from ahjo_settings table""" + """ + Extract the details from the dict and update the application batchwith the data. + and data from the the p2p settings from ahjo_settings table""" details = parse_details_from_decision_response(response_dict) @@ -233,6 +237,17 @@ def _handle_details_request_success( if application.status == ApplicationStatus.REJECTED: batch_status_to_update = ApplicationBatchStatus.DECIDED_REJECTED + if ( + settings.PAYMENT_INSTALMENTS_ENABLED + and application.status == ApplicationStatus.ACCEPTED + ): + calculation = application.calculation + instalments = Instalment.objects.filter( + calculation=calculation, status=InstalmentStatus.WAITING + ) + if instalments.exists(): + instalments.update(status=InstalmentStatus.ACCEPTED) + batch = application.batch batch.update_batch_after_details_request(batch_status_to_update, details) From 1e5b7c7c5d2285df6341c1aa3e829544353e56c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Fri, 29 Nov 2024 12:59:38 +0200 Subject: [PATCH 2/7] fix: instalment initial status to WAITING --- backend/benefit/calculator/rules.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/backend/benefit/calculator/rules.py b/backend/benefit/calculator/rules.py index 0ab9230e63..9b99b60865 100644 --- a/backend/benefit/calculator/rules.py +++ b/backend/benefit/calculator/rules.py @@ -180,14 +180,24 @@ def _calculate_instalment_amounts( """ if total_benefit_amount <= self.instalment_threshold: return [ - (1, total_benefit_amount, timezone.now(), InstalmentStatus.ACCEPTED) + ( + 1, + total_benefit_amount, + timezone.now(), + InstalmentStatus.WAITING, + ) ] first_instalment_amount = self.first_instalment_limit second_instalment_amount = total_benefit_amount - first_instalment_amount return [ - (1, first_instalment_amount, timezone.now(), InstalmentStatus.ACCEPTED), + ( + 1, + first_instalment_amount, + timezone.now(), + InstalmentStatus.WAITING, + ), ( 2, second_instalment_amount, From 33a4d9fa198fccf57a3205322818dcd3e8d04e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Fri, 29 Nov 2024 13:38:14 +0200 Subject: [PATCH 3/7] chore: rename setting_response_handler.py --- backend/benefit/applications/services/ahjo/request_handler.py | 2 +- .../ahjo/{setting_response_handler.py => response_handler.py} | 1 + .../benefit/applications/tests/test_ahjo_response_handler.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) rename backend/benefit/applications/services/ahjo/{setting_response_handler.py => response_handler.py} (99%) diff --git a/backend/benefit/applications/services/ahjo/request_handler.py b/backend/benefit/applications/services/ahjo/request_handler.py index efa38e0cfd..f8c4f739f0 100644 --- a/backend/benefit/applications/services/ahjo/request_handler.py +++ b/backend/benefit/applications/services/ahjo/request_handler.py @@ -2,7 +2,7 @@ from applications.enums import AhjoRequestType from applications.services.ahjo.enums import AhjoSettingName -from applications.services.ahjo.setting_response_handler import AhjoResponseHandler +from applications.services.ahjo.response_handler import AhjoResponseHandler from applications.services.ahjo_authentication import AhjoToken from applications.services.ahjo_client import ( AhjoApiClient, diff --git a/backend/benefit/applications/services/ahjo/setting_response_handler.py b/backend/benefit/applications/services/ahjo/response_handler.py similarity index 99% rename from backend/benefit/applications/services/ahjo/setting_response_handler.py rename to backend/benefit/applications/services/ahjo/response_handler.py index eb095d066d..45ef0af437 100644 --- a/backend/benefit/applications/services/ahjo/setting_response_handler.py +++ b/backend/benefit/applications/services/ahjo/response_handler.py @@ -150,3 +150,4 @@ def filter_signers(data: Dict) -> List[Dict[str, str]]: for item in data["agentList"]: result.append({"ID": item["ID"], "Name": item["Name"]}) return result + diff --git a/backend/benefit/applications/tests/test_ahjo_response_handler.py b/backend/benefit/applications/tests/test_ahjo_response_handler.py index f4cd357efe..1c2e507060 100644 --- a/backend/benefit/applications/tests/test_ahjo_response_handler.py +++ b/backend/benefit/applications/tests/test_ahjo_response_handler.py @@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError from applications.services.ahjo.enums import AhjoSettingName -from applications.services.ahjo.setting_response_handler import AhjoResponseHandler +from applications.services.ahjo.response_handler import AhjoResponseHandler def test_ahjo_response_handler_filter_decision_makers(decisionmaker_response): From e25837c67d1431163b2e793f1ad9c8580b364958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Fri, 29 Nov 2024 13:42:17 +0200 Subject: [PATCH 4/7] chore: refactor decision details response handling --- backend/benefit/applications/enums.py | 12 +- .../management/commands/send_ahjo_requests.py | 57 ++------- .../services/ahjo/response_handler.py | 94 ++++++++++++++- .../services/ahjo_decision_service.py | 48 +------- .../benefit/applications/tests/conftest.py | 59 +++++++++- .../applications/tests/test_ahjo_decisions.py | 25 +--- .../tests/test_ahjo_response_handler.py | 110 +++++++++++++++++- 7 files changed, 283 insertions(+), 122 deletions(-) diff --git a/backend/benefit/applications/enums.py b/backend/benefit/applications/enums.py index e88672696a..31f643051f 100644 --- a/backend/benefit/applications/enums.py +++ b/backend/benefit/applications/enums.py @@ -276,13 +276,23 @@ class AhjoDecisionUpdateType(models.TextChoices): UPDATED = "Updated", _("Updated") -@dataclass +@dataclass(frozen=True, order=True) class AhjoDecisionDetails: decision_maker_name: str decision_maker_title: str section_of_the_law: str decision_date: datetime + def __post_init__(self): + if not self.decision_maker_name.strip(): + raise ValueError("Decision maker name cannot be empty") + if not self.decision_maker_title.strip(): + raise ValueError("Decision maker name cannot be empty") + if not self.section_of_the_law.strip(): + raise ValueError("Decision maker name cannot be empty") + if not isinstance(self.decision_date, datetime): + raise TypeError("decision_date must be a datetime object") + DEFAULT_AHJO_CALLBACK_ERROR_MESSAGE = [ { diff --git a/backend/benefit/applications/management/commands/send_ahjo_requests.py b/backend/benefit/applications/management/commands/send_ahjo_requests.py index c64f281681..fcf91761da 100644 --- a/backend/benefit/applications/management/commands/send_ahjo_requests.py +++ b/backend/benefit/applications/management/commands/send_ahjo_requests.py @@ -1,29 +1,23 @@ import logging import time from datetime import datetime -from typing import Dict, List, Union +from typing import List, Union -from django.conf import settings from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.core.management.base import BaseCommand from django.db.models import QuerySet -from applications.enums import ( - AhjoRequestType, - AhjoStatus as AhjoStatusEnum, - ApplicationBatchStatus, - ApplicationStatus, -) -from applications.models import AhjoStatus, Application +from applications.enums import AhjoRequestType +from applications.models import Application from applications.services.ahjo.exceptions import DecisionProposalAlreadyAcceptedError +from applications.services.ahjo.response_handler import ( + AhjoDecisionDetailsResponseHandler, +) from applications.services.ahjo_application_service import AhjoApplicationsService from applications.services.ahjo_authentication import ( AhjoToken, AhjoTokenExpiredException, ) -from applications.services.ahjo_decision_service import ( - parse_details_from_decision_response, -) from applications.services.ahjo_integration import ( delete_application_in_ahjo, get_decision_details_from_ahjo, @@ -33,8 +27,6 @@ send_open_case_request_to_ahjo, update_application_summary_record_in_ahjo, ) -from calculator.enums import InstalmentStatus -from calculator.models import Instalment LOGGER = logging.getLogger(__name__) @@ -224,40 +216,6 @@ def _print_results( ) ) - def _handle_details_request_success( - self, application: Application, response_dict: Dict - ) -> str: - """ - Extract the details from the dict and update the application batchwith the data. - and data from the the p2p settings from ahjo_settings table""" - - details = parse_details_from_decision_response(response_dict) - - batch_status_to_update = ApplicationBatchStatus.DECIDED_ACCEPTED - if application.status == ApplicationStatus.REJECTED: - batch_status_to_update = ApplicationBatchStatus.DECIDED_REJECTED - - if ( - settings.PAYMENT_INSTALMENTS_ENABLED - and application.status == ApplicationStatus.ACCEPTED - ): - calculation = application.calculation - instalments = Instalment.objects.filter( - calculation=calculation, status=InstalmentStatus.WAITING - ) - if instalments.exists(): - instalments.update(status=InstalmentStatus.ACCEPTED) - - batch = application.batch - batch.update_batch_after_details_request(batch_status_to_update, details) - - AhjoStatus.objects.create( - application=application, status=AhjoStatusEnum.DETAILS_RECEIVED_FROM_AHJO - ) - - return f"Successfully received and updated decision details \ -for application {application.id} and batch {batch.id} from Ahjo" - def _handle_application_request_success( self, application: Application, @@ -283,7 +241,8 @@ def _handle_successful_request( request_type: AhjoRequestType, ) -> None: if request_type == AhjoRequestType.GET_DECISION_DETAILS: - success_text = self._handle_details_request_success( + response_handler = AhjoDecisionDetailsResponseHandler() + success_text = response_handler.handle_details_request_success( application, response_content[0] ) else: diff --git a/backend/benefit/applications/services/ahjo/response_handler.py b/backend/benefit/applications/services/ahjo/response_handler.py index 45ef0af437..e116cd3db6 100644 --- a/backend/benefit/applications/services/ahjo/response_handler.py +++ b/backend/benefit/applications/services/ahjo/response_handler.py @@ -1,10 +1,25 @@ import logging +import re +from datetime import datetime from typing import Dict, List, Union +from django.conf import settings from django.core.exceptions import ValidationError -from applications.models import AhjoSetting +from applications.enums import ( + AhjoDecisionDetails, + AhjoStatus as AhjoStatusEnum, + ApplicationBatchStatus, + ApplicationStatus, +) +from applications.models import AhjoSetting, AhjoStatus, Application from applications.services.ahjo.enums import AhjoSettingName +from applications.services.ahjo.exceptions import ( + AhjoDecisionDetailsParsingError, + AhjoDecisionError, +) +from calculator.enums import InstalmentStatus +from calculator.models import Instalment LOGGER = logging.getLogger(__name__) @@ -151,3 +166,80 @@ def filter_signers(data: Dict) -> List[Dict[str, str]]: result.append({"ID": item["ID"], "Name": item["Name"]}) return result + +class AhjoDecisionDetailsResponseHandler: + def handle_details_request_success( + self, application: Application, response_dict: Dict + ) -> str: + """ + Extract the details from the dict and update the application batchwith the data. + and data from the the p2p settings from ahjo_settings table""" + + details = self._parse_details_from_decision_response(response_dict) + + batch_status_to_update = ApplicationBatchStatus.DECIDED_ACCEPTED + if application.status == ApplicationStatus.REJECTED: + batch_status_to_update = ApplicationBatchStatus.DECIDED_REJECTED + + batch = application.batch + batch.update_batch_after_details_request(batch_status_to_update, details) + + if ( + settings.PAYMENT_INSTALMENTS_ENABLED + and application.status == ApplicationStatus.ACCEPTED + ): + self._update_instalments_as_accepted(application) + + AhjoStatus.objects.create( + application=application, status=AhjoStatusEnum.DETAILS_RECEIVED_FROM_AHJO + ) + + return f"Successfully received and updated decision details \ +for application {application.id} and batch {batch.id} from Ahjo" + + def _update_instalments_as_accepted(self, application: Application): + calculation = application.calculation + instalments = Instalment.objects.filter( + calculation=calculation, status=InstalmentStatus.WAITING + ) + if instalments.exists(): + instalments.update(status=InstalmentStatus.ACCEPTED) + + def _parse_details_from_decision_response( + self, decision_data: Dict + ) -> AhjoDecisionDetails: + """Extract the decision details from the given decision data""" + try: + html_content = decision_data["Content"] + decision_maker_name = self._parse_decision_maker_from_html(html_content) + decision_maker_title = decision_data["Organization"]["Name"] + section_of_the_law = decision_data["Section"] + decision_date_str = decision_data["DateDecision"] + decision_date = datetime.strptime(decision_date_str, "%Y-%m-%dT%H:%M:%S.%f") + + return AhjoDecisionDetails( + decision_maker_name=decision_maker_name, + decision_maker_title=decision_maker_title, + section_of_the_law=f"{section_of_the_law} §", + decision_date=decision_date, + ) + except KeyError as e: + raise AhjoDecisionDetailsParsingError( + f"Error in parsing decision details: {e}" + ) + except ValueError as e: + raise AhjoDecisionDetailsParsingError( + f"Error in parsing decision details date: {e}" + ) + + def _parse_decision_maker_from_html(self, html_content: str) -> str: + """Parse the decision maker from the given html string""" + match = re.search( + r'
([^<]+)
', html_content, re.I + ) + if match: + return match.group(1) + else: + raise AhjoDecisionError( + "Decision maker not found in the decision content html", html_content + ) diff --git a/backend/benefit/applications/services/ahjo_decision_service.py b/backend/benefit/applications/services/ahjo_decision_service.py index c7a04c84fd..f33904c259 100644 --- a/backend/benefit/applications/services/ahjo_decision_service.py +++ b/backend/benefit/applications/services/ahjo_decision_service.py @@ -1,20 +1,15 @@ -import re -from datetime import datetime from string import Template -from typing import Dict, List +from typing import List from django.conf import settings -from applications.enums import AhjoDecisionDetails, DecisionType +from applications.enums import DecisionType from applications.models import ( AhjoDecisionText, Application, DecisionProposalTemplateSection, ) -from applications.services.ahjo.exceptions import ( - AhjoDecisionDetailsParsingError, - AhjoDecisionError, -) +from applications.services.ahjo.exceptions import AhjoDecisionError from applications.tests.factories import ( AcceptedDecisionProposalFactory, DeniedDecisionProposalFactory, @@ -107,40 +102,3 @@ def _generate_decision_text_string( return replace_decision_template_placeholders( decision_string, decision_type, application ) - - -def parse_details_from_decision_response(decision_data: Dict) -> AhjoDecisionDetails: - """Extract the decision details from the given decision data""" - try: - html_content = decision_data["Content"] - decision_maker_name = parse_decision_maker_from_html(html_content) - decision_maker_title = decision_data["Organization"]["Name"] - section_of_the_law = decision_data["Section"] - decision_date_str = decision_data["DateDecision"] - decision_date = datetime.strptime(decision_date_str, "%Y-%m-%dT%H:%M:%S.%f") - - return AhjoDecisionDetails( - decision_maker_name=decision_maker_name, - decision_maker_title=decision_maker_title, - section_of_the_law=f"{section_of_the_law} §", - decision_date=decision_date, - ) - except KeyError as e: - raise AhjoDecisionDetailsParsingError(f"Error in parsing decision details: {e}") - except ValueError as e: - raise AhjoDecisionDetailsParsingError( - f"Error in parsing decision details date: {e}" - ) - - -def parse_decision_maker_from_html(html_content: str) -> str: - """Parse the decision maker from the given html string""" - match = re.search( - r'
([^<]+)
', html_content, re.I - ) - if match: - return match.group(1) - else: - raise AhjoDecisionError( - "Decision maker not found in the decision content html", html_content - ) diff --git a/backend/benefit/applications/tests/conftest.py b/backend/benefit/applications/tests/conftest.py index 023320fb30..47e8fbb8a1 100755 --- a/backend/benefit/applications/tests/conftest.py +++ b/backend/benefit/applications/tests/conftest.py @@ -55,6 +55,7 @@ EmployeeFactory, HandlingApplicationFactory, ReceivedApplicationFactory, + RejectedApplicationFactory, ) from common.tests.conftest import * # noqa from companies.tests.conftest import * # noqa @@ -109,6 +110,14 @@ def decided_application(mock_get_organisation_roles_and_create_company): ) +@pytest.fixture +def rejected_decided_application(mock_get_organisation_roles_and_create_company): + with factory.Faker.override_default_locale("fi_FI"): + return RejectedApplicationFactory( + company=mock_get_organisation_roles_and_create_company + ) + + @pytest.fixture def multiple_decided_applications(mock_get_organisation_roles_and_create_company): with factory.Faker.override_default_locale("fi_FI"): @@ -524,6 +533,12 @@ def application_with_ahjo_case_id(decided_application): return decided_application +@pytest.fixture +def rejected_application_with_ahjo_case_id(rejected_decided_application): + rejected_decided_application.ahjo_case_id = generate_ahjo_case_id() + return rejected_decided_application + + @pytest.fixture def multiple_applications_with_ahjo_case_id( mock_get_organisation_roles_and_create_company, @@ -567,12 +582,37 @@ def application_with_ahjo_decision(application_with_ahjo_case_id, fake_decisionm return application_with_ahjo_case_id +@pytest.fixture +def rejected_application_with_ahjo_decision( + rejected_application_with_ahjo_case_id, fake_decisionmakers +): + template = AcceptedDecisionProposalFactory() + replaced_decision_text = replace_decision_template_placeholders( + f""" +

Päätös

{template.template_decision_text}
+
+

Päätöksen perustelut

{template.template_justification_text}
""", + DecisionType.DENIED, + rejected_application_with_ahjo_case_id, + ) + AhjoDecisionTextFactory( + application=rejected_application_with_ahjo_case_id, + decision_type=DecisionType.DENIED, + decision_text=replaced_decision_text, + language="fi", + decision_maker_id=fake_decisionmakers[0]["ID"], + decision_maker_name=fake_decisionmakers[0]["Name"], + ) + return rejected_application_with_ahjo_case_id + + @pytest.fixture def ahjo_decision_detail_response(application_with_ahjo_decision): id = uuid.uuid4() handler = application_with_ahjo_decision.calculation.handler name = f"{handler.first_name} {handler.last_name}" company = application_with_ahjo_decision.company + today = date.today() content = f'\ \ \ @@ -801,7 +841,7 @@ def ahjo_decision_detail_response(application_with_ahjo_decision): "PersonalData": "Sisältää henkilötietoja", "Issued": "2024-04-09T03:00:00.000", }, - "DateDecision": "2024-04-09T03:00:00.000", + "DateDecision": f"{today}T03:00:00.000", "DecisionHistoryPDF": None, "DecisionHistoryHTML": "", "CaseID": f"{application_with_ahjo_decision.ahjo_case_id}", @@ -854,6 +894,23 @@ def decided_application_with_decision_date(application_with_ahjo_decision): return application_with_ahjo_decision +@pytest.fixture +def rejected_decided_application_with_decision_date( + rejected_application_with_ahjo_decision, +): + batch = ApplicationBatch.objects.create( + handler=rejected_application_with_ahjo_decision.calculation.handler, + auto_generated_by_ahjo=True, + decision_date=date.today(), + ) + batch.status = ApplicationBatchStatus.COMPLETED + batch.save() + rejected_application_with_ahjo_decision.pay_subsidy_percent = 100 + rejected_application_with_ahjo_decision.batch = batch + rejected_application_with_ahjo_decision.save() + return rejected_application_with_ahjo_decision + + @pytest.fixture def application_alteration_csv_service(): application_1 = DecidedApplicationFactory(application_number=100003) diff --git a/backend/benefit/applications/tests/test_ahjo_decisions.py b/backend/benefit/applications/tests/test_ahjo_decisions.py index 6dd912be2f..df1a7fb53e 100644 --- a/backend/benefit/applications/tests/test_ahjo_decisions.py +++ b/backend/benefit/applications/tests/test_ahjo_decisions.py @@ -1,14 +1,12 @@ import uuid -from datetime import datetime import pytest from rest_framework.reverse import reverse from applications.api.v1.serializers.decision_text import DecisionTextSerializer -from applications.enums import AhjoDecisionDetails, DecisionType +from applications.enums import DecisionType from applications.models import AhjoDecisionText from applications.services.ahjo_decision_service import ( - parse_details_from_decision_response, replace_decision_template_placeholders, ) @@ -261,24 +259,3 @@ def test_decision_text_api_put( assert decision_text.decision_maker_name == data["decision_maker_name"] assert decision_text.signer_id == data["signer_id"] assert decision_text.signer_name == data["signer_name"] - - -def test_parse_details_from_decision_response( - ahjo_decision_detail_response, application_with_ahjo_decision -): - details = parse_details_from_decision_response(ahjo_decision_detail_response[0]) - handler = application_with_ahjo_decision.calculation.handler - - assert isinstance(details, AhjoDecisionDetails) - assert details.decision_maker_name == f"{handler.first_name} {handler.last_name}" - assert ( - details.decision_maker_title - == ahjo_decision_detail_response[0]["Organization"]["Name"] - ) - assert isinstance(details.decision_date, datetime) - assert details.decision_date == datetime.strptime( - ahjo_decision_detail_response[0]["DateDecision"], "%Y-%m-%dT%H:%M:%S.%f" - ) - assert ( - details.section_of_the_law == ahjo_decision_detail_response[0]["Section"] + " §" - ) diff --git a/backend/benefit/applications/tests/test_ahjo_response_handler.py b/backend/benefit/applications/tests/test_ahjo_response_handler.py index 1c2e507060..d163c46535 100644 --- a/backend/benefit/applications/tests/test_ahjo_response_handler.py +++ b/backend/benefit/applications/tests/test_ahjo_response_handler.py @@ -1,10 +1,23 @@ +from datetime import datetime from unittest.mock import patch import pytest from django.core.exceptions import ValidationError +from applications.enums import ( + AhjoDecision, + AhjoDecisionDetails, + AhjoStatus as AhjoStatusEnum, + ApplicationBatchStatus, + ApplicationStatus, +) from applications.services.ahjo.enums import AhjoSettingName -from applications.services.ahjo.response_handler import AhjoResponseHandler +from applications.services.ahjo.response_handler import ( + AhjoDecisionDetailsResponseHandler, + AhjoResponseHandler, +) +from calculator.enums import InstalmentStatus +from calculator.models import Instalment def test_ahjo_response_handler_filter_decision_makers(decisionmaker_response): @@ -163,3 +176,98 @@ def test_save_ahjo_settings_database_error(setting_name, test_data): ValidationError, match=f"Failed to save setting {setting_name} to database" ): AhjoResponseHandler._save_ahjo_setting(setting_name, test_data) + + +def test_parse_details_from_decision_response( + ahjo_decision_detail_response, application_with_ahjo_decision +): + response_handler = AhjoDecisionDetailsResponseHandler() + details = response_handler._parse_details_from_decision_response( + ahjo_decision_detail_response[0] + ) + handler = application_with_ahjo_decision.calculation.handler + + assert isinstance(details, AhjoDecisionDetails) + assert details.decision_maker_name == f"{handler.first_name} {handler.last_name}" + assert ( + details.decision_maker_title + == ahjo_decision_detail_response[0]["Organization"]["Name"] + ) + assert isinstance(details.decision_date, datetime) + assert details.decision_date == datetime.strptime( + ahjo_decision_detail_response[0]["DateDecision"], "%Y-%m-%dT%H:%M:%S.%f" + ) + assert ( + details.section_of_the_law == ahjo_decision_detail_response[0]["Section"] + " §" + ) + + +@pytest.mark.parametrize( + "instalments_enabled,application_status,\ + expected_batch_status, expected_proposal_for_decision, expected_instalment_1_status", + [ + ( + True, + ApplicationStatus.REJECTED, + ApplicationBatchStatus.DECIDED_REJECTED, + AhjoDecision.DECIDED_REJECTED, + InstalmentStatus.WAITING, + ), + ( + True, + ApplicationStatus.ACCEPTED, + ApplicationBatchStatus.DECIDED_ACCEPTED, + AhjoDecision.DECIDED_ACCEPTED, + InstalmentStatus.ACCEPTED, + ), + ], +) +def test_handle_details_request_success( + ahjo_decision_detail_response, + decided_application_with_decision_date, + rejected_decided_application_with_decision_date, + application_status, + expected_batch_status, + expected_proposal_for_decision, + expected_instalment_1_status, + instalments_enabled, + p2p_settings, + settings, +): + settings.PAYMENT_INSTALMENTS_ENABLED = instalments_enabled + + if application_status == ApplicationStatus.REJECTED: + application = rejected_decided_application_with_decision_date + else: + application = decided_application_with_decision_date + + calculation = application.calculation + calculation.instalments.all().delete() + + instalment_1 = Instalment.objects.create( + calculation=calculation, + status=InstalmentStatus.WAITING, + instalment_number=1, + amount=1000.00, + amount_paid=1000.00, + ) + + response_handler = AhjoDecisionDetailsResponseHandler() + success_text = response_handler.handle_details_request_success( + application=application, response_dict=ahjo_decision_detail_response[0] + ) + + instalment_1.refresh_from_db() + latest_ahjo_status = application.ahjo_status.latest() + + assert ( + success_text + == f"Successfully received and updated decision details \ +for application {application.id} and batch {application.batch.id} from Ahjo" + ) + assert instalment_1.status == expected_instalment_1_status + assert latest_ahjo_status.status == AhjoStatusEnum.DETAILS_RECEIVED_FROM_AHJO + + batch = application.batch + assert batch.status == expected_batch_status + assert batch.proposal_for_decision == expected_proposal_for_decision From 8740876daadb0a172428dbe79a5cb315c7308a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Mon, 2 Dec 2024 10:07:24 +0200 Subject: [PATCH 5/7] fix: failing test for initial instalment status --- backend/benefit/calculator/tests/test_calculator_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/benefit/calculator/tests/test_calculator_api.py b/backend/benefit/calculator/tests/test_calculator_api.py index 463fafbb2c..e0916934b2 100644 --- a/backend/benefit/calculator/tests/test_calculator_api.py +++ b/backend/benefit/calculator/tests/test_calculator_api.py @@ -729,7 +729,7 @@ def test_application_calculation_instalments( instalment_1 = handling_application.calculation.instalments.all()[0] assert instalment_1.due_date is not None - assert instalment_1.status == InstalmentStatus.ACCEPTED + assert instalment_1.status == InstalmentStatus.WAITING due_date = instalment_1.due_date now_date = timezone.now().date() @@ -743,11 +743,11 @@ def test_application_calculation_instalments( instalment_1.amount == handling_application.calculation.calculated_benefit_amount ) - assert instalment_1.status == InstalmentStatus.ACCEPTED + assert instalment_1.status == InstalmentStatus.WAITING if number_of_instalments == 2: assert instalment_1.amount == decimal.Decimal(settings.FIRST_INSTALMENT_LIMIT) - assert instalment_1.status == InstalmentStatus.ACCEPTED + assert instalment_1.status == InstalmentStatus.WAITING instalment_2 = handling_application.calculation.instalments.all()[1] assert ( From 787a868afe8f04d24d51eb6271def9bf344dd3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Mon, 2 Dec 2024 10:40:32 +0200 Subject: [PATCH 6/7] fix: decision datetime should be convert to a date --- backend/benefit/applications/models.py | 2 +- backend/benefit/applications/tests/conftest.py | 2 +- backend/benefit/applications/tests/test_models.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/benefit/applications/models.py b/backend/benefit/applications/models.py index 13e1b4e897..5a061de119 100755 --- a/backend/benefit/applications/models.py +++ b/backend/benefit/applications/models.py @@ -925,7 +925,7 @@ def update_batch_after_details_request( self.decision_maker_name = details.decision_maker_name self.decision_maker_title = details.decision_maker_title self.section_of_the_law = details.section_of_the_law - self.decision_date = details.decision_date + self.decision_date = details.decision_date.date() p2p_settings = AhjoSetting.objects.get(name="p2p_settings") self.p2p_checker_name = p2p_settings.data["acceptor_name"] diff --git a/backend/benefit/applications/tests/conftest.py b/backend/benefit/applications/tests/conftest.py index 47e8fbb8a1..92a116e01f 100755 --- a/backend/benefit/applications/tests/conftest.py +++ b/backend/benefit/applications/tests/conftest.py @@ -867,7 +867,7 @@ def decision_details(): decision_maker_name="Test Test", decision_maker_title="Test Title", section_of_the_law="16 §", - decision_date=date.today(), + decision_date=datetime.now(), ) diff --git a/backend/benefit/applications/tests/test_models.py b/backend/benefit/applications/tests/test_models.py index 9600f6dc5f..29e069316c 100755 --- a/backend/benefit/applications/tests/test_models.py +++ b/backend/benefit/applications/tests/test_models.py @@ -57,7 +57,7 @@ def test_application_batch_update_after_details_request( application_batch.decision_maker_title == decision_details.decision_maker_title ) assert application_batch.section_of_the_law == decision_details.section_of_the_law - assert application_batch.decision_date == decision_details.decision_date + assert application_batch.decision_date == decision_details.decision_date.date() assert application_batch.p2p_checker_name == p2p_settings.data["acceptor_name"] assert application_batch.p2p_inspector_name == p2p_settings.data["inspector_name"] From bda56f7a79503406bcdb071613d404408108c0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Wed, 4 Dec 2024 11:56:39 +0200 Subject: [PATCH 7/7] fix: copy/paste error message --- backend/benefit/applications/enums.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/benefit/applications/enums.py b/backend/benefit/applications/enums.py index 31f643051f..51f1e49f70 100644 --- a/backend/benefit/applications/enums.py +++ b/backend/benefit/applications/enums.py @@ -287,9 +287,9 @@ def __post_init__(self): if not self.decision_maker_name.strip(): raise ValueError("Decision maker name cannot be empty") if not self.decision_maker_title.strip(): - raise ValueError("Decision maker name cannot be empty") + raise ValueError("Decision maker title cannot be empty") if not self.section_of_the_law.strip(): - raise ValueError("Decision maker name cannot be empty") + raise ValueError("Section of the law cannot be empty") if not isinstance(self.decision_date, datetime): raise TypeError("decision_date must be a datetime object")