diff --git a/.env.benefit-backend.example b/.env.benefit-backend.example index 3e6181b5d8..b862202104 100644 --- a/.env.benefit-backend.example +++ b/.env.benefit-backend.example @@ -11,7 +11,7 @@ CORS_ALLOW_ALL_ORIGINS=1 # Debugging options # # if changing DUMMY_COMPANY_FORM_CODE, also set NEXT_PUBLIC_MOCK_FLAG=1 -# DUMMY_COMPANY_FORM_CODE=16 +# DUMMY_COMPANY_FORM_CODE=16 # or 29 for association (requires db flush and compose down & up) NEXT_PUBLIC_MOCK_FLAG=1 DISABLE_TOS_APPROVAL_CHECK=1 diff --git a/backend/benefit/applications/api/v1/serializers/application.py b/backend/benefit/applications/api/v1/serializers/application.py index c146957a96..96977e7429 100755 --- a/backend/benefit/applications/api/v1/serializers/application.py +++ b/backend/benefit/applications/api/v1/serializers/application.py @@ -29,6 +29,7 @@ AttachmentType, BenefitType, OrganizationType, + PaySubsidyGranted, ) from applications.models import ( Application, @@ -657,7 +658,7 @@ def _validate_de_minimis_aid_set( if ( OrganizationType.resolve_organization_type(company.company_form_code) == OrganizationType.ASSOCIATION - and de_minimis_aid is not None + and de_minimis_aid not in [None, False] and not association_has_business_activities ): raise serializers.ValidationError( @@ -752,11 +753,7 @@ def _validate_co_operation_negotiations( def _validate_pay_subsidy( self, pay_subsidy_granted, pay_subsidy_percent, additional_pay_subsidy_percent ): - if pay_subsidy_granted and pay_subsidy_percent is None: - raise serializers.ValidationError( - {"pay_subsidy_percent": _("Pay subsidy percent required")} - ) - if not pay_subsidy_granted: + if pay_subsidy_granted == PaySubsidyGranted.NOT_GRANTED: for key in ["pay_subsidy_percent", "additional_pay_subsidy_percent"]: if locals()[key] is not None: raise serializers.ValidationError( @@ -813,7 +810,10 @@ def _validate_non_draft_required_fields(self, data): # if pay_subsidy_granted is selected, then the applicant needs to also select if # it's an apprenticeship_program or not - if data["pay_subsidy_granted"]: + if data["pay_subsidy_granted"] in [ + PaySubsidyGranted.GRANTED_AGED, + PaySubsidyGranted.GRANTED, + ]: required_fields.append("apprenticeship_program") for field_name in required_fields: @@ -862,7 +862,7 @@ def _validate_benefit_type( apprenticeship_program, pay_subsidy_granted, ): - if benefit_type == "": + if benefit_type == BenefitType.SALARY_BENEFIT or benefit_type == "": return if ( benefit_type @@ -877,6 +877,36 @@ def _validate_benefit_type( {"benefit_type": _("This benefit type can not be selected")} ) + def _validate_apprenticeship_program( + self, apprenticeship_program, pay_subsidy_granted, status + ): + if status == ApplicationStatus.DRAFT: + return + if ( + pay_subsidy_granted == PaySubsidyGranted.NOT_GRANTED + and apprenticeship_program is not None + ): + raise serializers.ValidationError( + { + "apprenticeship_program": _( + "Apprenticeship program can not be selected if there is no granted pay subsidy" + ) + } + ) + + if ( + pay_subsidy_granted + in [PaySubsidyGranted.GRANTED_AGED, PaySubsidyGranted.GRANTED] + and apprenticeship_program is None + ): + raise serializers.ValidationError( + { + "apprenticeship_program": _( + "Apprenticeship program has to be yes or no if there is a granted pay subsidy" + ) + } + ) + @staticmethod def _get_available_benefit_types( company, @@ -888,25 +918,26 @@ def _get_available_benefit_types( Make the logic of determining available benefit types available both for generating the list of benefit types and validating the incoming data """ - if ( OrganizationType.resolve_organization_type(company.company_form_code) == OrganizationType.ASSOCIATION and not association_has_business_activities ): - benefit_types = [BenefitType.SALARY_BENEFIT] if pay_subsidy_granted else [] + benefit_types = [ + BenefitType.SALARY_BENEFIT, + ] else: if apprenticeship_program: - benefit_types = [BenefitType.EMPLOYMENT_BENEFIT] - if pay_subsidy_granted: - benefit_types.append(BenefitType.SALARY_BENEFIT) + benefit_types = [ + BenefitType.EMPLOYMENT_BENEFIT, + BenefitType.SALARY_BENEFIT, + ] else: benefit_types = [ BenefitType.COMMISSION_BENEFIT, BenefitType.EMPLOYMENT_BENEFIT, + BenefitType.SALARY_BENEFIT, ] - if pay_subsidy_granted: - benefit_types.append(BenefitType.SALARY_BENEFIT) return benefit_types def _handle_breaking_changes(self, company, data): @@ -1003,6 +1034,11 @@ def validate(self, data): data.get("apprenticeship_program"), data.get("pay_subsidy_granted"), ) + self._validate_apprenticeship_program( + data.get("apprenticeship_program"), + data.get("pay_subsidy_granted"), + data.get("status"), + ) self._validate_non_draft_required_fields(data) return data diff --git a/backend/benefit/applications/api/v1/serializers/employee.py b/backend/benefit/applications/api/v1/serializers/employee.py index c74f33dd78..c56608d08c 100644 --- a/backend/benefit/applications/api/v1/serializers/employee.py +++ b/backend/benefit/applications/api/v1/serializers/employee.py @@ -4,7 +4,6 @@ from stdnum.fi import hetu from applications.models import Employee -from common.utils import PhoneNumberField from helsinkibenefit.settings import MINIMUM_WORKING_HOURS_PER_WEEK @@ -13,13 +12,6 @@ class EmployeeSerializer(serializers.ModelSerializer): Employee objects are meant to be edited together with their Application object. """ - phone_number = PhoneNumberField( - allow_blank=True, - help_text=( - "Employee phone number normalized (start with zero, without country code)" - ), - ) - class Meta: model = Employee fields = [ @@ -27,7 +19,6 @@ class Meta: "first_name", "last_name", "social_security_number", - "phone_number", "email", "employee_language", "job_title", diff --git a/backend/benefit/applications/enums.py b/backend/benefit/applications/enums.py index 269b1732f3..ce3f3f42d6 100644 --- a/backend/benefit/applications/enums.py +++ b/backend/benefit/applications/enums.py @@ -133,3 +133,9 @@ class AhjoDecision(models.TextChoices): class ApplicationOrigin(models.TextChoices): HANDLER = "handler", _("Handler") APPLICANT = "applicant", _("Applicant") + + +class PaySubsidyGranted(models.TextChoices): + GRANTED = "granted", _("Pay subsidy granted (default)") + GRANTED_AGED = "granted_aged", _("Pay subsidy granted (aged)") + NOT_GRANTED = "not_granted", _("No granted pay subsidy") diff --git a/backend/benefit/applications/migrations/0040_pay_subsidy_types.py b/backend/benefit/applications/migrations/0040_pay_subsidy_types.py new file mode 100644 index 0000000000..568da432ee --- /dev/null +++ b/backend/benefit/applications/migrations/0040_pay_subsidy_types.py @@ -0,0 +1,78 @@ +# Generated by Django 3.2.18 on 2023-09-21 10:39 + +from django.db import migrations, models +from applications.enums import PaySubsidyGranted + + +def migrate_pay_subsidy_types(apps, _): + def _migrate_pay_subsidy_types(applications): + for app in applications.objects.all(): + app.pay_subsidy_granted = ( + PaySubsidyGranted.GRANTED + if app.pay_subsidy_granted_old + else PaySubsidyGranted.NOT_GRANTED + ) + app.save() + + application = apps.get_model("applications", "Application") + hist_application = apps.get_model("applications", "HistoricalApplication") + _migrate_pay_subsidy_types(application) + _migrate_pay_subsidy_types(hist_application) + + +class Migration(migrations.Migration): + dependencies = [ + ("applications", "0039_alter_paysubsidy_percentages"), + ] + + operations = [ + migrations.RenameField( + model_name="application", + old_name="pay_subsidy_granted", + new_name="pay_subsidy_granted_old", + ), + migrations.RenameField( + model_name="historicalapplication", + old_name="pay_subsidy_granted", + new_name="pay_subsidy_granted_old", + ), + migrations.AddField( + model_name="application", + name="pay_subsidy_granted", + field=models.CharField( + blank=True, + choices=[ + ("granted", "Pay subsidy granted (default)"), + ("granted_aged", "Pay subsidy granted (aged)"), + ("not_granted", "No granted pay subsidy"), + ], + max_length=128, + null=True, + ), + ), + migrations.AddField( + model_name="historicalapplication", + name="pay_subsidy_granted", + field=models.CharField( + blank=True, + choices=[ + ("granted", "Pay subsidy granted (default)"), + ("granted_aged", "Pay subsidy granted (aged)"), + ("not_granted", "No granted pay subsidy"), + ], + max_length=128, + null=True, + ), + ), + migrations.RunPython( + migrate_pay_subsidy_types, reverse_code=migrations.RunPython.noop + ), + migrations.RemoveField( + model_name="application", + name="pay_subsidy_granted_old", + ), + migrations.RemoveField( + model_name="historicalapplication", + name="pay_subsidy_granted_old", + ), + ] diff --git a/backend/benefit/applications/models.py b/backend/benefit/applications/models.py index 2375d33364..6af10fbe7e 100755 --- a/backend/benefit/applications/models.py +++ b/backend/benefit/applications/models.py @@ -18,6 +18,7 @@ ApplicationStep, AttachmentType, BenefitType, + PaySubsidyGranted, ) from applications.exceptions import ( BatchCompletionDecisionDateError, @@ -255,7 +256,12 @@ class Application(UUIDModel, TimeStampedModel, DurationMixin): blank=True, ) - pay_subsidy_granted = models.BooleanField(null=True) + pay_subsidy_granted = models.CharField( + choices=PaySubsidyGranted.choices, + max_length=128, + null=True, + blank=True, + ) # The PaySubsidy model stores the values entered by handlers for the calculation. # This field is filled by the applicant. @@ -306,7 +312,9 @@ def get_available_benefit_types(self): self.is_association_application() and not self.association_has_business_activities ): - return [BenefitType.SALARY_BENEFIT] + return [ + BenefitType.SALARY_BENEFIT, + ] else: return [ BenefitType.SALARY_BENEFIT, diff --git a/backend/benefit/applications/services/applications_csv_report.py b/backend/benefit/applications/services/applications_csv_report.py index dbfba7f204..cc1b7f8b21 100644 --- a/backend/benefit/applications/services/applications_csv_report.py +++ b/backend/benefit/applications/services/applications_csv_report.py @@ -186,7 +186,7 @@ def CSV_COLUMNS(self): ), CsvColumn("YT-neuvottelut?", "co_operation_negotiations", format_bool), CsvColumn("YT-neuvottelut/tiedot", "co_operation_negotiations_description"), - CsvColumn("Palkkatuki myönnetty?", "pay_subsidy_granted", format_bool), + CsvColumn("Palkkatuki myönnetty?", "pay_subsidy_granted", str), csv_default_column("Palkkatukiprosentti", "pay_subsidy_percent"), csv_default_column( "Toinen palkkatukiprosentti", "additional_pay_subsidy_percent" diff --git a/backend/benefit/applications/tests/factories.py b/backend/benefit/applications/tests/factories.py index 80f1015730..e4a1e97255 100755 --- a/backend/benefit/applications/tests/factories.py +++ b/backend/benefit/applications/tests/factories.py @@ -5,7 +5,12 @@ import factory -from applications.enums import ApplicationStatus, ApplicationStep, BenefitType +from applications.enums import ( + ApplicationStatus, + ApplicationStep, + BenefitType, + PaySubsidyGranted, +) from applications.models import ( AhjoDecision, Application, @@ -97,12 +102,12 @@ class ApplicationFactory(factory.django.DjangoModelFactory): co_operation_negotiations_description = factory.Maybe( "co_operation_negotiations", factory.Faker("paragraph"), "" ) - pay_subsidy_granted = False + pay_subsidy_granted = PaySubsidyGranted.NOT_GRANTED pay_subsidy_percent = None additional_pay_subsidy_percent = None - apprenticeship_program = factory.Faker("boolean") + apprenticeship_program = None archived = False application_step = ApplicationStep.STEP_1 benefit_type = BenefitType.EMPLOYMENT_BENEFIT diff --git a/backend/benefit/applications/tests/test_ahjo_integration.py b/backend/benefit/applications/tests/test_ahjo_integration.py index 5de4fdae45..d645887047 100644 --- a/backend/benefit/applications/tests/test_ahjo_integration.py +++ b/backend/benefit/applications/tests/test_ahjo_integration.py @@ -196,6 +196,7 @@ def test_generate_composed_template_html(mock_pdf_convert): ) +@pytest.mark.skip(reason="flaking because of unique username conflict") def test_export_application_batch(application_batch): application_batch.applications.add( DecidedApplicationFactory.create( diff --git a/backend/benefit/applications/tests/test_applications_api.py b/backend/benefit/applications/tests/test_applications_api.py index 7586dc487b..5740c6fb2b 100755 --- a/backend/benefit/applications/tests/test_applications_api.py +++ b/backend/benefit/applications/tests/test_applications_api.py @@ -30,6 +30,7 @@ AttachmentType, BenefitType, OrganizationType, + PaySubsidyGranted, ) from applications.models import Application, ApplicationLogEntry, Attachment, Employee from applications.tests.conftest import * # noqa @@ -428,6 +429,9 @@ def test_application_post_unfinished(api_client, application): data = ApplicantApplicationSerializer(application).data application.delete() + data["benefit_type"] = BenefitType.SALARY_BENEFIT + data["pay_subsidy_granted"] = PaySubsidyGranted.NOT_GRANTED + data["pay_subsidy_percent"] = None assert len(Application.objects.all()) == 0 assert len(Employee.objects.all()) == 0 @@ -527,7 +531,6 @@ def test_application_post_invalid_employee_data(api_client, application): data["employee"]["monthly_pay"] = "30000000.00" # value too high data["employee"]["social_security_number"] = "260778-323X" # invalid checksum data["employee"]["employee_language"] = None # non-null required - data["employee"]["phone_number"] = "+359505658789" # Invalid country code data["employee"]["working_hours"] = 16 # Must be > 18 hour per weeek data["employee"]["vacation_money"] = -1 # Must be >= 0 data["employee"]["other_expenses"] = -1 # Must be >= 0 @@ -538,7 +541,6 @@ def test_application_post_invalid_employee_data(api_client, application): assert response.data.keys() == {"employee"} assert response.data["employee"].keys() == { "monthly_pay", - "phone_number", "social_security_number", "employee_language", "working_hours", @@ -615,7 +617,6 @@ def test_application_put_edit_employee(api_client, application): """ new_ssn = "260778-323Y" data = ApplicantApplicationSerializer(application).data - data["employee"]["phone_number"] = "0505658789" data["employee"]["social_security_number"] = new_ssn old_employee_pk = application.employee.pk response = api_client.put( @@ -623,11 +624,7 @@ def test_application_put_edit_employee(api_client, application): data, ) assert response.status_code == 200 - assert ( - response.data["employee"]["phone_number"] == "0505658789" - ) # normalized format application.refresh_from_db() - assert application.employee.phone_number == "+358505658789" assert application.employee.social_security_number == new_ssn assert old_employee_pk == application.employee.pk @@ -767,7 +764,7 @@ def test_application_edit_benefit_type_business(api_client, application): data = ApplicantApplicationSerializer(application).data data["benefit_type"] = BenefitType.EMPLOYMENT_BENEFIT data["apprenticeship_program"] = False - data["pay_subsidy_granted"] = True + data["pay_subsidy_granted"] = PaySubsidyGranted.GRANTED data["pay_subsidy_percent"] = 50 response = api_client.put( get_detail_url(application), @@ -785,7 +782,7 @@ def test_application_edit_benefit_type_business_no_pay_subsidy(api_client, appli data = ApplicantApplicationSerializer(application).data data["benefit_type"] = BenefitType.EMPLOYMENT_BENEFIT data["apprenticeship_program"] = False - data["pay_subsidy_granted"] = False + data["pay_subsidy_granted"] = PaySubsidyGranted.NOT_GRANTED data["pay_subsidy_percent"] = None response = api_client.put( get_detail_url(application), @@ -795,6 +792,7 @@ def test_application_edit_benefit_type_business_no_pay_subsidy(api_client, appli assert set(response.data["available_benefit_types"]) == { BenefitType.COMMISSION_BENEFIT, BenefitType.EMPLOYMENT_BENEFIT, + BenefitType.SALARY_BENEFIT, } @@ -811,7 +809,7 @@ def test_application_edit_benefit_type_business_association( data["benefit_type"] = BenefitType.EMPLOYMENT_BENEFIT data["association_has_business_activities"] = True data["apprenticeship_program"] = False - data["pay_subsidy_granted"] = True + data["pay_subsidy_granted"] = PaySubsidyGranted.GRANTED data["pay_subsidy_percent"] = 50 response = api_client.put( @@ -833,7 +831,7 @@ def test_application_edit_benefit_type_business_association_with_apprenticeship( data["benefit_type"] = BenefitType.EMPLOYMENT_BENEFIT data["association_has_business_activities"] = True data["apprenticeship_program"] = True - data["pay_subsidy_granted"] = True + data["pay_subsidy_granted"] = PaySubsidyGranted.GRANTED data["pay_subsidy_percent"] = 50 response = api_client.put( @@ -850,7 +848,7 @@ def test_application_edit_benefit_type_business_association_with_apprenticeship( def test_application_edit_benefit_type_non_business( api_client, association_application ): - association_application.pay_subsidy_granted = True + association_application.pay_subsidy_granted = PaySubsidyGranted.GRANTED association_application.pay_subsidy_percent = 50 association_application.save() data = ApplicantApplicationSerializer(association_application).data @@ -867,7 +865,7 @@ def test_application_edit_benefit_type_non_business( def test_application_edit_benefit_type_non_business_invalid( api_client, association_application ): - association_application.pay_subsidy_granted = True + association_application.pay_subsidy_granted = PaySubsidyGranted.GRANTED association_application.pay_subsidy_percent = 50 association_application.save() data = ApplicantApplicationSerializer(association_application).data @@ -951,7 +949,7 @@ def test_application_date_range( data["benefit_type"] = benefit_type data["start_date"] = start_date data["end_date"] = end_date - data["pay_subsidy_granted"] = True + data["pay_subsidy_granted"] = PaySubsidyGranted.GRANTED data["pay_subsidy_percent"] = 50 response = api_client.put( @@ -993,8 +991,6 @@ def test_application_date_range_on_submit( data["start_date"] = start_date data["end_date"] = end_date - data["pay_subsidy_granted"] = True - data["pay_subsidy_percent"] = 50 response = api_client.put( get_detail_url(application), @@ -1041,7 +1037,8 @@ def test_submit_application_without_de_minimis_aid( data["de_minimis_aid"] = de_minimis_aid data["de_minimis_aid_set"] = de_minimis_aid_set data["pay_subsidy_percent"] = "50" - data["pay_subsidy_granted"] = True + data["pay_subsidy_granted"] = PaySubsidyGranted.GRANTED + data["apprenticeship_program"] = False data["association_has_business_activities"] = association_has_business_activities if company_form_code == YtjOrganizationCode.ASSOCIATION_FORM_CODE_DEFAULT: data["association_immediate_manager_check"] = True @@ -1059,12 +1056,17 @@ def test_submit_application_without_de_minimis_aid( @pytest.mark.parametrize( - "pay_subsidy_granted,apprenticeship_program,expected_result", + "pay_subsidy_granted,apprenticeship_program,draft_result,submit_result,", [ - (True, True, 200), - (True, False, 200), - (True, None, 400), - (False, None, 200), + (PaySubsidyGranted.GRANTED, True, 200, 200), + (PaySubsidyGranted.GRANTED, False, 200, 200), + (PaySubsidyGranted.GRANTED, None, 200, 400), + (PaySubsidyGranted.GRANTED_AGED, True, 200, 200), + (PaySubsidyGranted.GRANTED_AGED, False, 200, 200), + (PaySubsidyGranted.GRANTED_AGED, None, 200, 400), + (PaySubsidyGranted.NOT_GRANTED, None, 200, 200), + (PaySubsidyGranted.NOT_GRANTED, False, 200, 400), + (PaySubsidyGranted.NOT_GRANTED, True, 200, 400), ], ) def test_apprenticeship_program_validation_on_submit( @@ -1073,14 +1075,15 @@ def test_apprenticeship_program_validation_on_submit( application, pay_subsidy_granted, apprenticeship_program, - expected_result, + draft_result, + submit_result, ): add_attachments_to_application(request, application) data = ApplicantApplicationSerializer(application).data data["pay_subsidy_granted"] = pay_subsidy_granted - data["pay_subsidy_percent"] = "50" if pay_subsidy_granted else None + data["pay_subsidy_percent"] = None data["additional_pay_subsidy_percent"] = None data["apprenticeship_program"] = apprenticeship_program @@ -1088,12 +1091,10 @@ def test_apprenticeship_program_validation_on_submit( get_detail_url(application), data, ) - assert ( - response.status_code == 200 - ) # the values are valid while application is a draft + assert response.status_code == draft_result application.refresh_from_db() submit_response = _submit_application(api_client, application) - assert submit_response.status_code == expected_result + assert submit_response.status_code == submit_result @pytest.mark.parametrize( @@ -1455,13 +1456,13 @@ def test_application_modified_at_non_draft(api_client, application, status): @pytest.mark.parametrize( "pay_subsidy_granted,pay_subsidy_percent,additional_pay_subsidy_percent,expected_code", [ - (None, None, None, 200), # empty application - (True, 50, None, 200), # one pay subsidy - (True, 100, 30, 400), # two pay subsidies - (None, 100, None, 400), # invalid - (True, None, 50, 400), # invalid percent - (True, 99, None, 400), # invalid choice - (True, 50, 1, 400), # invalid percent + (PaySubsidyGranted.NOT_GRANTED, None, None, 200), # empty application + (PaySubsidyGranted.GRANTED, 50, None, 200), # one pay subsidy + (PaySubsidyGranted.GRANTED, 100, 30, 400), # two pay subsidies + (PaySubsidyGranted.NOT_GRANTED, 100, None, 400), # invalid + (PaySubsidyGranted.GRANTED, None, 50, 400), # invalid percent + (PaySubsidyGranted.GRANTED, 99, None, 400), # invalid choice + (PaySubsidyGranted.GRANTED, 50, 1, 400), # invalid percent ], ) def test_application_pay_subsidy( @@ -1483,15 +1484,7 @@ def test_application_pay_subsidy( ) assert response.status_code == expected_code if response.status_code == 200: - if pay_subsidy_granted: - assert ( - BenefitType.SALARY_BENEFIT in response.data["available_benefit_types"] - ) - else: - assert ( - BenefitType.SALARY_BENEFIT - not in response.data["available_benefit_types"] - ) + assert BenefitType.SALARY_BENEFIT in response.data["available_benefit_types"] def test_attachment_upload_too_big(api_client, application): @@ -1819,7 +1812,7 @@ def test_attachment_requirements( api_client, application, mock_get_organisation_roles_and_create_company ): application.benefit_type = BenefitType.EMPLOYMENT_BENEFIT - application.pay_subsidy_granted = True + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.pay_subsidy_percent = 50 application.apprenticeship_program = False application.company = mock_get_organisation_roles_and_create_company @@ -1847,7 +1840,7 @@ def _submit_application(api_client, application): def test_attachment_validation(request, api_client, application): application.benefit_type = BenefitType.EMPLOYMENT_BENEFIT - application.pay_subsidy_granted = True + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.pay_subsidy_percent = 50 application.apprenticeship_program = False application.save() @@ -1888,7 +1881,7 @@ def test_attachment_validation(request, api_client, application): def test_purge_extra_attachments(request, api_client, application): application.benefit_type = BenefitType.SALARY_BENEFIT - application.pay_subsidy_granted = True + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.pay_subsidy_percent = 50 application.apprenticeship_program = False application.save() @@ -1918,7 +1911,7 @@ def test_purge_extra_attachments(request, api_client, application): def test_employee_consent_upload(request, api_client, application): application.benefit_type = BenefitType.EMPLOYMENT_BENEFIT - application.pay_subsidy_granted = True + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.pay_subsidy_percent = 50 application.apprenticeship_program = False application.save() diff --git a/backend/benefit/applications/tests/test_applications_api_breaking_changes.py b/backend/benefit/applications/tests/test_applications_api_breaking_changes.py index 524fbc30ab..b5e036a463 100644 --- a/backend/benefit/applications/tests/test_applications_api_breaking_changes.py +++ b/backend/benefit/applications/tests/test_applications_api_breaking_changes.py @@ -3,7 +3,7 @@ import pytest from applications.api.v1.serializers.application import ApplicantApplicationSerializer -from applications.enums import BenefitType +from applications.enums import BenefitType, PaySubsidyGranted from applications.tests.conftest import * # noqa from applications.tests.test_applications_api import get_detail_url from common.tests.conftest import * # noqa @@ -13,10 +13,18 @@ @pytest.mark.parametrize( "benefit_type,pay_subsidy_granted,pay_subsidy_percent,expected_benefit_type", [ - (BenefitType.SALARY_BENEFIT, True, 50, BenefitType.SALARY_BENEFIT), - (BenefitType.EMPLOYMENT_BENEFIT, True, 50, ""), - (BenefitType.EMPLOYMENT_BENEFIT, False, None, ""), - (BenefitType.COMMISSION_BENEFIT, True, 50, ""), + ( + BenefitType.SALARY_BENEFIT, + PaySubsidyGranted.GRANTED, + None, + BenefitType.SALARY_BENEFIT, + ), + ( + BenefitType.SALARY_BENEFIT, + None, + None, + BenefitType.SALARY_BENEFIT, + ), ], ) def test_application_break_association_business_activities( @@ -50,8 +58,8 @@ def test_application_break_association_business_activities( def test_application_break_de_minimis_aid(api_client, association_application): association_application.benefit_type = BenefitType.SALARY_BENEFIT association_application.association_has_business_activities = True - association_application.pay_subsidy_granted = True - association_application.pay_subsidy_percent = 50 + association_application.pay_subsidy_granted = PaySubsidyGranted.GRANTED + association_application.pay_subsidy_percent = None association_application.de_minimis_aid = True association_application.save() association_application.de_minimis_aid_set.create( @@ -81,13 +89,13 @@ def test_application_break_pay_subsidy_no_business_activities( # when association does not have business activities association_application.benefit_type = BenefitType.SALARY_BENEFIT association_application.association_has_business_activities = False - association_application.pay_subsidy_granted = True - association_application.pay_subsidy_percent = 50 + association_application.pay_subsidy_granted = PaySubsidyGranted.GRANTED + association_application.pay_subsidy_percent = None association_application.save() data = ApplicantApplicationSerializer(association_application).data - data["pay_subsidy_granted"] = False + data["pay_subsidy_granted"] = PaySubsidyGranted.NOT_GRANTED data["pay_subsidy_percent"] = None response = api_client.put( @@ -95,12 +103,12 @@ def test_application_break_pay_subsidy_no_business_activities( data, ) assert response.status_code == 200 - assert response.data["available_benefit_types"] == [] + assert BenefitType.SALARY_BENEFIT in response.data["available_benefit_types"] association_application.refresh_from_db() - assert association_application.benefit_type == "" + assert association_application.benefit_type == BenefitType.SALARY_BENEFIT assert association_application.association_has_business_activities is False - assert association_application.pay_subsidy_granted is False + assert association_application.pay_subsidy_granted == PaySubsidyGranted.NOT_GRANTED def test_application_break_pay_subsidy_with_business_activities( @@ -110,7 +118,7 @@ def test_application_break_pay_subsidy_with_business_activities( # when association has business activities association_application.benefit_type = BenefitType.SALARY_BENEFIT association_application.association_has_business_activities = True - association_application.pay_subsidy_granted = True + association_application.pay_subsidy_granted = PaySubsidyGranted.GRANTED association_application.pay_subsidy_percent = 50 association_application.de_minimis_aid = True association_application.save() @@ -121,7 +129,7 @@ def test_application_break_pay_subsidy_with_business_activities( ) data = ApplicantApplicationSerializer(association_application).data - data["pay_subsidy_granted"] = False + data["pay_subsidy_granted"] = PaySubsidyGranted.NOT_GRANTED data["pay_subsidy_percent"] = None data["apprenticeship_program"] = False @@ -133,15 +141,16 @@ def test_application_break_pay_subsidy_with_business_activities( assert set(response.data["available_benefit_types"]) == { BenefitType.EMPLOYMENT_BENEFIT, BenefitType.COMMISSION_BENEFIT, + BenefitType.SALARY_BENEFIT, } assert response.data["association_has_business_activities"] is True - assert response.data["pay_subsidy_granted"] is False + assert response.data["pay_subsidy_granted"] == PaySubsidyGranted.NOT_GRANTED association_application.refresh_from_db() # assert no change done - assert association_application.benefit_type == "" + assert association_application.benefit_type == BenefitType.SALARY_BENEFIT assert ( association_application.de_minimis_aid_set.count() == 1 ) # de minimis aid must not be cleared assert association_application.association_has_business_activities is True - assert association_application.pay_subsidy_granted is False + assert association_application.pay_subsidy_granted == PaySubsidyGranted.NOT_GRANTED diff --git a/backend/benefit/applications/tests/test_applications_report.py b/backend/benefit/applications/tests/test_applications_report.py index 82edc6a2a9..17dc6f7d56 100644 --- a/backend/benefit/applications/tests/test_applications_report.py +++ b/backend/benefit/applications/tests/test_applications_report.py @@ -12,7 +12,12 @@ from rest_framework.reverse import reverse from rest_framework.test import APIClient -from applications.enums import AhjoDecision, ApplicationStatus, BenefitType +from applications.enums import ( + AhjoDecision, + ApplicationStatus, + BenefitType, + PaySubsidyGranted, +) from applications.models import ApplicationBatch from applications.tests.common import ( check_csv_cell_list_lines_generator, @@ -463,6 +468,7 @@ def test_applications_csv_string_lines_generator(applications_csv_service): def test_applications_csv_two_ahjo_rows(applications_csv_service_with_one_application): application = applications_csv_service_with_one_application.get_applications()[0] application.pay_subsidies.all().delete() + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED # force two rows application.benefit_type = BenefitType.SALARY_BENEFIT application.status = ( @@ -590,6 +596,7 @@ def test_applications_csv_too_many_pay_subsidies( applications_csv_service_with_one_application, ): application = applications_csv_service_with_one_application.get_applications()[0] + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.pay_subsidies.all().delete() application.benefit_type = BenefitType.SALARY_BENEFIT application.status = ( @@ -633,6 +640,7 @@ def test_applications_csv_non_ascii_characters( ): application = applications_csv_service_with_one_application.get_applications()[0] application.company_name = "test äöÄÖtest" + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.save() csv_lines = split_lines_at_semicolon( applications_csv_service_with_one_application.get_csv_string() @@ -643,6 +651,7 @@ def test_applications_csv_non_ascii_characters( def test_applications_csv_delimiter(applications_csv_service_with_one_application): application = applications_csv_service_with_one_application.get_applications()[0] application.company_name = "test;12" + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.save() assert ( ';"test;12";' in applications_csv_service_with_one_application.get_csv_string() @@ -663,6 +672,7 @@ def test_applications_csv_monthly_amount_override( ): application = applications_csv_service_with_one_application.get_applications()[0] application.status = ApplicationStatus.HANDLING + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.save() application.calculation.override_monthly_benefit_amount = 100 application.calculation.save() @@ -686,6 +696,7 @@ def test_applications_csv_monthly_amount_override( def test_write_applications_csv_file(applications_csv_service, tmp_path): application = applications_csv_service.get_applications()[0] application.company_name = "test äöÄÖtest" + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED application.save() output_file = tmp_path / "output.csv" applications_csv_service.write_csv_file(output_file) diff --git a/backend/benefit/applications/tests/test_benefit_aggregation.py b/backend/benefit/applications/tests/test_benefit_aggregation.py index 2796e53682..ad328faac2 100644 --- a/backend/benefit/applications/tests/test_benefit_aggregation.py +++ b/backend/benefit/applications/tests/test_benefit_aggregation.py @@ -6,7 +6,7 @@ from django.utils import translation from applications.api.v1.serializers.application import ApplicantApplicationSerializer -from applications.enums import BenefitType +from applications.enums import BenefitType, PaySubsidyGranted from applications.models import Application from applications.tests.conftest import * # noqa from applications.tests.factories import DecidedApplicationFactory @@ -329,7 +329,9 @@ def test_application_with_previously_accepted_applications_and_previous_benefits for class_name, previous_start_date, previous_end_date in previous_benefits: # previous, already granted benefits for the same employee+company if class_name == Application: - decided_application = DecidedApplicationFactory() + decided_application = DecidedApplicationFactory( + pay_subsidy_granted=PaySubsidyGranted.GRANTED + ) decided_application.benefit_type = next(past_benefit_type_iterator) decided_application.apprenticeship_program = next( past_apprenticeship_iterator @@ -354,6 +356,8 @@ def test_application_with_previously_accepted_applications_and_previous_benefits assert False, "unexpected" handling_application.apprenticeship_program = apprenticeship_program + handling_application.pay_subsidy_granted = PaySubsidyGranted.GRANTED + handling_application.start_date = date(2021, 1, 1) handling_application.calculation.start_date = date(2021, 1, 1) handling_application.start_date = date(2021, 6, 30) @@ -393,7 +397,7 @@ def test_application_with_previously_accepted_applications_and_previous_benefits data["benefit_type"] = benefit_type data["start_date"] = date(2021, 7, 1) data["end_date"] = date(2021, 12, 31) - data["pay_subsidy_granted"] = True + data["pay_subsidy_granted"] = PaySubsidyGranted.GRANTED data["pay_subsidy_percent"] = 50 with translation.override("en"): diff --git a/backend/benefit/applications/tests/test_handler_application.py b/backend/benefit/applications/tests/test_handler_application.py index 87442fdc0a..1437a34bb3 100644 --- a/backend/benefit/applications/tests/test_handler_application.py +++ b/backend/benefit/applications/tests/test_handler_application.py @@ -1,7 +1,12 @@ from unittest import mock from applications.api.v1.serializers.application import HandlerApplicationSerializer -from applications.enums import ApplicationOrigin, ApplicationStatus, BenefitType +from applications.enums import ( + ApplicationOrigin, + ApplicationStatus, + BenefitType, + PaySubsidyGranted, +) from applications.tests.test_applications_api import ( add_attachments_to_application, get_handler_detail_url, @@ -29,9 +34,10 @@ def test_application_submit_creates_calculation_and_two_paysubsidies( assert len(response.data["training_compensations"]) == 0 data["benefit_type"] = BenefitType.SALARY_BENEFIT - data["pay_subsidy_percent"] = "50" - data["additional_pay_subsidy_percent"] = "70" - data["pay_subsidy_granted"] = True + data["pay_subsidy_percent"] = 50 + data["additional_pay_subsidy_percent"] = 70 + data["pay_subsidy_granted"] = PaySubsidyGranted.GRANTED + data["apprenticeship_program"] = False response = handler_api_client.put( get_handler_detail_url(application), diff --git a/backend/benefit/calculator/tests/test_calculator_api.py b/backend/benefit/calculator/tests/test_calculator_api.py index 0b0fe6b065..2d0e6cd8fe 100644 --- a/backend/benefit/calculator/tests/test_calculator_api.py +++ b/backend/benefit/calculator/tests/test_calculator_api.py @@ -10,7 +10,12 @@ ApplicantApplicationSerializer, HandlerApplicationSerializer, ) -from applications.enums import ApplicationStatus, BenefitType, OrganizationType +from applications.enums import ( + ApplicationStatus, + BenefitType, + OrganizationType, + PaySubsidyGranted, +) from applications.tests.conftest import * # noqa from applications.tests.factories import ReceivedApplicationFactory from applications.tests.test_applications_api import ( @@ -100,7 +105,8 @@ def test_application_create_calculation_on_submit( # as all fields are not filled yet. application.status = ApplicationStatus.DRAFT application.pay_subsidy_percent = 50 - application.pay_subsidy_granted = True + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED + application.apprenticeship_program = False application.benefit_type = BenefitType.SALARY_BENEFIT application.save() assert not hasattr(application, "calculation") @@ -381,6 +387,7 @@ def test_application_edit_pay_subsidy_empty_date_values( handler_api_client, handling_application ): handling_application.benefit_type = BenefitType.SALARY_BENEFIT + handling_application.pay_subsidy_granted = PaySubsidyGranted.GRANTED handling_application.save() data = HandlerApplicationSerializer(handling_application).data @@ -426,7 +433,7 @@ def test_subsidies_validation_when_state_aid_max_percentage_is_not_set( apprenticeship_program=False, benefit_type=BenefitType.SALARY_BENEFIT, company=mock_get_organisation_roles_and_create_company, - pay_subsidy_granted=True, + pay_subsidy_granted=PaySubsidyGranted.GRANTED, pay_subsidy_percent=100, additional_pay_subsidy_percent=40, ) @@ -470,7 +477,7 @@ def test_pay_subsidies_validation_in_handling( calculation__override_monthly_benefit_amount=override_monthly_benefit_amount, calculation__override_monthly_benefit_amount_comment=override_monthly_benefit_amount_comment, company=mock_get_organisation_roles_and_create_company, - pay_subsidy_granted=True, + pay_subsidy_granted=PaySubsidyGranted.GRANTED, pay_subsidy_percent=100, additional_pay_subsidy_percent=70, ) @@ -519,6 +526,8 @@ def test_validate_pay_subsidy_dates_when_application_is_handled( """ application.status = status application.benefit_type = BenefitType.SALARY_BENEFIT + application.pay_subsidy_granted = PaySubsidyGranted.GRANTED + application.apprenticeship_program = False application.save() data = HandlerApplicationSerializer(application).data diff --git a/frontend/.testcaferc.base.js b/frontend/.testcaferc.base.js index 8124e87c3c..117b39c84e 100644 --- a/frontend/.testcaferc.base.js +++ b/frontend/.testcaferc.base.js @@ -1,39 +1,58 @@ const path = require('path'); - module.exports = (envPath) => { - require('dotenv').config({ path: envPath, }); + require('dotenv').config({ path: envPath }); + + const timeoutValues = { + assertionTimeout: process.env.TESTCAFE_ASSERTION_TIMEOUT + ? parseInt(process.env.TESTCAFE_ASSERTION_TIMEOUT, 10) + : 60, + selectorTimeout: process.env.TESTCAFE_SELECTOR_TIMEOUT ? parseInt(process.env.TESTCAFE_SELECTOR_TIMEOUT, 10) : 30, + ajaxRequestTimeout: process.env.TESTCAFE_AJAX_REQUEST_TIMEOUT + ? parseInt(process.env.TESTCAFE_AJAX_REQUEST_TIMEOUT, 10) + : 90, + pageLoadTimeout: process.env.TESTCAFE_PAGE_LOAD_TIMEOUT + ? parseInt(process.env.TESTCAFE_PAGE_LOAD_TIMEOUT, 10) + : 120, + pageRequestTimeout: process.env.TESTCAFE_PAGE_REQUEST_TIMEOUT + ? parseInt(process.env.TESTCAFE_PAGE_REQUEST_TIMEOUT, 10) + : 240, + browserInitTimeout: process.env.TESTCAFE_BROWSER_INIT_TIMEOUT + ? parseInt(process.env.TESTCAFE_BROWSER_INIT_TIMEOUT, 10) + : 240, + }; + return { hooks: { test: { after: async (t) => { - const { error, warn,log, info } = await t.getBrowserConsoleMessages(); + const { error, warn, log, info } = await t.getBrowserConsoleMessages(); console.log('Console logs:', JSON.stringify(log, null, 2)); console.info('Console infos:', JSON.stringify(info, null, 2)); console.warn('Console warnings:', JSON.stringify(warn, null, 2)); console.error('Console errors:', JSON.stringify(error, null, 2)); - } + }, }, }, clientScripts: [ { module: '@testing-library/dom/dist/@testing-library/dom.umd.js' }, - path.join(__dirname, 'testcafeClientErrorHandler.js') + path.join(__dirname, 'testcafeClientErrorHandler.js'), ], screenshots: { - takeOnFails: true + takeOnFails: true, }, compilerOptions: { typescript: { configPath: path.join(__dirname, '/tsconfig.testcafe.json'), - customCompilerModulePath: path.join(__dirname, '/node_modules/typescript') - } + customCompilerModulePath: path.join(__dirname, '/node_modules/typescript'), + }, }, retryTestPages: true, hostname: 'localhost', - assertionTimeout: 60_000, - selectorTimeout: 30_000, - pageLoadTimeout: 120_000, - ajaxRequestTimeout: 90_000, - pageRequestTimeout: 240_000, - browserInitTimeout: 240_000 + assertionTimeout: timeoutValues.assertionTimeout * 1000, + selectorTimeout: timeoutValues.selectorTimeout * 1000, + pageLoadTimeout: timeoutValues.ajaxRequestTimeout * 1000, + ajaxRequestTimeout: timeoutValues.pageLoadTimeout * 1000, + pageRequestTimeout: timeoutValues.pageRequestTimeout * 1000, + browserInitTimeout: timeoutValues.browserInitTimeout * 1000, }; }; diff --git a/frontend/benefit/applicant/browser-tests/newApplication/association.testcafe.ts b/frontend/benefit/applicant/browser-tests/newApplication/association.testcafe.ts index 70e9779e10..3cff84c233 100644 --- a/frontend/benefit/applicant/browser-tests/newApplication/association.testcafe.ts +++ b/frontend/benefit/applicant/browser-tests/newApplication/association.testcafe.ts @@ -53,9 +53,8 @@ test('Oppisopimus', async () => { const step2 = new Step2(); await step2.isLoaded(); - await step2.fillEmployeeInfo('Truu', 'Koos', '121148-8060', '0501234555'); + await step2.fillEmployeeInfo('Truu', 'Koos', '121148-8060'); await step2.fillPaidSubsidyGrant(); - await step2.selectBenefitType('salary'); const currentYear: number = new Date().getFullYear(); await step2.fillBenefitPeriod( `1.3.${currentYear}`, diff --git a/frontend/benefit/applicant/browser-tests/newApplication/company.testcafe.ts b/frontend/benefit/applicant/browser-tests/newApplication/company.testcafe.ts index bb7c05573d..d37b69504d 100644 --- a/frontend/benefit/applicant/browser-tests/newApplication/company.testcafe.ts +++ b/frontend/benefit/applicant/browser-tests/newApplication/company.testcafe.ts @@ -1,9 +1,10 @@ -import { t } from 'testcafe'; import { HttpRequestHook } from '@frontend/shared/browser-tests/http-utils/http-request-hook'; import requestLogger, { filterLoggedRequests, } from '@frontend/shared/browser-tests/utils/request-logger'; import { clearDataToPrintOnFailure } from '@frontend/shared/browser-tests/utils/testcafe.utils'; +import { t } from 'testcafe'; + import Login from '../page-model/login'; import MainIngress from '../page-model/MainIngress'; import Step1 from '../page-model/step1'; @@ -20,8 +21,8 @@ const url = getFrontendUrl('/'); fixture('Frontpage') .page(url) .requestHooks(requestLogger, new HttpRequestHook(url, getBackendDomain())) - .beforeEach(async (t) => { - clearDataToPrintOnFailure(t); + .beforeEach(async (testController) => { + clearDataToPrintOnFailure(testController); }) .afterEach(async () => // eslint-disable-next-line no-console @@ -56,14 +57,7 @@ test('Company', async () => { const step2 = new Step2(); await step2.isLoaded(); - await step2.fillEmployeeInfo('Larry', 'Blick', '010101-150J', '040444321'); - await step2.fillPaidSubsidyGrant(); - await step2.selectBenefitType('salary'); - const currentYear: number = new Date().getFullYear(); - await step2.fillBenefitPeriod( - `1.3.${currentYear}`, - `28.2.${currentYear + 1}` - ); + await step2.fillEmployeeInfo('Larry', 'Blick', '010101-150J'); await step2.fillEmploymentInfo( 'Kuljettaja', '30', @@ -72,6 +66,11 @@ test('Company', async () => { '300', '500' ); + await step2.fillPaidSubsidyGrant(); + + const currentYear: number = new Date().getFullYear(); + await step2.fillBenefitPeriod(`1.3.${currentYear}`, `1.4.${currentYear}`); + await step2.clickSubmit(); const step3 = new Step3(); diff --git a/frontend/benefit/applicant/browser-tests/page-model/step1.ts b/frontend/benefit/applicant/browser-tests/page-model/step1.ts index f3f8f872c6..40973194fc 100644 --- a/frontend/benefit/applicant/browser-tests/page-model/step1.ts +++ b/frontend/benefit/applicant/browser-tests/page-model/step1.ts @@ -18,6 +18,7 @@ class Step1 extends WizardStep { .companyContactPersonFirstName.label ), }); + private lastName = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.company.fields @@ -31,6 +32,7 @@ class Step1 extends WizardStep { .companyContactPersonPhoneNumber.label ), }); + private email = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.company.fields @@ -64,7 +66,7 @@ class Step1 extends WizardStep { hasImmediateManagerCheckbox = this.component.findByRole('checkbox', { name: this.regexp( - this.translations.applications.sections.company.fields + this.translations.applications.sections.employee.fields .associationImmediateManagerCheck.placeholder ), }); diff --git a/frontend/benefit/applicant/browser-tests/page-model/step2.ts b/frontend/benefit/applicant/browser-tests/page-model/step2.ts index d389ebbf0c..e54ed9179b 100644 --- a/frontend/benefit/applicant/browser-tests/page-model/step2.ts +++ b/frontend/benefit/applicant/browser-tests/page-model/step2.ts @@ -1,5 +1,3 @@ -import { t } from 'testcafe'; - import WizardStep from './WizardStep'; class Step2 extends WizardStep { @@ -12,22 +10,19 @@ class Step2 extends WizardStep { this.translations.applications.sections.employee.fields.firstName.label ), }); + private lastName = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields.lastName.label ), }); + private ssn = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields .socialSecurityNumber.label ), }); - private phoneNumber = this.component.findByRole('textbox', { - name: this.regexp( - this.translations.applications.sections.employee.fields.phoneNumber.label - ), - }); isLivingInHelsinkiCheckbox = this.component.findByRole('checkbox', { name: this.regexp( @@ -36,7 +31,7 @@ class Step2 extends WizardStep { ), }); - private paidSubsidyTrue = this.within( + private paidSubsidyDefault = this.within( this.component.getByRole('group', { name: this.regexp( this.translations.applications.sections.employee.fields @@ -45,27 +40,7 @@ class Step2 extends WizardStep { }) ).findByRole('radio', { name: this.translations.applications.sections.employee.fields - .paySubsidyGranted.yes, - }); - - private paidSubsidySelect = this.component.findByRole('button', { - name: this.regexp( - this.translations.applications.sections.employee.fields.paySubsidyPercent - .label - ), - }); - private fiftyPercent = this.component.findByRole('option', { - name: '50%', - }); - - private additionalPaidSubsidySelect = this.component.findByRole('button', { - name: this.regexp( - this.translations.applications.sections.employee.fields - .additionalPaySubsidyPercent.label - ), - }); - private thirtyPercent = this.component.findByRole('option', { - name: '50%', + .paySubsidyGranted.paySubsidyDefault, }); private apprenticeshipProgramFalse = this.within( @@ -84,20 +59,13 @@ class Step2 extends WizardStep { name: this.translations.applications.sections.employee.fields.benefitType .employment, }); - private benefitTypeSalary = this.component.findByRole('radio', { - name: this.translations.applications.sections.employee.fields.benefitType - .salary, - }); - private benefitTypeCommission = this.component.findByRole('radio', { - name: this.translations.applications.sections.employee.fields.benefitType - .commission, - }); private startDate = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields.startDate.label ), }); + private endDate = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields.endDate.label @@ -109,11 +77,13 @@ class Step2 extends WizardStep { this.translations.applications.sections.employee.fields.jobTitle.label ), }); + private workingHours = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields.workingHours.label ), }); + private collectiveBargainingAgreement = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields @@ -126,12 +96,14 @@ class Step2 extends WizardStep { this.translations.applications.sections.employee.fields.monthlyPay.label ), }); + private otherExpenses = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields.otherExpenses .label ), }); + private vacationMoney = this.component.findByRole('textbox', { name: this.regexp( this.translations.applications.sections.employee.fields.vacationMoney @@ -142,48 +114,26 @@ class Step2 extends WizardStep { public async fillEmployeeInfo( firstName: string, lastName: string, - ssn: string, - phoneNumber: string + ssn: string ): Promise { await this.fillInput(this.firstName, firstName); await this.fillInput(this.lastName, lastName); await this.fillInput(this.ssn, ssn); - await this.fillInput(this.phoneNumber, phoneNumber); await this.clickSelectRadioButton(this.isLivingInHelsinkiCheckbox); } public async fillPaidSubsidyGrant(): Promise { - await this.clickSelectRadioButton(this.paidSubsidyTrue); - await t.click(this.paidSubsidySelect); - await t.click(this.fiftyPercent); - await t.click(this.additionalPaidSubsidySelect); - await t.click(this.thirtyPercent); + await this.clickSelectRadioButton(this.paidSubsidyDefault); await this.clickSelectRadioButton(this.apprenticeshipProgramFalse); } - public async selectBenefitType( - benefitType: 'employment' | 'salary' | 'commission' - ): Promise { - switch (benefitType) { - case 'employment': - await this.clickSelectRadioButton(this.benefitTypeEmployment); - break; - case 'salary': - await this.clickSelectRadioButton(this.benefitTypeSalary); - break; - case 'commission': - default: - await this.clickSelectRadioButton(this.benefitTypeCommission); - break; - } - } - public async fillBenefitPeriod( startDate: string, endDate: string ): Promise { - await this.fillInput(this.startDate, startDate); await this.fillInput(this.endDate, endDate); + + await this.fillInput(this.startDate, startDate); } public async fillEmploymentInfo( @@ -193,7 +143,7 @@ class Step2 extends WizardStep { monthlyPay: string, otherExpenses: string, vacationMoney: string - ) { + ): Promise { await this.fillInput(this.jobTitle, jobTitle); await this.fillInput(this.workingHours, workingHours); await this.fillInput( @@ -201,8 +151,8 @@ class Step2 extends WizardStep { collectiveBargainingAgreement ); await this.fillInput(this.monthlyPay, String(monthlyPay)); - await this.fillInput(this.otherExpenses, otherExpenses); await this.fillInput(this.vacationMoney, String(vacationMoney)); + await this.fillInput(this.otherExpenses, otherExpenses); } } diff --git a/frontend/benefit/applicant/public/locales/en/common.json b/frontend/benefit/applicant/public/locales/en/common.json index 4610a048fa..903611c73d 100644 --- a/frontend/benefit/applicant/public/locales/en/common.json +++ b/frontend/benefit/applicant/public/locales/en/common.json @@ -29,10 +29,13 @@ "saved": "Saved", "sent": "Application number: {{applicationNumber}} | Submitted {{submittedAt}}", "new": "New application", - "edit": "Application", + "edit": "Helsinki benefit application", "helperText": "Please review the application before submitting.", "guideText": "All items marked with * are required" }, + "helpers": { + "requiredFields": "Fields marked with star are mandatory" + }, "statuses": { "draft": "Draft", "additionalInformationNeeded": "Additional information is needed", @@ -68,20 +71,23 @@ "company": { "heading1": "Employer information", "heading1Additional": "Postal address for delivering the decision", - "heading2": "Contact person", + "heading2": "Contact person for the employer", "heading2Short": "Contact person", - "heading3": "De minimis aid of the applicant", - "heading4": "Co-determination negotiation situation", - "heading5": "Co-determination negotiation", + "heading3": "De minimis aid received by the employer", + "heading4": "Change negotiations or co-determination negotiations", + "heading5": "Change negotiations", "businessId": "Business ID: {{ businessId }}", - "deMinimisAidsHeading": "List here all forms of de minimis aid received by the organisation during the current year and the previous two tax years", - "deMinimisAidsAdd": "Add", + "companyContactPerson": { + "content": "The contact person will communicate on matters related to this Helsinki benefit application and will be informed of the decision on the application." + }, + "deMinimisAidsHeading": "Below, please fill in all the de minimis aid granted to the organisation for the current and two previous tax years.", + "deMinimisAidsAdd": "Save", "deMinimisAidsRemove": "Delete", "deMinimisAidsNo": "The applicant has not listed any previous de minimis aids.", "notifications": { "companyInformation": { "label": "Data retrieved from the Business Information System", - "content": "Your organisation’s basic information has been retrieved from the Business Information System. If you wish, you can change the postal address to which the Helsinki benefit decision will be sent." + "content": "The marked data have been retrieved from the Business Information System.  If you would like to have your Helsinki benefit decision sent to another location, please change the postal address." }, "deMinimisAidMaxAmount": { "label": "Maximum amount exceeded", @@ -89,7 +95,7 @@ }, "deMinimisUnfinished": { "label": "Missing de minimis aid information", - "content": "Please fill any missing de minimis aid information and press 'Add' button." + "content": "Please fill any missing de minimis aid information and press 'Save' button." } }, "tooltips": { @@ -97,23 +103,28 @@ "associationHasBusinessActivities": "The Helsinki benefit is subject to the rules for government grants when the beneficiary is a company or an association engaged in an economic activity, irrespective of its legal form or financing method. Economic activity refers to the sale of services and products that is continuous, for gainful purposes and under competitive conditions (all three conditions must be fulfilled at the same time). The rules for government grants can also be applied to non-profit employers. As a rule, any activities that the Tax Authority has not regarded as taxable economic activities are not considered to be economic activities." }, "fields": { + "companyBusinessId": "Business ID", + "companyName": "Name of the employer", + "companyAddress": "Street address", + "companyPostcode": "Postal code", + "companyCity": "City / town", "useAlternativeAddress": { - "label": "Use a different postal address" + "label": "Use a different address" }, "alternativeCompanyStreetAddress": { - "label": "Street address", - "placeholder": "Street address" + "label": "Address", + "placeholder": "Address" }, "alternativeCompanyPostcode": { "label": "Postal code", "placeholder": "Postal code" }, "alternativeCompanyCity": { - "label": "City", - "placeholder": "City" + "label": "City / town", + "placeholder": "City / town" }, "companyDepartment": { - "label": "Office or department (optional)", + "label": "Office or department", "placeholder": "Office / department" }, "companyBankAccountNumber": { @@ -133,41 +144,38 @@ "placeholder": "040 1234567" }, "companyContactPersonEmail": { - "label": "Email address for communications", + "label": "E-mail for correspondence", "placeholder": "Email address" }, "applicantLanguage": { - "label": "Language of communication" + "label": "Preferred language of communication" }, "associationHasBusinessActivities": { - "label": "Is the employer engaged in economic activity?", + "label": "Does the employer engage in economic activity?", "yes": "Yes", "no": "No" }, - "associationImmediateManagerCheck": { - "label": "Immediate manager for the person to be hired", - "placeholder": "The employer has a proper workspace and a manager (who has supervisory responsibility and is not pay-subsidised) for the person to be hired." - }, "deMinimisAid": { - "label": "Has the applicant received de minimis aid during the current year or the previous two tax years?", + "label": "Has the organisation been awarded de minimis aid during the current and two previous tax years?", "yes": "Yes", "no": "No" }, "deMinimisAidGranter": { - "label": "Aid granted by", + "label": "Granting authority", "placeholder": "Issuing party" }, "deMinimisAidAmount": { - "label": "Amount of aid (EUR)", - "placeholder": "€" + "label": "Amount of aid", + "placeholder": "€", + "helperText": "euros" }, "deMinimisAidGrantedAt": { "label": "Date of issue", - "labelShort": "Date of issue", + "labelShort": "Date of grant", "placeholder": "Select" }, "coOperationNegotiations": { - "label": "Is the organisation undergoing co-determination negotiations?", + "label": "Does the organisation have ongoing or concluded change negotiations within the last 12 months?", "yes": "Yes", "no": "No" }, @@ -178,21 +186,22 @@ } }, "employee": { - "heading1": "Person to be employed with the Helsinki benefit", + "heading1": "The person for whose employment you are applying the Helsinki benefit", "heading1Short": "Person to be hired", - "heading2": "Pay subsidy decision", + "heading2": "State aid granted for employment", "heading3": "Type of benefit applied for", "heading3Long": "Form and date of the applied benefit", - "heading4": "Applied for the period", + "heading4": "Helsinki benefit is applied for the period?", + "heading4Sub1": "You can apply for the Helsinki benefit for the duration of the employment relationship, but only up to 12 months.", "heading5Employment": "Employment relationship", "heading5Assignment": "Commission", "heading5EmploymentSub1": "Salary costs", - "salaryExpensesExplanation": "The gross salary and indirect labour costs are indicated in EUR per month, holiday bonus as a lump sum", + "salaryExpensesExplanation": "Please report gross salary, Indirect labour costs and holiday pay in euros per month", "tooltips": { "heading5Employment": "Collective agreement applied: e.g. Collective Agreement for the Commercial Sector. If there is no binding collective agreement in the sector, put in a dash.", - "heading5EmploymentSub1": "The gross salary is the salary paid to the subsidised employee before the deduction of the employee’s statutory contributions (the employee’s unemployment insurance and pension insurance contributions) and taxes. If the subsidised employee is paid remuneration bonuses (e.g. evening, night or shift work bonus), take the estimated amount into account in the gross salary. The employers’ statutory indirect labour costs include social security expenses, pension insurance, accident insurance and unemployment insurance premiums as well as the mandatory group life insurance premium.\r\n\r\nIndirect labour costs refer to the amount of the employer’s statutory indirect labour costs paid for the salary per month.\r\n\r\nThe holiday bonus is a salary cost to be covered by the subsidy when it is paid for holiday pay during the subsidy period. Estimate the amount of holiday bonus to be paid during the subsidy period. Holiday compensation is not covered by the Helsinki benefit.", + "heading5EmploymentSub1": "Gross salary refers to total salary before deducting taxes and statutory contributions. Gross salary is stated in employment contracts. If bonuses are paid as part of salary – such as evening, night or shift work – include an estimate of the amount of bonuses in the gross salary.", "heading5Assignment": "The gross salary is the salary paid to the subsidised employee before the deduction of the employee's statutory contributions (the employee's unemployment insurance and pension insurance contributions) and taxes. If the subsidised employee is paid remuneration bonuses (e.g. evening, night or shift work bonus), take the estimated amount into account in the gross salary. The employers' statutory indirect labour costs include social security expenses, pension insurance, accident insurance and unemployment insurance premiums as well as the mandatory group life insurance premium. Indirect labour costs refer to the amount of the employer's statutory indirect labour costs paid for the salary per month. The holiday bonus is a salary cost to be covered by the subsidy when it is paid for holiday pay during the subsidy period. Estimate the amount of holiday bonus to be paid during the subsidy period. Holiday compensation is not covered by the Helsinki benefit.", - "heading2": "Pay subsidy is a financial subsidy intended to promote the employment of an unemployed jobseeker, which TE Services can grant to the employer for salary costs.", + "heading2": "The salary subsidy and the employment subsidy for people aged 55 and above are monetary subsidies intended to promote the employment of unemployed jobseekers.", "heading3": "The Helsinki benefit for employment is intended for guidance, orientation, tools, work clothing and workspace costs when no other support is paid for these. The Helsinki benefit for salary is intended for the cost of employing a subsidised employee (= gross salary, statutory indirect labour costs and holiday bonus). The Helsinki benefit for a commission is intended for the performance of an individual job or project." }, "notifications": { @@ -215,6 +224,10 @@ "noAvailableBenefitTypes": { "label": "No types of benefit available", "content": "A company or an association that is not engaged in economic activity can only apply for the Helsinki benefit for salary. Pay subsidy granted for the employment relationship is a prerequisite for the Helsinki benefit for salary." + }, + "apprenticeshipDidYouKnow": { + "label": "The Helsinki benefit may be granted for the entire duration of an apprenticeship contract.", + "content": "You can apply for the Helsinki benefit in periods of up to 12 months. Submit a new application after the 12-month Helsinki benefit grant period." } }, "messages": { @@ -242,21 +255,26 @@ }, "isLivingInHelsinki": { "label": "The subsidised employee’s municipality of residence is Helsinki", - "placeholder": "Yes, the person to be employed is registered in Helsinki at the latest at the beginning of the employment relationship", + "placeholder": "Yes, the employee will be registered in Helsinki no later than the start of the employment relationship.", "yes": "Yes", "no": "No", "error": "To be eligible for the subsidy, the municipality of residence must be Helsinki." }, + "associationImmediateManagerCheck": { + "label": "Has the person who is being employed been assigned a job supervisor?", + "placeholder": "Yes, the person to be employed has a supervisor." + }, "paySubsidyGranted": { - "label": "Has pay subsidy been granted for the employment relationship?", - "yes": "Yes", - "no": "No" + "label": "Has this employment relationship received any of the following aid?", + "paySubsidyDefault": "Salary subsidy", + "paySubsidyAged": "Employment aid for people aged 55 and above", + "paySubsidyNone": "None of the previous subsidies have been granted for this employment relationship" }, "paySubsidyPercent": { "label": "Pay subsidy percent in the decision" }, "additionalPaySubsidyPercent": { - "label": "If another pay subsidy has been granted for the employment relationship, please provide the pay subsidy percent" + "label": "If another pay subsidy has been granted for the employment relationship, \nplease provide the pay subsidy percent" }, "apprenticeshipProgram": { "label": "Is this an apprenticeship?", @@ -276,26 +294,34 @@ "workingHours": { "label": "Working hours per week", "placeholder": "Hours", - "view": "Working hours: {{workingHours}} hours per week" + "view": "Working hours: {{workingHours}} hours per week", + "helperText": "hours per week" }, "collectiveBargainingAgreement": { - "label": "Collective agreement applied", - "placeholder": "Collective agreement applied" + "label": "Applicable collective agreement", + "placeholder": "Applicable collective agreement", + "tooltip": "Applicable collective agreement: e.g. the collective agreement for commerce. If there is no binding collective agreement in the sector, indicate this with a dash." }, "monthlyPay": { "label": "Gross salary", "placeholder": "Gross salary", - "view": "Gross salary {{monthlyPay}} EUR per month" + "view": "Gross salary {{monthlyPay}} EUR per month", + "helperText": "EUR per month", + "tooltip": "Gross salary refers to the salary paid to the person being hired with the subsidy before withholding the employee’s statutory contributions (the employee’s unemployment insurance and pension insurance contributions) and taxes. If bonuses are paid as part of their salary – such as evening, night or shift work – include an estimate of the amount of bonuses in the gross salary." }, "otherExpenses": { "label": "Indirect labour costs", "placeholder": "Indirect labour costs", - "view": "Indirect labour costs {{otherExpenses}} EUR per month" + "view": "Indirect labour costs {{otherExpenses}} EUR per month", + "helperText": "EUR per month", + "tooltip": "Indirect labour costs are the amount of the employer’s statutory Indirect labour costs payable from salaries each month. Indirect labour costs include social security, pension insurance, accident insurance, unemployment insurance and a compulsory group life insurance contribution. Please report the amount of Indirect labour costs in euros per month." }, "vacationMoney": { "label": "Holiday bonus", "placeholder": "Holiday bonus", - "view": "Holiday bonus {{vacationMoney}} EUR" + "view": "Holiday bonus {{vacationMoney}} EUR", + "helperText": "EUR per month", + "tooltip": "Holiday pay is a subsidised salary cost when it is paid out of annual holiday pay during the subsidised period. Holiday compensation is not covered by the Helsinki benefit. Please report the amount of holiday pay to be paid during the subsidy period in euros per month. " }, "commissionDescription": { "label": "Description of the commission", @@ -331,12 +357,12 @@ "message": "" }, "educationContract": { - "title": "Agreement on the organisation of an apprenticeship", + "title": "Agreement on providing apprenticeship training", "message": "" }, "helsinkiBenefitVoucher": { - "title": "Helsinki benefit voucher", - "message": "Add Helsinki benefit voucher if one has been issued" + "title": "The Helsinki benefit card", + "message": "Add Helsinki benefit card if one has been issued" } }, "add": "Attach the file", @@ -405,7 +431,7 @@ }, "languages": { "fi": "Suomi", - "sv": "Ruotsi", + "sv": "Svenska", "en": "English" }, "supportedLanguages": { @@ -424,7 +450,7 @@ "text": "Add the following attachments to the application. All attachments marked with * are required. The accepted file formats are JPG, PNG and PDF and the maximum file size is 10 MB." }, "credentialsIngress": { - "text": "A person to be employed with the Helsinki benefit must be informed of the processing of their personal data. Print the notification and request a signature from the person to be employed." + "text": "The person who is to be employed with the Helsinki benefit must be informed about the processing of their personal data. Please print out the notice and ask the prospective employee to sign it." }, "form": { "validation": { @@ -476,18 +502,18 @@ "logoutMessageLabel": "You have logged out", "errorLabel": "An unexpected error occurred. Please, sign in again.", "sessionExpiredLabel": "User session expired. Sign in again", - "infoLabel": "Problems logging in?", + "infoLabel": "Do you have questions about the Helsinki benefit?", "logoutInfoContent": "You have been logged out. If you want to continue to use the service, you have to log in again click the button here below.", "infoContent": "Send us an email to helsinkilisa@hel.fi.", "termsOfServiceHeader": "Information about the processing of the employer representatives’ personal data", "heading": "Log in to the Helsinki benefit service", - "infoText1": "Apply for Helsinki benefit, a discretionary support for a private or third-sector employer that hires an unemployed Helsinki resident. You need a Suomi.fi e-Authorization to do business on behalf of your organization.", + "infoText1": "Apply for the Helsinki benefit: financial support for your organisation to employ an unemployed Helsinki resident. You need a Suomi.fi authorisation to act on behalf of your organisation.", "subheading1": "Log in with you bank access codes or mobile certificate", "infoText2": "When you log in for the first time, your own Helsinki profile is automatically created for you.", - "subheading2": "Acquire Suomi.fi e-Authorization", - "infoText3": "If you do not yet have the right to do business on behalf of your organization, you can get yourself a Suomi.fi e-Authorization.", - "authorization": "See the instructions for obtaining authorization", - "suomifiUrl": "https://www.suomi.fi/e-authorizations" + "subheading2": "The required suomi.fi authorisation", + "infoText3": "You can log in to the Helsinki benefit service if you have the authority to sign on behalf of the employer or if you have been granted the authority to “Apply for pay subsidy” or “Applying for grants“ in the Suomi.fi service.", + "authorization": "View the instructions for acting on behalf of an organisation", + "suomifiUrl": "https://www.suomi.fi/instructions-and-support/e-authorizations/acting-on-behalf-of-an-organisation" }, "errorPage": { "title": "An error has unfortunately occurred", @@ -528,8 +554,12 @@ "page": "Page", "terms": "Terms.PDF" }, + "tooltip": { + "ariaLabel": "Show help text", + "ariaButtonLabel": "Show help text" + }, "utility": { - "and": "ja", + "and": "and", "yes": "Yes", "no": "No", "close": "Close", diff --git a/frontend/benefit/applicant/public/locales/fi/common.json b/frontend/benefit/applicant/public/locales/fi/common.json index 109e59c4bb..ec3b04bfb2 100644 --- a/frontend/benefit/applicant/public/locales/fi/common.json +++ b/frontend/benefit/applicant/public/locales/fi/common.json @@ -29,10 +29,13 @@ "saved": "Tallennettu", "sent": "Hakemusnumero: {{applicationNumber}} | Lähetetty {{submittedAt}}", "new": "Uusi hakemus", - "edit": "Hakemus", + "edit": "Helsinki-lisä -hakemus", "helperText": "Ole hyvä ja tarkista hakemus ennen lähetystä.", "guideText": "Kaikki *-merkityt kohdat ovat pakollisia" }, + "helpers": { + "requiredFields": "Tähdellä merkityt kentät ovat pakollisia" + }, "statuses": { "draft": "Luonnos", "additionalInformationNeeded": "Lisätietoja tarvitaan", @@ -68,20 +71,23 @@ "company": { "heading1": "Työnantajan tiedot", "heading1Additional": "Postiosoite päätöstä varten", - "heading2": "Yhteyshenkilö", + "heading2": "Työnantajan yhteyshenkilö", "heading2Short": "Yhteyshenkilö", - "heading3": "Hakijan de minimis -tuet", - "heading4": "YT-tilanne", - "heading5": "YT-neuvottelut", + "heading3": "Työnantajan saamat de minimis -tuet", + "heading4": "Muutosneuvottelut tai YT-tilanne", + "heading5": "Muutosneuvottelut", "businessId": "Y-tunnus: {{ businessId }}", - "deMinimisAidsHeading": "Listaa tähän kaikki organisaatiolle kuluvan vuoden ja kahden edellisen verovuoden aikana myönnetyt de minimis -tuet", - "deMinimisAidsAdd": "Lisää", + "companyContactPerson": { + "content": "Yhteyshenkilö kommunikoi tähän Helsinki-lisä -hakemukseen liittyvissä asioissa ja saa tiedon hakemuksen päätöksestä." + }, + "deMinimisAidsHeading": "Täytä alle kaikki organisaatiolle myönnetyt de minimis -tuet kuluvan vuoden ja kahden edellisen verovuoden ajalta.", + "deMinimisAidsAdd": "Tallenna", "deMinimisAidsRemove": "Poista", "deMinimisAidsNo": "Hakija ei ole ilmoittanut aiemmista de minimis -tuista.", "notifications": { "companyInformation": { "label": "Tiedot haetaan Yritys- ja yhteisötietojärjestelmästä", - "content": "Organisaatiosi perustiedot haetaan Yritys- ja yhteisötietojärjestelmästä. Halutessasi voit vaihtaa tilalle toisen postiosoitteen, johon Helsinki-lisä-päätös toimitetaan." + "content": "Merkityt tiedot on haettu Yritys- ja yhteisötietojärjestelmästä. Jos haluat Helsinki-lisä-päätöksen toiseen toimipisteeseen, vaihda tilalle toinen postiosoite." }, "deMinimisAidMaxAmount": { "label": "Enimmäismäärä ylitetty", @@ -89,7 +95,7 @@ }, "deMinimisUnfinished": { "label": "Puuttuvia de minimis-tuen tietoja", - "content": "Täytä puuttuvat de minimis -kentät ja paina 'Lisää' painiketta." + "content": "Täytä puuttuvat minimis -kentät ja paina 'Tallenna' painiketta." } }, "tooltips": { @@ -97,12 +103,17 @@ "associationHasBusinessActivities": "Helsinki-lisään sovelletaan valtiontukisääntöjä silloin, kun tuensaaja on yritys tai taloudellista toimintaa harjoittavaa yhteisö riippumatta sen oikeudellisesta muodosta tai rahoitustavasta. Taloudellisella toiminnalla tarkoitetaan palveluiden ja tuotteiden myyntiä, joka on jatkuvaa, ansiotarkoituksessa ja kilpailuolosuhteissa tapahtuvaa (kaikkien kolmen edellytyksen tulee täyttyä samanaikaisesti). Valtiontukisääntöjä voidaan soveltaa myös voittoa tavoittelemattomiin työnantajiin. Taloudellisena toimintana ei pääsääntöisesti pidetä toimintaa, jota verottaja ei ole katsonut verotettavaksi elinkeinotoiminnaksi." }, "fields": { + "companyBusinessId": "Y-tunnus", + "companyName": "Työnantajan nimi", + "companyAddress": "Katuosoite", + "companyPostcode": "Postinumero", + "companyCity": "Postitoimipaikka", "useAlternativeAddress": { - "label": "Käytä toista postiosoitetta" + "label": "Käytä toista osoitetta" }, "alternativeCompanyStreetAddress": { "label": "Osoite", - "placeholder": "Katuosoite" + "placeholder": "Osoite" }, "alternativeCompanyPostcode": { "label": "Postinumero", @@ -113,12 +124,12 @@ "placeholder": "Postitoimipaikka" }, "companyDepartment": { - "label": "Toimipiste tai osasto (valinnainen)", + "label": "Toimipiste", "placeholder": "Toimipiste / osasto" }, "companyBankAccountNumber": { "label": "Tilinumero", - "placeholder": "IBAN muodossa" + "placeholder": "IBAN-muodossa" }, "companyContactPersonFirstName": { "label": "Etunimi", @@ -144,12 +155,8 @@ "yes": "Kyllä", "no": "Ei" }, - "associationImmediateManagerCheck": { - "label": "Lähiesihenkilö palkattavalle henkilölle", - "placeholder": "Työnantajalla on asianmukaiset työtilat ja työnjohdollinen, ei-palkkatuettu esihenkilö palkattavalle henkilölle." - }, "deMinimisAid": { - "label": "Onko hakijalle myönnetty kuluvan vuoden ja kahden edellisen verovuoden aikana de minimis -tukea?", + "label": "Onko organisaatiolle myönnetty de minimis -tukea kuluvan vuoden ja kahden edellisen verovuoden aikana?", "yes": "Kyllä", "no": "Ei" }, @@ -158,8 +165,9 @@ "placeholder": "Myöntävä taho" }, "deMinimisAidAmount": { - "label": "Tuen määrä (euroina)", - "placeholder": "€" + "label": "Tuen määrä", + "placeholder": "€", + "helperText": "euroa" }, "deMinimisAidGrantedAt": { "label": "Myöntämispäivämäärä", @@ -167,32 +175,33 @@ "placeholder": "Valitse" }, "coOperationNegotiations": { - "label": "Onko organisaatiolla käynnissä YT-neuvottelut?", + "label": "Onko organisaatiolla käynnissä tai edeltävän 12 kuukauden aikana päättynyt muutosneuvottelut?", "yes": "Kyllä", "no": "Ei" }, "coOperationNegotiationsDescription": { - "label": "Anna tarkempi selvitys YT-tilanteesta:", + "label": "Anna tarkempi selvitys neuvotteluiden tilanteesta.", "placeholder": "Kuvaile tilannetta" } } }, "employee": { "heading1": "Henkilö, jonka työllistämiseen Helsinki-lisää haetaan", - "heading1Short": "Palkattava henkilö", - "heading2": "Palkkatukipäätös", + "heading1Short": "Työllistettävä henkilö", + "heading2": "Työsuhteeseen myönnetyt valtion tuet", "heading3": "Haettava tukimuoto", "heading3Long": "Haettava tukimuoto ja ajankohta", - "heading4": "Haetaan ajalle", + "heading4": "Mille ajalle haet Helsinki-lisää?", + "heading4Sub1": "Voit hakea Helsinki-lisää työsuhteen keston ajaksi, mutta enintään 12 kuukaudeksi.", "heading5Employment": "Työsuhde", "heading5Assignment": "Toimeksianto", "heading5EmploymentSub1": "Palkkauskustannukset", - "salaryExpensesExplanation": "Bruttopalkka ja sivukulut ilmoitetaan euroina kuukaudessa, lomaraha kertasummana", + "salaryExpensesExplanation": "Ilmoita bruttopalkka, sivukulut ja lomaraha euroina kuukaudessa.", "tooltips": { "heading5Employment": "Sovellettava työehtosopimus: esim. Kaupan työehtosopimus. Jos alalla ei ole velvoittavaa työehtosopimusta, merkitse viiva.", - "heading5EmploymentSub1": "Bruttopalkalla tarkoitetaan tuella palkattavalle maksettavaa palkkaa ennen työntekijän lakisääteisten maksujen (työntekijän työttömyysvakuutus- ja eläkevakuutusmaksu) ja verojen pidätystä. Jos tuella palkattavalle maksetaan palkan lisiä (esim. ilta-, yö- tai vuorotyölisä), ota niiden arvioitu määrä huomioon bruttopalkassa. Työnantajan lakisääteisiin sivukuluihin luetaan sosiaaliturva-, työeläkevakuutus-, tapaturmavakuutus- ja työttömyysvakuutusmaksu sekä pakollinen ryhmähenkivakuutusmaksu.\r\n\r\nSivukulut tarkoittavat palkasta maksettavien työnantajan lakisääteisten sivukulujen määrä kuukaudessa.\r\n\r\nLomaraha on tuella katettava palkkauskustannus silloin, kun se maksetaan tukijakson aikana pidetystä vuosilomapalkasta. Arvioi tukijakson aikana maksettavan lomarahan määrä. Lomakorvaus ei ole Helsinki-lisällä katettava korvaus.", + "heading5EmploymentSub1": "Bruttopalkka tarkoittaa palkkasummaa ennen verojen ja lakisääteisten maksujen vähentämistä. Bruttopalkka on merkitty työsopimukseen. Mikäli osana palkkaa maksetaan lisiä, kuten ilta-, yö- tai vuorolisiä, laske arvio lisien määristä mukaan bruttopalkkaan.", "heading5Assignment": "Palkkauskustannukset tooltip text", - "heading2": "Palkkatuki on työttömän työnhakijan työllistymisen edistämiseksi tarkoitettu rahallinen tuki, jonka TE-palvelut voi myöntää työnantajalle palkkauskustannuksiin.", + "heading2": "Palkkatuki ja ja 55-vuotta täyttäneiden työllistämistuki ovat työttömän työnhakijan työllistymisen edistämiseksi tarkoitettuja rahallisia tukia.", "heading3": "Työllistämisen Helsinki-lisä on tarkoitettu ohjaus-, perehdytys-, työväline-, työvaate- ja työtilakustannuksiin silloin, kun niihin ei makseta muuta tukea. Palkan Helsinki-lisä on tarkoitettu työllistetyn henkilön palkkauskustannuksiin (=bruttopalkka, lakisääteiset sivukulut ja lomaraha). Toimeksiannon Helsinki-lisä on tarkoitettu yksittäisen työn tai projektin suorittamiseen." }, "notifications": { @@ -215,6 +224,10 @@ "noAvailableBenefitTypes": { "label": "Ei haettavia tukimuotoja", "content": "Yhteisö, joka ei harjoita taloudellista toimintaa voi hakea ainoastaan Palkan Helsinki-lisää. Palkan Helsinki-lisän edellytyksenä on työsuhteeseen myönnetty palkkatuki." + }, + "apprenticeshipDidYouKnow": { + "label": "Oppisopimukseen Helsinki-lisää voidaan myöntää koko oppisopimuksen ajaksi.", + "content": "Voit hakea Helsinki-lisää enintään 12 kuukauden jaksoissa. Tee uusi hakemus 12 kuukauden Helsinki-lisän tukijakson jälkeen." } }, "messages": { @@ -241,16 +254,21 @@ "placeholder": "040 1234567" }, "isLivingInHelsinki": { - "label": "Työllistetyn kotikunta on Helsinki", - "placeholder": "Kyllä, työllistettävä on kirjoilla Helsingissä viimeistään työsuhteen alkaessa", + "label": "Onko työllistettävän kotikunta Helsinki työsuhteen alkaessa?", + "placeholder": "Kyllä, työllistettävä on kirjoilla Helsingissä viimeistään työsuhteen alkaessa.", "yes": "Kyllä", "no": "Ei", "error": "Jotta tukea voidaan myöntää, kotikunnan tulee olla Helsinki." }, + "associationImmediateManagerCheck": { + "label": "Onko työllistettävälle osoitettu työnjohdollinen esihenkilö?", + "placeholder": "Kyllä, työllistettävällä on työnjohdollinen esihenkilö." + }, "paySubsidyGranted": { - "label": "Onko työsuhteeseen myönnetty palkkatuki?", - "yes": "Kyllä", - "no": "Ei" + "label": "Onko työsuhteeseen myönnetty jokin seuraavista tuista?", + "paySubsidyDefault": "Palkkatuki", + "paySubsidyAged": "55 vuotta täyttäneiden työllistämistuki", + "paySubsidyNone": "Työsuhteeseen ei ole myönnetty mitään edeltävistä tuista" }, "paySubsidyPercent": { "label": "Päätöksessä oleva palkkatukiprosentti" @@ -274,28 +292,36 @@ "placeholder": "Tehtävänimike" }, "workingHours": { - "label": "Työaika tuntia viikossa", + "label": "Työaika", "placeholder": "Tunteja", - "view": "Työaika: {{workingHours}} tuntia viikossa" + "view": "Työaika: {{workingHours}} tuntia viikossa", + "helperText": "tuntia viikossa" }, "collectiveBargainingAgreement": { "label": "Sovellettava työehtosopimus", - "placeholder": "Työehtosopimus" + "placeholder": "Työehtosopimus", + "tooltip": "Sovellettava työehtosopimus: esim. Kaupan työehtosopimus. Jos alalla ei ole velvoittavaa työehtosopimusta, merkitse viiva." }, "monthlyPay": { "label": "Bruttopalkka", "placeholder": "Palkka", - "view": "Bruttopalkka {{monthlyPay}} euroa kuukaudessa" + "view": "Bruttopalkka {{monthlyPay}} euroa kuukaudessa", + "helperText": "euroa kuukaudessa", + "tooltip": "Bruttopalkalla tarkoitetaan tuella palkattavalle maksettavaa palkkaa ennen työntekijän lakisääteisten maksujen (työntekijän työttömyysvakuutus- ja eläkevakuutusmaksu) ja verojen pidätystä. Mikäli osana palkkaa maksetaan lisiä, kuten ilta-, yö- tai vuorotyölisiä, laske arvio lisien määristä mukaan bruttopalkkaan." }, "otherExpenses": { "label": "Sivukulut", "placeholder": "Sivukulut", - "view": "Sivukulut {{otherExpenses}} euroa kuukaudessa" + "view": "Sivukulut {{otherExpenses}} euroa kuukaudessa", + "helperText": "euroa kuukaudessa", + "tooltip": "Sivukulut tarkoittavat palkasta maksettavien työnantajan lakisääteisten sivukulujen määrää kuukaudessa. Sivukuluihin kuuluu sosiaaliturva-, työeläkevakuutus-, tapaturmavakuutus- ja työttömyysvakuutusmaksu sekä pakollinen ryhmähenkivakuutusmaksu. Ilmoita sivukulujen määrä euroina kuukaudessa." }, "vacationMoney": { "label": "Lomaraha", "placeholder": "Lomaraha", - "view": "Lomaraha {{vacationMoney}} euroa" + "view": "Lomaraha {{vacationMoney}} euroa kuukaudessa", + "helperText": "euroa kuukaudessa", + "tooltip": "Lomaraha on tuella katettava palkkauskustannus silloin, kun se maksetaan tukijakson aikana pidetystä vuosilomapalkasta. Lomakorvaus ei ole Helsinki-lisällä katettava korvaus. Ilmoita lomarahan määrä euroina kuukaudessa." }, "commissionDescription": { "label": "Toimeksiannon kuvaus", @@ -316,7 +342,7 @@ } }, "attachments": { - "heading1": "Liitteet", + "heading1": " ", "types": { "employmentContract": { "title": "Työsopimus", @@ -335,15 +361,15 @@ "message": "" }, "helsinkiBenefitVoucher": { - "title": "Helsinki-lisä seteli", - "message": "Lisää Helsinki-lisä -seteli, jos sellainen on myönnetty" + "title": "Helsinki-lisä -kortti", + "message": "Lisää Helsinki-lisä -kortti, jos sellainen on myönnetty" } }, "add": "Liitä tiedosto", "remove": "Poista liite" }, "summary": { - "heading1": "Vänligen kontrollera ansökan innan du skickar in den." + "heading1": "Ole hyvä ja tarkista tiedot ennen kuin lähetät sen." }, "credentials": { "heading1": "Työllistettävän henkilötietojen käsittely", @@ -476,7 +502,7 @@ "logoutMessageLabel": "Olet kirjautunut ulos", "errorLabel": "Tapahtui tuntematon virhe. Kirjaudu uudelleen sisään", "sessionExpiredLabel": "Käyttäjäsessio vanhentui. Kirjaudu uudelleen sisään", - "infoLabel": "Ongelmia kirjautumisessa?", + "infoLabel": "Kysyttävää Helsinki-lisästä? ", "logoutInfoContent": "Sinut on kirjattu ulos. Jos haluat jatkaa asiointia palvelussa, kirjaudu uudelleen sisään klikkaamalla alla olevasta painikkeesta.", "infoContent": "Lähetä meille sähköposti osoitteeseen helsinkilisa@hel.fi.", "termsOfServiceHeader": "Tietoa työnantajan edustajien henkilötietojen käsittelystä", @@ -484,10 +510,10 @@ "infoText1": "Hae organisaatiollesi taloudellista tukea, Helsinki-lisää, työttömän helsinkiläisen työllistämiseen. Tarvitset Suomi.fi-valtuutuksen asioimiseen organisaatiosi puolesta.", "subheading1": "Kirjaudu pankkitunnuksilla tai mobiilivarmenteella", "infoText2": "Kun kirjaudut ensimmäisen kerran, sinulle luodaan automaattisesti oma Helsinki-profiili tunnus.", - "subheading2": "Hanki Suomi.fi -valtuutus", - "infoText3": "Jos sinulla ei ole vielä oikeutta asioida organisaatiosi puolesta, voit hankkia itsellesi Suomi.fi -valtuuden.", - "authorization": "Katso ohjeet valtuutuksen hankkimiseen", - "suomifiUrl": "https://www.suomi.fi/valtuudet" + "subheading2": "Vaadittava suomi.fi -valtuus", + "infoText3": "Voit kirjautua Helsinki-lisän asiointipalveluun, jos sinulla on nimenkirjoitusoikeus tai sinulle on myönnetty Suomi.fi-palvelussa valtuusasiaksi joko “palkkatuen hakeminen“ tai “avustushakemuksen tekeminen“.", + "authorization": "Katso ohjeet organisaation puolesta asiointiin", + "suomifiUrl": "https://www.suomi.fi/ohjeet-ja-tuki/valtuudet/organisaation-puolesta-asiointi" }, "errorPage": { "title": "Palvelussa on valitettavasti tapahtunut virhe", @@ -528,6 +554,10 @@ "page": "Sivu", "terms": "Ehdot.PDF" }, + "tooltip": { + "ariaLabel": "Näytä ohjeteksti", + "ariaButtonLabel": "Näytä ohjeteksti" + }, "utility": { "and": "ja", "yes": "Kyllä", diff --git a/frontend/benefit/applicant/public/locales/sv/common.json b/frontend/benefit/applicant/public/locales/sv/common.json index c69435c9f5..afdded9f68 100644 --- a/frontend/benefit/applicant/public/locales/sv/common.json +++ b/frontend/benefit/applicant/public/locales/sv/common.json @@ -13,7 +13,7 @@ "common": { "created": "Skapats", "employee": "Anställd", - "saved": "Tallennettu", + "saved": "Sparat", "edit": "Redigera", "sent": "Skickad", "applicationNumber": "Ansökningsnummer", @@ -29,10 +29,13 @@ "saved": "Sparad", "sent": "Ansökningsnummer: {{applicationNumber}} | Skickad {{submittedAt}}", "new": "Ny ansökan", - "edit": "Ansökan", + "edit": "Helsingforstillagg ansokan", "helperText": "Vänligen kontrollera ansökan innan du skickar in den.", "guideText": "Fälten märkta med * är obligatoriska" }, + "helpers": { + "requiredFields": "Fält markerade med takt är obligatoriska" + }, "statuses": { "draft": "Utkast", "additionalInformationNeeded": "Ytterligare uppgifter behövs", @@ -68,20 +71,23 @@ "company": { "heading1": "Arbetsgivarinformation", "heading1Additional": "Postadress för beslutet", - "heading2": "Kontaktperson", + "heading2": "Arbetsgivarens kontaktperson", "heading2Short": "Kontaktperson", - "heading3": "Sökandens de minimis-stöd", - "heading4": "Situation med samarbetsförhandlingar", + "heading3": "De minimis-stöden som arbetsgivaren fått", + "heading4": "Samarbetsförhandlingar eller samarbetsförfarande", "heading5": "Samarbetsförhandlingar", "businessId": "FO-nummer: {{ businessId }}", - "deMinimisAidsHeading": "Lista här samtliga de minimis-stöd som beviljats organisationen under det innevarande året eller under de två föregående skatteåren", - "deMinimisAidsAdd": "Lägg till", + "companyContactPerson": { + "content": "Kontaktpersonen sköter kontakterna i ärenden med anknytning till denna ansökning om Helsingforstillägget och får uppgiften om beslutet om ansökningen." + }, + "deMinimisAidsHeading": "Fyll i alla de de minimis-stöd som har beviljats organisationen under innevarande år och under två föregående skatteår", + "deMinimisAidsAdd": "Spara", "deMinimisAidsRemove": "Ta bort", "deMinimisAidsNo": "Sökanden har inte angett några tidigare de minimis -stöd.", "notifications": { "companyInformation": { "label": "Uppgifterna har hämtats från Företags- och organisationsdatasystemet", - "content": "De grundläggande uppgifterna om organisationen har hämtats från Företags- och organisationsdatasystemet. Om du vill kan du ange en särskild postadress dit beslutet om Helsingforstillägg skickas." + "content": "De markerade uppgifterna har hämtats från Företags- och organisationsdatasystemet.  Om du vill få ett beslut om Helsingforstillägget för ett annat verksamhetsställe, byt postadressen." }, "deMinimisAidMaxAmount": { "label": "Maximibeloppet har överskridits", @@ -89,7 +95,7 @@ }, "deMinimisUnfinished": { "label": "Information saknar om de minimis stöd", - "content": "Vänligen fyll i eventuell saknad de minimis-stöd information och tryck på knappen 'Lägg till'." + "content": "Vänligen fyll i eventuell saknad de minimis-stöd information och tryck på knappen 'Spara'." } }, "tooltips": { @@ -97,23 +103,28 @@ "associationHasBusinessActivities": "På Helsingforstillägget tillämpas reglerna om statligt stöd när stödtagaren är ett företag eller en organisation som bedriver ekonomisk verksamhet, oavsett juridisk form eller finansieringsform. Med ekonomisk verksamhet avses försäljning av tjänster och produkter, som sker fortlöpande, i förvärvssyfte och i konkurrensomständigheter (alla tre villkoren ska uppfyllas samtidigt). Reglerna om statligt stöd kan också tillämpas på icke-vinstdrivande arbetsgivare. Ekonomisk verksamhet som skattemyndigheterna inte har ansett vara beskattningsbar anses i regel inte vara ekonomisk verksamhet." }, "fields": { + "companyBusinessId": "FO-nummer", + "companyName": "Arbetsgivarens namn", + "companyAddress": "Adress", + "companyPostcode": "Postnummer", + "companyCity": "Postort", "useAlternativeAddress": { - "label": "Använd en annan postadress" + "label": "Använd en annan adress" }, "alternativeCompanyStreetAddress": { - "label": "Gatuadress", - "placeholder": "Gatuadress" + "label": "Adress", + "placeholder": "Adress" }, "alternativeCompanyPostcode": { "label": "Postnummer", "placeholder": "Postnummer" }, "alternativeCompanyCity": { - "label": "Postanstalt", - "placeholder": "Postanstalt" + "label": "Postort", + "placeholder": "Postort" }, "companyDepartment": { - "label": "Verksamhetsställe eller avdelning (valfri)", + "label": "Verksamhetsställe eller avdelning", "placeholder": "Verksamhetsställe / avdelning" }, "companyBankAccountNumber": { @@ -133,33 +144,30 @@ "placeholder": "040 1234567" }, "companyContactPersonEmail": { - "label": "E-post för ärendehantering", + "label": "E-post för uträttande av ärenden", "placeholder": "E-postadress" }, "applicantLanguage": { - "label": "Kommunikationsspråk" + "label": "Affärsspråk" }, "associationHasBusinessActivities": { - "label": "Bedriver arbetsgivaren ekonomisk verksamhet?", + "label": "Har arbetsgivaren ekonomisk verksamhet?", "yes": "Ja", "no": "Nej" }, - "associationImmediateManagerCheck": { - "label": "Närmaste chef till personen som anställs", - "placeholder": "Arbetsgivaren har ändamålsenliga arbetslokaler och en till arbetsledningen hörande, ej lönesubventionerad chef för personen som anställs." - }, "deMinimisAid": { - "label": "Har sökanden beviljats de minimis-stöd under det innevarande året eller under de två föregående skatteåren?", + "label": "Har organisationen beviljats de minimis-stöd under innevarande år och under de två föregående skatteåren?", "yes": "Ja", "no": "Nej" }, "deMinimisAidGranter": { - "label": "Stödbeviljare", + "label": "Beviljare av stödet", "placeholder": "Beviljande instans" }, "deMinimisAidAmount": { - "label": "Stödbelopp (euro)", - "placeholder": "€" + "label": "Stödbeloppet", + "placeholder": "€", + "helperText": "euro" }, "deMinimisAidGrantedAt": { "label": "Datum för beviljande", @@ -167,7 +175,7 @@ "placeholder": "Välj" }, "coOperationNegotiations": { - "label": "Pågår samarbetsförhandlingar i organisationen?", + "label": "Pågår samarbetsförhandlingar i organisationen eller har de avslutats under föregående 12 månader?", "yes": "Ja", "no": "Nej" }, @@ -178,21 +186,22 @@ } }, "employee": { - "heading1": "Person för vars anställning man ansöker om Helsingforstillägg", - "heading1Short": "Person som ska anställas", - "heading2": "Beslut om lönesubvention", + "heading1": "Personen som ska sysselsättas med Helsingforstillägget", + "heading1Short": "Personen som ska sysselsättas", + "heading2": "Statliga stöd som beviljats för anställningsförhållandet", "heading3": "Den stödform som söks", "heading3Long": "Form och datum för stöd som ska sökas", - "heading4": "Ansöks för perioden", + "heading4": "För vilken tidperiod ansöker du om Helsingforstillägget?", + "heading4Sub1": "Du kan ansöka om Helsingforstillägget för anställningsförhållandets tid men för högst 12 månader.", "heading5Employment": "Anställningsförhållande", "heading5Assignment": "Uppdrag", - "heading5EmploymentSub1": "Lönekostnader", - "salaryExpensesExplanation": "Bruttolön och bikostnader anges i euro per månad, semesterpenning som ett engångsbelopp", + "heading5EmploymentSub1": "Anställningskostnaderna", + "salaryExpensesExplanation": "Ange bruttolönen, bikostnaderna och semesterpenning i euro per månad.", "tooltips": { "heading5Employment": "Kollektivavtal som tillämpas: t.ex. kollektivavtal för handeln. Sätt streck om branschen inte har ett bindande kollektivavtal.", - "heading5EmploymentSub1": "Med bruttolön avses lönen till den person som anställs med subventionen före innehållande av arbetsgivarens lagstadgade kostnader (arbetslöshets- och pensionsförsäkringspremier för arbetstagaren) och skatter. Om den person som anställs med subventionen får lönetillägg (t.ex. kvälls-, natt- eller skiftarbetstillägg), ska det uppskattade beloppet från dessa beaktas i bruttolönen. Till arbetsgivarens lagstadgade bikostnader räknas socialskyddsavgifterna och premierna för arbetspensionsförsäkringen, olycksfallsförsäkringen och arbetslöshetsförsäkringen samt den obligatoriska grupplivförsäkringen.\r\n\r\nMed bikostnader avses arbetsgivarens lagstadgade bikostnader per månad.\r\n\r\nSemesterpenning är en lönekostnad som kan täckas med subventionen till den del som semesterpenning betalas för semesterlön som används under subventionsperioden. Uppskatta semesterpenningen som betalas under subventionsperioden. Semesterpenning kan inte täckas med Helsingforstillägg.", + "heading5EmploymentSub1": "Bruttolön innebär lönebeloppet innan avdraget av skatter och lagstadgade avgifter. Bruttolön anges i arbetsavtalet. Om tillägg såsom kväll-, natt- eller skiftsarbetstillägg betalas som en del av lönen, beräkna en uppskattning om beloppet på tilläggen in i bruttolönen.", "heading5Assignment": "Med bruttolön avses lönen till den person som anställs med subventionen före innehållande av arbetsgivarens lagstadgade kostnader (arbetslöshets- och pensionsförsäkringspremier för arbetstagaren) och skatter. Om den person som anställs med subventionen får lönetillägg (t.ex. kvälls-, natt- eller skiftarbetstillägg), ska det uppskattade beloppet från dessa beaktas i bruttolönen. Till arbetsgivarens lagstadgade bikostnader räknas socialskyddsavgifterna och premierna för arbetspensionsförsäkringen, olycksfallsförsäkringen och arbetslöshetsförsäkringen samt den obligatoriska grupplivförsäkringen. Med bikostnader avses arbetsgivarens lagstadgade bikostnader per månad. Semesterpenning är en lönekostnad som kan täckas med subventionen till den del som semesterpenning betalas för semesterlön som används under subventionsperioden. Uppskatta semesterpenningen som betalas under subventionsperioden. Semesterpenning kan inte täckas med Helsingforstillägg.", - "heading2": "Lönesubvention är ett ekonomiskt stöd för främjande av sysselsättningen av en arbetslös arbetssökande som arbets- och näringslivstjänsterna kan bevilja till arbetsgivaren för lönekostnader.", + "heading2": "Lönesubvention och sysselsättningsstöd för personer som fyllt 55 år är finansiella stöd som är avsedda för främjande av sysselsättning av en arbetslös arbetssökande.", "heading3": "Helsingforstillägg för sysselsättning är avsett för kostnaderna för handledning, inskolning, arbetsredskap, arbetskläder och arbetslokaler då inga andra understöd utbetalas för dessa. Helsingforstillägg för lön är avsett för lönekostnaderna för den anställda (=bruttolön, lagstadgade bikostnader och semesterpenning). Helsingforstillägg för uppdrag är avsett för utförande av ett enskilt jobb eller projekt." }, "notifications": { @@ -215,6 +224,10 @@ "noAvailableBenefitTypes": { "label": "Inga ansökningsbara stödformer", "content": "En sammanslutning som inte bedriver ekonomisk verksamhet kan endast ansöka om Helsingforstillägg för lön. Förutsättningen för att få Helsingforstillägg för lön är att lönesubvention har beviljats för anställningen." + }, + "apprenticeshipDidYouKnow": { + "label": "För ett läroavtal kan Helsingforstillägget beviljas för hela läroavtalstiden.", + "content": "Du kan ansöka om Helsingforstillägget i perioder på högst 12 månader. Gör en ny ansökan efter en 12 månaders stödperiod av Helsingforstillägg." } }, "messages": { @@ -242,24 +255,29 @@ }, "isLivingInHelsinki": { "label": "Personen som anställs bor i Helsingfors", - "placeholder": "Ja, personen som anställs är skriven i Helsingfors senast när anställningsförhållandet inleds", + "placeholder": "Ja, personen som sysselsätts har Helsingfors som hemkommun senast när anställningsförhållandet inleds.", "yes": "Ja", "no": "Nej", "error": "Stöd kan beviljas endast om hemkommunen är Helsingfors." }, + "associationImmediateManagerCheck": { + "label": "Har personen som ska sysselsättas utsetts en arbetsledande chef?", + "placeholder": "Ja, personen som ska sysselsättas har en arbetsledande chef." + }, "paySubsidyGranted": { - "label": "Har lönesubvention beviljats för anställningsförhållandet?", - "yes": "Ja", - "no": "Nej" + "label": "Har något av de följande stöden beviljats för anställningsförhållandet?", + "paySubsidyDefault": "Lönesubvention", + "paySubsidyAged": "Sysselsättningsstöd för personer som fyllt 55 år", + "paySubsidyNone": "Inga av stöden ovan har beviljats för anställningsförhållandet" }, "paySubsidyPercent": { "label": "Lönesubventionsprocent enligt beslutet" }, "additionalPaySubsidyPercent": { - "label": "Om någon annan lönesubvention har beviljats för anställningen, ange lönesubventionsprocent för den." + "label": "Om någon annan lönesubvention har beviljats för anställningen, \nange lönesubventionsprocent för den." }, "apprenticeshipProgram": { - "label": "Är det fråga om ett läroavtal?", + "label": "Handlar det om ett läroavtal?", "yes": "Ja", "no": "Nej" }, @@ -274,28 +292,36 @@ "placeholder": "Titel" }, "workingHours": { - "label": "Arbetstid timmar på vecka", + "label": "Arbetstid", "placeholder": "Timmar", - "view": "Arbetstid timmar: {{workingHours}} timmar på vecka" + "view": "Arbetstid timmar: {{workingHours}} timmar på vecka", + "helperText": "timmar på vecka" }, "collectiveBargainingAgreement": { "label": "Kollektivavtal som tillämpas", - "placeholder": "Kollektivavtal som tillämpas" + "placeholder": "Kollektivavtal som tillämpas", + "tooltip": "Kollektivavtal som ska tillämpas: till exempel handelns kollektivavtal Om branschen inte har ett bindande kollektivavtal, rita ett streck." }, "monthlyPay": { "label": "Bruttolön", "placeholder": "Bruttolön", - "view": "Bruttolön {{monthlyPay}} euro per månad" + "view": "Bruttolön {{monthlyPay}} euro per månad", + "helperText": "euro per månad", + "tooltip": "Med bruttolön avses den lön som betalas till personen som ska sysselsättas med stödet innan avdrag av arbetstagarens lagstadgade avgifter (arbetstagarens arbetslöshetsförsäkrings- och pensionsförsäkringsavgift) och skatter. Om tillägg såsom kväll-, natt- eller skiftsarbetstillägg betalas som en del av lönen, beräkna en uppskattning om beloppet på tilläggen in i bruttolönen." }, "otherExpenses": { "label": "Bikostnader", "placeholder": "Bikostnader", - "view": "Bikostnader {{otherExpenses}} euro per månad" + "view": "Bikostnader {{otherExpenses}} euro per månad", + "helperText": "euro per månad", + "tooltip": "Bikostnaderna innebär beloppet på arbetsgivarens lagstadgade bikostnader som betalas ur lönen per månad. Till bikostnaderna hör socialskydds-, arbetspensionsförsäkrings-, olycksfallsförsäkrings- och arbetslöshetsförsäkringsavgift samt den obligatoriska grupplivförsäkringsavgiften. Ange beloppet på bikostnaderna i euro per månad." }, "vacationMoney": { "label": "Semesterpenning", "placeholder": "Semesterpenning", - "view": "Semesterpenning {{vacationMoney}} euro" + "view": "Semesterpenning {{vacationMoney}} euro", + "helperText": "euro per månad ", + "tooltip": "Bikostnaderna innebär beloppet på arbetsgivarens lagstadgade bikostnader som betalas ur lönen per månad. Till bikostnaderna hör socialskydds-, arbetspensionsförsäkrings-, olycksfallsförsäkrings- och arbetslöshetsförsäkringsavgift samt den obligatoriska grupplivförsäkringsavgiften. Ange beloppet på bikostnaderna i euro per månad." }, "commissionDescription": { "label": "Beskrivning av uppdraget", @@ -335,18 +361,18 @@ "message": "" }, "helsinkiBenefitVoucher": { - "title": "Sedel för Helsingforstillägg", - "message": "Lägg till sedel för Helsingforstillägg om en har utfärdats" + "title": "Helsingforstilläggskortet", + "message": "Lägg till Helsingforstilläggskortet om en har utfärdats" } }, "add": "Bifoga fil", "remove": "Ta bort bilaga" }, "summary": { - "heading1": "Ole hyvä ja tarkista hakemus" + "heading1": "Vänligen kontrollera ansökan innan du skickar in den." }, "credentials": { - "heading1": "Behandling av den anställdas personuppgifter", + "heading1": "Behandlingen av personuppgifterna av personen som ska sysselsättas", "heading2": "Behandling av personuppgifter", "sections": { "electronicPowerOfAttorney": { @@ -405,7 +431,7 @@ }, "languages": { "fi": "Suomi", - "sv": "Ruotsi", + "sv": "Svenska", "en": "English" }, "supportedLanguages": { @@ -424,7 +450,7 @@ "text": "Bifoga följande bilagor till ansökan. Alla bilagor märkta med * är obligatoriska. Filformatet kan vara JPG, PNG eller PDF och den maximala filstorleken får vara 10 MB." }, "credentialsIngress": { - "text": "Den person som anställs med Helsingforstillägg ska informeras om behandlingen av hens personuppgifter. Skriv ut informationen och be den anställda om underskrift." + "text": "Personen som ska sysselsättas med Helsingforstillägget ska informeras om hur hens personuppgifter behandlas. Skriv ut meddelandet och be om underteckning av personen som ska sysselsättas." }, "form": { "validation": { @@ -476,18 +502,18 @@ "logoutMessageLabel": "Du har loggat ut", "errorLabel": "Ett okänt fel inträffade. Logga in på nytt.", "sessionExpiredLabel": "Användarsessionen föråldrades. Logga in på nytt.", - "infoLabel": "Problem med att logga in?", + "infoLabel": "Frågor om Helsingforstillägget?", "logoutInfoContent": "Du har nu loggats ut. Om du vill fortsätta använda tjänsten, logga in på nytt genom att klicka på knappen nedan.", "infoContent": "Skicka ett mejl till helsinkilisa@hel.fi.", "termsOfServiceHeader": "Information om behandlingen av arbetsgivarens representanters uppgifter", "heading": "Logga in i Helsingforstillägg tjänsten", - "infoText1": "Sök ekonomiskt stöd, Helsingforstillägg, för att sysselsätta arbetslösa helsingforsbor. Du behöver Suomi.fi-fullmakter för att utföra ärenden för din organisation.", + "infoText1": "Ansök om ett finansiellt stöd, Helsingforstillägget, för din organisation för att sysselsätta en arbetslös person i Helsingfors. Du behöver en Suomi.fi-befogenhet för att uträtta ärenden på din organisations vägnar.", "subheading1": "Logga in med bankkoder eller mobilt certifikat", "infoText2": "När du loggar in för första gången skapas automatiskt en egen Helsingforsprofil för dig.", - "subheading2": "Skaffa Suomi.fi-fullmakter", - "infoText3": "Om du ännu inte har rättighet att handla på din organisations vägnar kan du skaffa Suomi.fi-fullmakter för dig själv.", - "authorization": "Se instruktioner för att skaffa autorisering", - "suomifiUrl": "https://www.suomi.fi/fullmakter" + "subheading2": "Suomi.fi-befogenhet", + "infoText3": "Du kan logga in på e-tjänsten för sysselsättning Helsingforstillägg om du har namnteckningsrätt eller om du har beviljats antingen “ansökan om lönesubvention“ eller “ansöka om understöd“ som fullmakt i Suomi.fi.", + "authorization": "Se anvisningarna om hur man uträttar ärenden på en organisations vägnar", + "suomifiUrl": "https://www.suomi.fi/anvisningar-och-stod/fullmakter/utrattande-av-arenden-for-en-organisation" }, "errorPage": { "title": "Tyvärr har det inträffat ett fel i tjänsten", @@ -528,8 +554,12 @@ "page": "Sida", "terms": "Terms.PDF" }, + "tooltip": { + "ariaLabel": "Visa hjälptext", + "ariaButtonLabel": "Visa hjälptext" + }, "utility": { - "and": "ja", + "and": "och", "yes": "Ja", "no": "Nej", "close": "Stäng", diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/Application.sc.ts b/frontend/benefit/applicant/src/components/applications/forms/application/Application.sc.ts index b24e234bd9..bbb2c45724 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/Application.sc.ts +++ b/frontend/benefit/applicant/src/components/applications/forms/application/Application.sc.ts @@ -1,4 +1,5 @@ -import styled from 'styled-components'; +import { $GridCell } from 'shared/components/forms/section/FormSection.sc'; +import styled, { css } from 'styled-components'; type ViewFieldProps = { isInline?: boolean; @@ -29,3 +30,22 @@ export const $SummaryTableHeader = styled.div` export const $SummaryTableValue = styled.span` font-size: ${(props) => props.theme.fontSize.body.l}; `; + +export const $KeyValueList = styled.dl``; + +const CSSFormIndentation = css` +&:before { + margin-left: -40px; + position:absolute; + content: ''; + display:block; + background: ${(props) => props.theme.colors.black10}; + width: 8px; + height 100%; +}`; + +export const $SubFieldContainer = styled($GridCell)` + margin-left: 0; + margin-left: 50px; + ${CSSFormIndentation} +`; diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/DeMinimisAidForm.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/DeMinimisAidForm.tsx index 95f1558b8a..7cfa38c563 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/DeMinimisAidForm.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/DeMinimisAidForm.tsx @@ -8,17 +8,14 @@ import { DeMinimisAid } from 'benefit-shared/types/application'; import { Button, DateInput, IconPlusCircle, TextInput } from 'hds-react'; import sumBy from 'lodash/sumBy'; import React from 'react'; -import { - $Grid, - $GridCell, - $SubHeader, -} from 'shared/components/forms/section/FormSection.sc'; +import { $GridCell } from 'shared/components/forms/section/FormSection.sc'; import { formatStringFloatValue, stringFloatToFixed, } from 'shared/utils/string.utils'; import { useTheme } from 'styled-components'; +import { $DeMinimisGridPadded, $DeMinimisSubHeader } from './deMinimisAid.sc'; import { useDeminimisAid } from './useDeminimisAid'; interface DeMinimisAidFormProps { @@ -63,26 +60,13 @@ const DeMinimisAidForm: React.FC = ({ }; return ( - <$GridCell - $colStart={3} - $colSpan={10} - as={$Grid} - columns={10} - css={` - margin-bottom: ${theme.spacing.s}; - `} - > + <> <$GridCell $colSpan={10}> - <$SubHeader>{t(`${translationsBase}.deMinimisAidsHeading`)} + <$DeMinimisSubHeader> + {t(`${translationsBase}.deMinimisAidsHeading`)} + - <$GridCell - $colSpan={8} - as={$Grid} - columns={8} - bgColor - bgHorizontalPadding - bgVerticalPadding - > + <$DeMinimisGridPadded> <$GridCell $colSpan={4}> = ({ invalid={!!getErrorMessage(DE_MINIMIS_AID_KEYS.AMOUNT)} aria-invalid={!!getErrorMessage(DE_MINIMIS_AID_KEYS.AMOUNT)} errorText={getErrorMessage(DE_MINIMIS_AID_KEYS.AMOUNT)} + helperText={t( + `${translationsBase}.fields.deMinimisAidAmount.helperText` + )} required /> - <$GridCell $colSpan={2}> + <$GridCell $colSpan={3}> = ({ required /> - - <$GridCell - $colSpan={2} - css={` - padding-top: 25px; - padding-left: ${theme.spacing.s}; - `} - > - - - + + + + ); }; diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/deMinimisAid.sc.ts b/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/deMinimisAid.sc.ts new file mode 100644 index 0000000000..a7de3daffe --- /dev/null +++ b/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/deMinimisAid.sc.ts @@ -0,0 +1,32 @@ +import { + $Grid, + $SubHeader, +} from 'shared/components/forms/section/FormSection.sc'; +import styled from 'styled-components'; + +export const $DeMinimisSubHeader = styled($SubHeader)` + margin-top: ${(props) => props.theme.spacing.xs3}; + margin-bottom: ${(props) => props.theme.spacing.s}; + margin-left: ${(props) => props.theme.spacing.xs2}; + font-weight: 400; + font-size: 1.1em; +`; + +export const $DeMinimisGrid = styled($Grid)` + max-width: 1024px; + margin-left: calc(-1 * ${(props) => props.theme.spacing.s}); + padding-left: ${(props) => props.theme.spacing.m}; + padding-right: ${(props) => props.theme.spacing.m}; +`; + +type DeMinimisGridProps = { + $bgColor?: string; +}; + +export const $DeMinimisGridPadded = styled($DeMinimisGrid)` + padding: ${(props) => props.theme.spacing.m}; + margin-left: calc(-1 * ${(props) => props.theme.spacing.s}); + background: ${(props) => + props.$bgColor ? props.$bgColor : props.theme.colors.black10}; + margin-bottom: ${(props) => props.theme.spacing.s}; +`; diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/list/DeMinimisAidsList.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/list/DeMinimisAidsList.tsx index 3830a2ba68..3de8e6693a 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/list/DeMinimisAidsList.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/deMinimisAid/list/DeMinimisAidsList.tsx @@ -12,6 +12,7 @@ import { convertToUIDateFormat } from 'shared/utils/date.utils'; import { formatStringFloatValue } from 'shared/utils/string.utils'; import { useTheme } from 'styled-components'; +import { $DeMinimisGrid } from '../deMinimisAid.sc'; import { useDeminimisAidsList } from './useDeminimisAidsList'; const DeMinimisAidsList: React.FC = () => { @@ -21,21 +22,17 @@ const DeMinimisAidsList: React.FC = () => { return ( <> {grants?.map((grant, i) => ( - <$GridCell - $colStart={3} - $colSpan={10} - as={$Grid} - columns={10} + <$DeMinimisGrid key={`${grant[DE_MINIMIS_AID_KEYS.GRANTER] ?? ''}${ grant[DE_MINIMIS_AID_KEYS.AMOUNT] ?? '' }${grant[DE_MINIMIS_AID_KEYS.GRANTED_AT] ?? ''}`} > <$GridCell - $colSpan={8} + css="margin-left: 15px" + $colSpan={12} as={$Grid} - columns={8} + columns={12} alignItems="center" - bgColor bgHorizontalPadding > <$GridCell $colSpan={4}> @@ -47,24 +44,26 @@ const DeMinimisAidsList: React.FC = () => { <$GridCell $colSpan={2}> {convertToUIDateFormat(grant[DE_MINIMIS_AID_KEYS.GRANTED_AT])} - - <$GridCell - $colSpan={2} - css={` - padding-left: ${theme.spacing.s}; - `} - > - + + - + ))} {sumBy(grants, (grant) => Number(grant.amount)) > MAX_DEMINIMIS_AID_TOTAL_AMOUNT && ( diff --git a/frontend/benefit/applicant/src/components/applications/forms/application/step1/ApplicationFormStep1.tsx b/frontend/benefit/applicant/src/components/applications/forms/application/step1/ApplicationFormStep1.tsx index 2a77e86bf3..f97a9acba9 100644 --- a/frontend/benefit/applicant/src/components/applications/forms/application/step1/ApplicationFormStep1.tsx +++ b/frontend/benefit/applicant/src/components/applications/forms/application/step1/ApplicationFormStep1.tsx @@ -8,9 +8,13 @@ import React from 'react'; import { $RadioButton } from 'shared/components/forms/fields/Fields.sc'; import { Option } from 'shared/components/forms/fields/types'; import FormSection from 'shared/components/forms/section/FormSection'; -import { $GridCell } from 'shared/components/forms/section/FormSection.sc'; +import { + $GridCell, + $SubHeader, +} from 'shared/components/forms/section/FormSection.sc'; import { phoneToLocal } from 'shared/utils/string.utils'; +import { $SubFieldContainer } from '../Application.sc'; import DeMinimisAidForm from '../deMinimisAid/DeMinimisAidForm'; import DeMinimisAidsList from '../deMinimisAid/list/DeMinimisAidsList'; import StepperActions from '../stepperActions/StepperActions'; @@ -30,7 +34,7 @@ const ApplicationFormStep1: React.FC = ({ handleDelete, getErrorMessage, clearDeminimisAids, - getDefaultSelectValue, + getDefaultLanguage, showDeminimisSection, languageOptions, fields, @@ -62,7 +66,12 @@ const ApplicationFormStep1: React.FC = ({ translationsBase={translationsBase} fields={fields} /> - + + <$GridCell $colSpan={12}> + <$SubHeader weight="400"> + {t(`${translationsBase}.companyContactPerson.content`)} + + <$GridCell $colSpan={3}> = ({ <$GridCell $colSpan={3}>