diff --git a/backend/benefit/applications/api/v1/serializers/application.py b/backend/benefit/applications/api/v1/serializers/application.py index 137aa0be4d..a663746bc9 100755 --- a/backend/benefit/applications/api/v1/serializers/application.py +++ b/backend/benefit/applications/api/v1/serializers/application.py @@ -1513,6 +1513,13 @@ def get_company_for_new_application(self, _): many=True, ) + handled_by_ahjo_automation = serializers.BooleanField( + read_only=True, + help_text=( + "True if the application has been handled by the Ahjo automation system" + ), + ) + @do_delayed_calls_at_end() # application recalculation def update(self, instance, validated_data): if not ApplicationStatus.is_applicant_editable_status(instance.status): @@ -1522,7 +1529,11 @@ def update(self, instance, validated_data): return self._base_update(instance, validated_data) class Meta(BaseApplicationSerializer.Meta): - fields = BaseApplicationSerializer.Meta.fields + ["batch", "has_batch"] + fields = BaseApplicationSerializer.Meta.fields + [ + "batch", + "has_batch", + "handled_by_ahjo_automation", + ] class HandlerApplicationSerializer(BaseApplicationSerializer): @@ -1590,6 +1601,14 @@ class HandlerApplicationSerializer(BaseApplicationSerializer): ), ) + handled_by_ahjo_automation = serializers.BooleanField( + read_only=False, + required=False, + help_text=( + "True if the application has been handled by the Ahjo automation system" + ), + ) + def get_changes(self, obj): return { "handler": get_application_change_history_made_by_handler(obj), @@ -1640,6 +1659,7 @@ class Meta(BaseApplicationSerializer.Meta): "paper_application_date", "decision_proposal_draft", "handler", + "handled_by_ahjo_automation", ] read_only_fields = BaseApplicationSerializer.Meta.read_only_fields + [ "latest_decision_comment", diff --git a/backend/benefit/applications/management/commands/send_ahjo_requests.py b/backend/benefit/applications/management/commands/send_ahjo_requests.py index ad264938d8..0b63a0d467 100644 --- a/backend/benefit/applications/management/commands/send_ahjo_requests.py +++ b/backend/benefit/applications/management/commands/send_ahjo_requests.py @@ -103,7 +103,12 @@ def get_applications_for_request( False, ) - return applications + # Only send applications that have automation enabled + applications_with_ahjo_automation = applications.filter( + handled_by_ahjo_automation=True + ) + + return applications_with_ahjo_automation def handle(self, *args, **options): try: @@ -160,6 +165,7 @@ def run_requests( request_handler = self._get_request_handler(ahjo_request_type) counter = 0 + for application in applications: sent_application, response_text = request_handler( application, ahjo_auth_token diff --git a/backend/benefit/applications/migrations/0075_application_handled_by_ahjo_automation_and_more.py b/backend/benefit/applications/migrations/0075_application_handled_by_ahjo_automation_and_more.py new file mode 100644 index 0000000000..58da58bbfd --- /dev/null +++ b/backend/benefit/applications/migrations/0075_application_handled_by_ahjo_automation_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.11 on 2024-06-10 14:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("applications", "0074_auto_20240606_1252"), + ] + + operations = [ + migrations.AddField( + model_name="application", + name="handled_by_ahjo_automation", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="historicalapplication", + name="handled_by_ahjo_automation", + field=models.BooleanField(default=False), + ), + ] diff --git a/backend/benefit/applications/models.py b/backend/benefit/applications/models.py index 8a195d6c2c..c6e19194c6 100755 --- a/backend/benefit/applications/models.py +++ b/backend/benefit/applications/models.py @@ -497,6 +497,7 @@ def get_available_benefit_types(self): # This is the diary number in Ahjo ahjo_case_id = models.CharField(max_length=64, null=True, blank=True) ahjo_case_guid = models.UUIDField(null=True, blank=True) + handled_by_ahjo_automation = models.BooleanField(default=False) handler = models.ForeignKey( User, diff --git a/backend/benefit/applications/tests/test_application_tasks.py b/backend/benefit/applications/tests/test_application_tasks.py index d87440bc5e..b354dd29b8 100755 --- a/backend/benefit/applications/tests/test_application_tasks.py +++ b/backend/benefit/applications/tests/test_application_tasks.py @@ -224,9 +224,14 @@ def test_send_ahjo_requests( else: mock_response = f"{uuid.uuid4()}" - applications = [MagicMock(spec=Application) for _ in range(number_to_send)] - - mock_get_applications.return_value = applications + applications = [ + MagicMock(spec=Application, handled_by_ahjo_automation=True) + for _ in range(number_to_send) + ] + applications_qs = MagicMock() + applications_qs.filter.return_value = applications + + mock_get_applications.return_value = applications_qs mock_send_request.return_value = ( application, mock_response, diff --git a/backend/benefit/applications/tests/test_applications_api.py b/backend/benefit/applications/tests/test_applications_api.py index 102a8b24bf..da525b8ea0 100755 --- a/backend/benefit/applications/tests/test_applications_api.py +++ b/backend/benefit/applications/tests/test_applications_api.py @@ -1307,30 +1307,32 @@ def test_application_status_change_as_applicant( @pytest.mark.parametrize( - "from_status,to_status,expected_code", + "from_status,to_status,expected_code,handled_by_ahjo_automation", [ - (ApplicationStatus.DRAFT, ApplicationStatus.RECEIVED, 200), - (ApplicationStatus.RECEIVED, ApplicationStatus.HANDLING, 200), + (ApplicationStatus.DRAFT, ApplicationStatus.RECEIVED, 200, False), + (ApplicationStatus.RECEIVED, ApplicationStatus.HANDLING, 200, True), ( ApplicationStatus.HANDLING, ApplicationStatus.ADDITIONAL_INFORMATION_NEEDED, 200, + False, ), ( ApplicationStatus.ADDITIONAL_INFORMATION_NEEDED, ApplicationStatus.HANDLING, 200, + False, ), - (ApplicationStatus.HANDLING, ApplicationStatus.ACCEPTED, 200), - (ApplicationStatus.HANDLING, ApplicationStatus.REJECTED, 200), - (ApplicationStatus.HANDLING, ApplicationStatus.CANCELLED, 200), - (ApplicationStatus.ACCEPTED, ApplicationStatus.HANDLING, 200), - (ApplicationStatus.REJECTED, ApplicationStatus.HANDLING, 200), - (ApplicationStatus.RECEIVED, ApplicationStatus.DRAFT, 400), - (ApplicationStatus.ACCEPTED, ApplicationStatus.RECEIVED, 400), - (ApplicationStatus.CANCELLED, ApplicationStatus.ACCEPTED, 400), - (ApplicationStatus.CANCELLED, ApplicationStatus.HANDLING, 400), - (ApplicationStatus.REJECTED, ApplicationStatus.DRAFT, 400), + (ApplicationStatus.HANDLING, ApplicationStatus.ACCEPTED, 200, False), + (ApplicationStatus.HANDLING, ApplicationStatus.REJECTED, 200, False), + (ApplicationStatus.HANDLING, ApplicationStatus.CANCELLED, 200, False), + (ApplicationStatus.ACCEPTED, ApplicationStatus.HANDLING, 200, False), + (ApplicationStatus.REJECTED, ApplicationStatus.HANDLING, 200, False), + (ApplicationStatus.RECEIVED, ApplicationStatus.DRAFT, 400, False), + (ApplicationStatus.ACCEPTED, ApplicationStatus.RECEIVED, 400, False), + (ApplicationStatus.CANCELLED, ApplicationStatus.ACCEPTED, 400, False), + (ApplicationStatus.CANCELLED, ApplicationStatus.HANDLING, 400, False), + (ApplicationStatus.REJECTED, ApplicationStatus.DRAFT, 400, False), ], ) @pytest.mark.parametrize("log_entry_comment", [None, "", "comment"]) @@ -1342,6 +1344,7 @@ def test_application_status_change_as_handler( from_status, to_status, expected_code, + handled_by_ahjo_automation, log_entry_comment, mailoutbox, ): @@ -1359,12 +1362,16 @@ def test_application_status_change_as_handler( application.refresh_from_db() data = HandlerApplicationSerializer(application).data data["status"] = to_status + + data["handled_by_ahjo_automation"] = handled_by_ahjo_automation + if log_entry_comment is not None: # the field is write-only data["log_entry_comment"] = log_entry_comment data["bases"] = [] # as of 2021-10, bases are not used when submitting application if to_status in [ApplicationStatus.RECEIVED, ApplicationStatus.HANDLING]: add_attachments_to_application(request, application) + data["handled_by_ahjo_automation"] = True if data["company"]["organization_type"] == OrganizationType.ASSOCIATION: data["association_has_business_activities"] = False data["association_immediate_manager_check"] = True @@ -1391,6 +1398,9 @@ def test_application_status_change_as_handler( application.log_entries.all().first().comment == expected_log_entry_comment ) + if handled_by_ahjo_automation: + assert response.data["handled_by_ahjo_automation"] is True + if to_status == ApplicationStatus.ADDITIONAL_INFORMATION_NEEDED: assert application.messages.count() == 1 assert ( diff --git a/backend/benefit/applications/tests/test_command_set_as_archived.py b/backend/benefit/applications/tests/test_command_set_as_archived.py index da43928a32..003e927c9d 100644 --- a/backend/benefit/applications/tests/test_command_set_as_archived.py +++ b/backend/benefit/applications/tests/test_command_set_as_archived.py @@ -29,6 +29,7 @@ "company_contact_person_phone_number": "05012345678", "company_contact_person_email": "test@example.com", "company_bank_account_number": "FI8149754587000402", + "handled_by_ahjo_automation": False, } diff --git a/frontend/benefit/handler/src/components/applicationReview/actions/receivedApplicationActions/ReceivedApplicationActions.tsx b/frontend/benefit/handler/src/components/applicationReview/actions/receivedApplicationActions/ReceivedApplicationActions.tsx index 280c342bf4..0f609c2ca8 100644 --- a/frontend/benefit/handler/src/components/applicationReview/actions/receivedApplicationActions/ReceivedApplicationActions.tsx +++ b/frontend/benefit/handler/src/components/applicationReview/actions/receivedApplicationActions/ReceivedApplicationActions.tsx @@ -1,3 +1,4 @@ +import { useDetermineAhjoMode } from 'benefit/handler/hooks/useDetermineAhjoMode'; import useHandlerReviewActions from 'benefit/handler/hooks/useHandlerReviewActions'; import useUpdateApplicationQuery from 'benefit/handler/hooks/useUpdateApplicationQuery'; import { APPLICATION_STATUSES } from 'benefit-shared/constants'; @@ -27,6 +28,8 @@ const ReceivedApplicationActions: React.FC = ({ const { mutate: updateApplication } = useUpdateApplicationQuery(); + const isNewAhjoMode = useDetermineAhjoMode(); + const handleStatusChange = (): void => { const currentApplicationData = snakecaseKeys( { @@ -49,6 +52,7 @@ const ReceivedApplicationActions: React.FC = ({ } : undefined, status: APPLICATION_STATUSES.HANDLING, + handled_by_ahjo_automation: isNewAhjoMode, }, { deep: true } ) as ApplicationData; diff --git a/frontend/benefit/shared/src/types/application.d.ts b/frontend/benefit/shared/src/types/application.d.ts index ecce08e534..4bfa4ba208 100644 --- a/frontend/benefit/shared/src/types/application.d.ts +++ b/frontend/benefit/shared/src/types/application.d.ts @@ -454,6 +454,7 @@ export type ApplicationData = { ahjo_case_id: string; archived_for_applicant?: boolean; is_granted_as_de_minimis_aid?: boolean | null; + handled_by_ahjo_automation?: boolean; }; export type EmployeeData = {