diff --git a/backend/benefit/applications/api/v1/instalment_views.py b/backend/benefit/applications/api/v1/instalment_views.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/backend/benefit/applications/api/v1/serializers/application.py b/backend/benefit/applications/api/v1/serializers/application.py index 89e98f350f..6e032fe8d5 100755 --- a/backend/benefit/applications/api/v1/serializers/application.py +++ b/backend/benefit/applications/api/v1/serializers/application.py @@ -90,13 +90,15 @@ from users.utils import get_company_from_request, get_request_user_from_context -def _get_pending_instalment(application): - """Get the latest pending instalment for the application""" +def _get_instalment(application, instalment_number): + """Get the second instalment for the application""" try: - instalments = application.calculation.instalments.filter( - instalment_number__gt=1 + instalment = ( + application.calculation.instalments.filter( + instalment_number=instalment_number + ).first() + or None ) - instalment = instalments.filter(instalment_number=2).first() or None if instalment is not None: return InstalmentSerializer(instalment).data except AttributeError: @@ -1638,10 +1640,14 @@ class HandlerApplicationSerializer(BaseApplicationSerializer): ahjo_error = serializers.SerializerMethodField() - pending_instalment = serializers.SerializerMethodField("get_pending_instalment") + first_instalment = serializers.SerializerMethodField("get_first_instalment") + second_instalment = serializers.SerializerMethodField("get_second_instalment") - def get_pending_instalment(self, application): - return _get_pending_instalment(application) + def get_first_instalment(self, application): + return _get_instalment(application, 1) + + def get_second_instalment(self, application): + return _get_instalment(application, 2) def get_latest_ahjo_error(self, obj) -> Union[Dict, None]: """Get the latest Ahjo error for the application""" @@ -1703,13 +1709,15 @@ class Meta(BaseApplicationSerializer.Meta): "handler", "handled_by_ahjo_automation", "ahjo_error", - "pending_instalment", + "first_instalment", + "second_instalment", ] read_only_fields = BaseApplicationSerializer.Meta.read_only_fields + [ "latest_decision_comment", "handled_at", "handler", - "pending_instalment", + "first_instalment", + "second_instalment", ] @transaction.atomic @@ -1912,7 +1920,8 @@ class Meta: "batch", "ahjo_error", "talpa_status", - "pending_instalment", + "first_instalment", + "second_instalment", ] read_only_fields = [ @@ -1937,7 +1946,8 @@ class Meta: "batch", "ahjo_error", "talpa_status", - "pending_instalment", + "first_instalment", + "second_instalment", ] archived = serializers.BooleanField() @@ -1958,10 +1968,14 @@ class Meta: "Timestamp when the application was handled (accepted/rejected/cancelled)" ), ) - pending_instalment = serializers.SerializerMethodField("get_pending_instalment") + first_instalment = serializers.SerializerMethodField("get_first_instalment") + second_instalment = serializers.SerializerMethodField("get_second_instalment") + + def get_first_instalment(self, application): + return _get_instalment(application, 1) - def get_pending_instalment(self, application): - return _get_pending_instalment(application) + def get_second_instalment(self, application): + return _get_instalment(application, 2) ahjo_error = serializers.SerializerMethodField("get_latest_ahjo_error") diff --git a/backend/benefit/applications/services/clone_application.py b/backend/benefit/applications/services/clone_application.py index 317ae80a0b..019691b9de 100644 --- a/backend/benefit/applications/services/clone_application.py +++ b/backend/benefit/applications/services/clone_application.py @@ -43,7 +43,11 @@ def clone_application_based_on_other( "alternative_company_postcode": application_base.alternative_company_postcode, "alternative_company_street_address": application_base.alternative_company_street_address, "applicant_language": "fi", - "application_origin": ApplicationOrigin.APPLICANT, + "application_origin": ( + application_base.application_origin + if clone_all_data + else ApplicationOrigin.APPLICANT + ), "application_step": ApplicationStep.STEP_1, "archived": False, "association_has_business_activities": application_base.association_has_business_activities diff --git a/backend/benefit/calculator/api/v1/views.py b/backend/benefit/calculator/api/v1/views.py index 2a6ff9c168..701fa201be 100644 --- a/backend/benefit/calculator/api/v1/views.py +++ b/backend/benefit/calculator/api/v1/views.py @@ -5,6 +5,7 @@ from rest_framework.response import Response from rest_framework.views import APIView +from applications.enums import ApplicationTalpaStatus from calculator.api.v1.serializers import ( InstalmentSerializer, PreviousBenefitSerializer, @@ -53,9 +54,43 @@ def patch(self, request, instalment_id): ) if serializer.is_valid(): serializer.save() - if instalment_status == InstalmentStatus.CANCELLED: - application = instalment.calculation.application - application.archived = True - application.save() - return Response(serializer.data, status=status.HTTP_200_OK) + application = instalment.calculation.application + instalment_count = instalment.calculation.instalments.count() + + if instalment.instalment_number == 1: + if instalment_status == InstalmentStatus.WAITING: + application.talpa_status = ( + ApplicationTalpaStatus.NOT_PROCESSED_BY_TALPA + ) + application.save() + + if instalment_status == InstalmentStatus.PAID: + if instalment.instalment_number == 1: + application.talpa_status = ( + ApplicationTalpaStatus.SUCCESSFULLY_SENT_TO_TALPA + ) + else: + application.talpa_status = ( + ApplicationTalpaStatus.PARTIALLY_SENT_TO_TALPA + ) + instalment.amount_paid = instalment.amount_after_recoveries + + if instalment_count == 1: + application.archived = True + + instalment.save() + application.save() + return Response(serializer.data, status=status.HTTP_200_OK) + if instalment.instalment_number == 2: + first_instalment = instalment.calculation.instalments.get( + instalment_number=1 + ) + if ( + instalment_status == InstalmentStatus.CANCELLED + and first_instalment.amount_paid is not None + ): + application.archived = True + application.save() + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/frontend/benefit/handler/public/locales/en/common.json b/frontend/benefit/handler/public/locales/en/common.json index 34114e7f07..d3e09972c5 100644 --- a/frontend/benefit/handler/public/locales/en/common.json +++ b/frontend/benefit/handler/public/locales/en/common.json @@ -753,6 +753,10 @@ "noChanges": "Ei muutoksia lomaketietoihin. Mikäli poistit tai lisäsit liitetiedostoja, voit kirjata niiden syyn." }, "helperText": "Kirjaa milloin ja mitä kautta hakija on ollut yhteydessä. Tiedot tulevat näkyviin hakemuksen muutoshistoriassa." + }, + "talpaStatusChange": { + "heading": "Maksun tilan muutos", + "text": "Tuen automaattinen maksu on epäonnistunut. Palauta hakemus odottamaan automaattista Talpa-käsittelyä tai merkitse maksu suoritetuksi jolloin maksutapahtuma on vahvistettava manuaalisesti Talpalta." } }, "decision": { diff --git a/frontend/benefit/handler/public/locales/fi/common.json b/frontend/benefit/handler/public/locales/fi/common.json index 1693c71da7..fb72597620 100644 --- a/frontend/benefit/handler/public/locales/fi/common.json +++ b/frontend/benefit/handler/public/locales/fi/common.json @@ -753,6 +753,10 @@ "noChanges": "Ei muutoksia lomaketietoihin. Mikäli poistit tai lisäsit liitetiedostoja, voit kirjata niiden syyn." }, "helperText": "Kirjaa milloin ja mitä kautta hakija on ollut yhteydessä. Tiedot tulevat näkyviin hakemuksen muutoshistoriassa." + }, + "talpaStatusChange": { + "heading": "Maksun tilan muutos", + "text": "Tuen automaattinen maksu on epäonnistunut. Palauta hakemus odottamaan automaattista Talpa-käsittelyä tai merkitse maksu suoritetuksi jolloin maksutapahtuma on vahvistettava manuaalisesti Talpalta." } }, "decision": { diff --git a/frontend/benefit/handler/public/locales/sv/common.json b/frontend/benefit/handler/public/locales/sv/common.json index 2749cf153e..ffc8a41cc4 100644 --- a/frontend/benefit/handler/public/locales/sv/common.json +++ b/frontend/benefit/handler/public/locales/sv/common.json @@ -753,6 +753,10 @@ "noChanges": "Ei muutoksia lomaketietoihin. Mikäli poistit tai lisäsit liitetiedostoja, voit kirjata niiden syyn." }, "helperText": "Kirjaa milloin ja mitä kautta hakija on ollut yhteydessä. Tiedot tulevat näkyviin hakemuksen muutoshistoriassa." + }, + "talpaStatusChange": { + "heading": "Maksun tilan muutos", + "text": "Tuen automaattinen maksu on epäonnistunut. Palauta hakemus odottamaan automaattista Talpa-käsittelyä tai merkitse maksu suoritetuksi jolloin maksutapahtuma on vahvistettava manuaalisesti Talpalta." } }, "decision": { diff --git a/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx b/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx index 7c07013f99..9f44d5d00b 100644 --- a/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx +++ b/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx @@ -1,4 +1,5 @@ import { ALL_APPLICATION_STATUSES, ROUTES } from 'benefit/handler/constants'; +import useInstalmentStatusTransition from 'benefit/handler/hooks/useInstalmentStatusTransition'; import { ApplicationListTableColumns, ApplicationListTableTransforms, @@ -7,7 +8,11 @@ import { getTagStyleForStatus, getTalpaTagStyleForStatus, } from 'benefit/handler/utils/applications'; -import { APPLICATION_STATUSES, TALPA_STATUSES } from 'benefit-shared/constants'; +import { + APPLICATION_STATUSES, + INSTALMENT_STATUSES, + TALPA_STATUSES, +} from 'benefit-shared/constants'; import { AhjoError, ApplicationAlteration, @@ -37,6 +42,7 @@ import { $TagWrapper, $UnreadMessagesCount, } from './ApplicationList.sc'; +import TalpaStatusChangeModal from './TalpaStatusChangeDialog'; import { useApplicationList } from './useApplicationList'; export interface ApplicationListProps { @@ -65,14 +71,14 @@ const buildApplicationUrl = ( const getFirstInstalmentTotalAmount = ( calculatedBenefitAmount: string, - pendingInstalment?: Instalment, + secondInstalment?: Instalment, alterations?: ApplicationAlteration[] ): string | JSX.Element => { let firstInstalment = parseInt(calculatedBenefitAmount, 10); let recoveryAmount = 0; - if (pendingInstalment) { + if (secondInstalment) { firstInstalment -= parseInt( - String(pendingInstalment?.amountAfterRecoveries), + String(secondInstalment?.amountAfterRecoveries), 10 ); recoveryAmount = alterations @@ -83,7 +89,7 @@ const getFirstInstalmentTotalAmount = ( ) : 0; } - return pendingInstalment ? ( + return secondInstalment ? ( <> {formatFloatToEvenEuros(firstInstalment)} /{' '} {formatFloatToEvenEuros( @@ -100,10 +106,18 @@ const dateForAdditionalInformationNeededBy = ( export const renderPaymentTagPerStatus = ( t: TFunction, - talpaStatus?: TALPA_STATUSES + talpaStatus?: TALPA_STATUSES, + id?: string, + clickTalpaTag?: (id: string, talpaStatus: TALPA_STATUSES) => void ): JSX.Element => ( <$TagWrapper $colors={getTalpaTagStyleForStatus(talpaStatus)}> - + clickTalpaTag(id, talpaStatus) + : null + } + > {t(`applications.list.columns.talpaStatuses.${String(talpaStatus)}`)} @@ -150,6 +164,20 @@ const ApplicationList: React.FC = ({ [isAllStatuses, status] ); + const [showTalpaModal, setShowTaplaModal] = React.useState(false); + const [selectedApplication, setSelectedApplication] = React.useState(''); + + const { + mutate: changeInstalmentStatus, + isSuccess: isInstalmentStatusChanged, + } = useInstalmentStatusTransition(); + + React.useEffect(() => { + if (isInstalmentStatusChanged) { + setShowTaplaModal(false); + } + }, [isInstalmentStatusChanged]); + const renderTableActions = React.useCallback( ( id: string, @@ -219,10 +247,6 @@ const ApplicationList: React.FC = ({ [t] ); - const renderPaymentTagWrapper = React.useCallback(renderPaymentTagPerStatus, [ - t, - ]); - const columns = React.useMemo(() => { const cols: ApplicationListTableColumns[] = [ { @@ -366,6 +390,10 @@ const ApplicationList: React.FC = ({ isSortable: true, }); } + const onTalpaTagClick = (id: string): void => { + setShowTaplaModal(true); + setSelectedApplication(id); + }; if (inPayment) { cols.push( @@ -379,19 +407,27 @@ const ApplicationList: React.FC = ({ headerName: getHeader('paymentStatus'), key: 'paymentStatus', isSortable: true, - transform: ({ talpaStatus }: ApplicationListTableTransforms) => - renderPaymentTagWrapper(t, talpaStatus as TALPA_STATUSES), + transform: ({ + talpaStatus, + firstInstalment, + }: ApplicationListTableTransforms) => + renderPaymentTagPerStatus( + t, + talpaStatus as TALPA_STATUSES, + firstInstalment?.id, + onTalpaTagClick + ), }, { headerName: getHeader('calculatedBenefitAmount'), key: 'calculatedBenefitAmount', transform: ({ calculatedBenefitAmount, - pendingInstalment, + secondInstalment, }: ApplicationListTableTransforms) => getFirstInstalmentTotalAmount( String(calculatedBenefitAmount), - pendingInstalment || null + secondInstalment || null ), } ); @@ -412,7 +448,6 @@ const ApplicationList: React.FC = ({ renderTagWrapper, renderTableActions, t, - renderPaymentTagWrapper, ]); if (isLoading) { @@ -434,6 +469,10 @@ const ApplicationList: React.FC = ({ } const statusAsString = isAllStatuses ? 'all' : status.join(','); + const handleTalpaStatusChange = (talpaStatus: INSTALMENT_STATUSES): void => { + changeInstalmentStatus({ id: selectedApplication, status: talpaStatus }); + }; + return ( <$ApplicationList data-testid={`application-list-${statusAsString}`}> {list.length > 0 ? ( @@ -450,6 +489,12 @@ const ApplicationList: React.FC = ({ {t(`${translationsBase}.messages.empty.${statusAsString}`)} )} + + setShowTaplaModal(false)} + onStatusChange={handleTalpaStatusChange} + /> ); }; diff --git a/frontend/benefit/handler/src/components/applicationList/ApplicationListForInstalments.tsx b/frontend/benefit/handler/src/components/applicationList/ApplicationListForInstalments.tsx index 5772eb2a0f..6e91317b98 100644 --- a/frontend/benefit/handler/src/components/applicationList/ApplicationListForInstalments.tsx +++ b/frontend/benefit/handler/src/components/applicationList/ApplicationListForInstalments.tsx @@ -11,11 +11,7 @@ import { INSTALMENT_STATUSES, } from 'benefit-shared/constants'; import { ApplicationListItemData } from 'benefit-shared/types/application'; -import { - IconErrorFill, - Table, - Tag, -} from 'hds-react'; +import { IconErrorFill, Table, Tag } from 'hds-react'; import noop from 'lodash/noop'; import { TFunction } from 'next-i18next'; import * as React from 'react'; @@ -129,37 +125,35 @@ const ApplicationListForInstalments: React.FC = ({ key: 'dueDate', isSortable: true, customSortCompareFunction: sortFinnishDate, - transform: ({ pendingInstalment }: ApplicationListTableTransforms) => - convertToUIDateFormat(pendingInstalment?.dueDate), + transform: ({ secondInstalment }: ApplicationListTableTransforms) => + convertToUIDateFormat(secondInstalment?.dueDate), }, { - transform: ({ pendingInstalment }: ApplicationListTableTransforms) => + transform: ({ secondInstalment }: ApplicationListTableTransforms) => renderInstalmentTagPerStatus( t, - pendingInstalment?.status as INSTALMENT_STATUSES + secondInstalment?.status as INSTALMENT_STATUSES ), headerName: getHeader('paymentStatus'), key: 'status', isSortable: true, }, { - transform: ({ pendingInstalment }: ApplicationListTableTransforms) => - pendingInstalment?.amountAfterRecoveries > 0 ? ( + transform: ({ secondInstalment }: ApplicationListTableTransforms) => + secondInstalment?.amountAfterRecoveries > 0 ? ( <> {formatFloatToEvenEuros( - Math.max(0, pendingInstalment?.amountAfterRecoveries) + Math.max(0, secondInstalment?.amountAfterRecoveries) )} {' / '} - {formatFloatToEvenEuros(pendingInstalment.amount)} + {formatFloatToEvenEuros(secondInstalment.amount)} ) : ( <$Wrapper> <$Column> {' '} - {formatFloatToEvenEuros( - pendingInstalment.amountAfterRecoveries - )} + {formatFloatToEvenEuros(secondInstalment.amountAfterRecoveries)} ), @@ -211,7 +205,7 @@ const ApplicationListForInstalments: React.FC = ({ list.find( (app: ApplicationListItemData) => app.id === String(selectedApplication?.id) - )?.pendingInstalment || null; + )?.secondInstalment || null; const onSubmitCancel = (): void => { changeInstalmentStatus({ @@ -237,17 +231,17 @@ const ApplicationListForInstalments: React.FC = ({ setSelectedRows={setSelectedRows} zebra /> - {selectedRows.length > 0 && ( - - )} + {selectedRows.length > 0 && ( + + )} void; setIsInstalmentCancelModalShown: (show: boolean) => void; } @@ -30,23 +30,22 @@ const ApplicationTableFooter: React.FC = ({ isLoadingStatusChange, translationsBase, changeInstalmentStatus, - setIsInstalmentCancelModalShown + setIsInstalmentCancelModalShown, }) => { const { t } = useTranslation(); - const selectedApplication = list.find((app) => app.id === selectedRows[0]); const selectedInstalment = list.find( (app: ApplicationListItemData) => app.id === String(selectedApplication?.id) - )?.pendingInstalment || null; + )?.secondInstalment || null; const handleStatusChange = (status: INSTALMENT_STATUSES): void => { - changeInstalmentStatus({ - id: selectedInstalment.id, - status, - }); + changeInstalmentStatus({ + id: selectedInstalment.id, + status, + }); }; // If no rows or multiple rows selected, show hint @@ -117,9 +116,9 @@ const ApplicationTableFooter: React.FC = ({ INSTALMENT_STATUSES.CANCELLED, ].includes(selectedInstalment?.status as INSTALMENT_STATUSES) && ( handleStatusChange(INSTALMENT_STATUSES.WAITING)} + isLoading={isLoading} + isLoadingStatusChange={isLoadingStatusChange} + onClick={() => handleStatusChange(INSTALMENT_STATUSES.WAITING)} > {t(`${translationsBase}.actions.return`)} @@ -130,4 +129,4 @@ const ApplicationTableFooter: React.FC = ({ ); }; -export default ApplicationTableFooter; \ No newline at end of file +export default ApplicationTableFooter; diff --git a/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx b/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx index 971b7224a5..a0a933f4b6 100644 --- a/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx +++ b/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx @@ -78,8 +78,7 @@ const HandlerIndex: React.FC = ({ const getTabCountInstalments = (): number => list.filter( - (app: ApplicationListItemData) => - app.pendingInstalment && isInPayment(app) + (app: ApplicationListItemData) => app.secondInstalment && isInPayment(app) ).length; const getTabCountInPayment = (): number => @@ -199,7 +198,7 @@ const HandlerIndex: React.FC = ({ - updateTabToUrl(APPLICATION_LIST_TABS.PENDING_INSTALMENTS) + updateTabToUrl(APPLICATION_LIST_TABS.SECOND_INSTALMENTS) } > {getListHeadingByStatus('instalments', [ @@ -293,7 +292,7 @@ const HandlerIndex: React.FC = ({ app.pendingInstalment && isInPayment(app) + (app) => app.secondInstalment && isInPayment(app) )} heading={t(`${translationBase}.instalments`)} /> diff --git a/frontend/benefit/handler/src/components/applicationList/TalpaStatusChangeDialog.tsx b/frontend/benefit/handler/src/components/applicationList/TalpaStatusChangeDialog.tsx new file mode 100644 index 0000000000..84187938f7 --- /dev/null +++ b/frontend/benefit/handler/src/components/applicationList/TalpaStatusChangeDialog.tsx @@ -0,0 +1,67 @@ +import { INSTALMENT_STATUSES } from 'benefit-shared/constants'; +import { Button, Dialog, IconArrowRedo, IconArrowUndo } from 'hds-react'; +import React from 'react'; +import { + $Grid, + $GridCell, +} from 'shared/components/forms/section/FormSection.sc'; +import Modal from 'shared/components/modal/Modal'; +import { useTheme } from 'styled-components'; + +import { useApplicationList } from './useApplicationList'; + +const TalpaStatusChangeModal: React.FC<{ + isOpen: boolean; + onClose: () => void; + onStatusChange: (status: INSTALMENT_STATUSES) => void; +}> = ({ isOpen, onClose, onStatusChange }) => { + const { t } = useApplicationList(); + const theme = useTheme(); + + return ( + false} + handleSubmit={() => false} + customContent={ + <> + + <$Grid columns={1} alignItems="center"> + <$GridCell> +

{t('applications.dialog.talpaStatusChange.heading')}

+

{t('applications.dialog.talpaStatusChange.text')}

+ + +
+ + + + + + + + } + /> + ); +}; + +export default TalpaStatusChangeModal; diff --git a/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts b/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts index 66db8d5b93..bd7152eefa 100644 --- a/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts +++ b/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts @@ -45,7 +45,8 @@ const useApplicationListData = ( handled_by_ahjo_automation, handled_at: handledAt, ahjo_error, - pending_instalment, + first_instalment, + second_instalment, alterations, } = application; @@ -77,7 +78,8 @@ const useApplicationListData = ( ahjoError: camelcaseKeys(ahjo_error, { deep: true }) || null, decisionDate: convertToUIDateFormat(batch?.decision_date) || '-', calculatedBenefitAmount: calculation?.calculated_benefit_amount || '0', - pendingInstalment: camelcaseKeys(pending_instalment), + firstInstalment: camelcaseKeys(first_instalment), + secondInstalment: camelcaseKeys(second_instalment), alterations: alterations || [], }; }) diff --git a/frontend/benefit/handler/src/components/applicationReview/handlingView/InstalmentAccordionSections.tsx b/frontend/benefit/handler/src/components/applicationReview/handlingView/InstalmentAccordionSections.tsx index 9465a608ef..002e1bb188 100644 --- a/frontend/benefit/handler/src/components/applicationReview/handlingView/InstalmentAccordionSections.tsx +++ b/frontend/benefit/handler/src/components/applicationReview/handlingView/InstalmentAccordionSections.tsx @@ -1,4 +1,5 @@ import { $Section } from 'benefit/handler/components/applicationReview/handlingView/DecisionCalculationAccordion.sc'; +import useInstalmentStatusTransition from 'benefit/handler/hooks/useInstalmentStatusTransition'; import { ALTERATION_STATE, INSTALMENT_STATUSES, @@ -14,6 +15,7 @@ import { formatFloatToEvenEuros } from 'shared/utils/string.utils'; import { renderPaymentTagPerStatus } from '../../applicationList/ApplicationList'; import { renderInstalmentTagPerStatus } from '../../applicationList/ApplicationListForInstalments'; +import TalpaStatusChangeModal from '../../applicationList/TalpaStatusChangeDialog'; import { $Column, $Wrapper, @@ -28,17 +30,38 @@ type Props = { const InstalmentAccordionSections: React.FC = ({ data }) => { const translationsBase = 'common:calculators.result'; const { t } = useTranslation(); - - const secondInstalmentText = data.pendingInstalment ? ( + const [showTalpaModal, setShowTaplaModal] = React.useState(false); + const { + mutate: changeInstalmentStatus, + isSuccess: isInstalmentStatusChanged, + } = useInstalmentStatusTransition(); + + React.useEffect(() => { + if (isInstalmentStatusChanged) { + setShowTaplaModal(false); + } + }, [isInstalmentStatusChanged]); + + const handleTalpaStatusChange = (talpaStatus: INSTALMENT_STATUSES): void => { + changeInstalmentStatus({ + id: data?.firstInstalment?.id, + status: talpaStatus, + }); + }; + const secondInstalmentText = data.secondInstalment ? ( <> {t(`${translationsBase}.secondInstalment`)}{' '} - {convertToUIDateFormat(data.pendingInstalment.dueDate)} + {convertToUIDateFormat(data.secondInstalment.dueDate)} ) : null; const { amounts, areInstalmentsPaid, isSecondInstalmentReduced } = useInstalmentAccordionSections(data); + const onTalpaTagClick = (): void => { + setShowTaplaModal(true); + }; + return ( <> <$Section> @@ -46,13 +69,18 @@ const InstalmentAccordionSections: React.FC = ({ data }) => { <$ViewField>{t(`${translationsBase}.firstInstalment`)} <$RowWrap> - {renderPaymentTagPerStatus(t, data.talpaStatus)} + {renderPaymentTagPerStatus( + t, + data.talpaStatus, + data?.firstInstalment?.id, + onTalpaTagClick + )}
{formatFloatToEvenEuros(amounts.firstInstalment)}
- {data.pendingInstalment && ( + {data.secondInstalment && ( <$Section> <$CalculatorTableRow> <$ViewField> @@ -62,7 +90,7 @@ const InstalmentAccordionSections: React.FC = ({ data }) => { INSTALMENT_STATUSES.CANCELLED, INSTALMENT_STATUSES.ACCEPTED, ].includes( - data.pendingInstalment?.status as INSTALMENT_STATUSES + data.secondInstalment?.status as INSTALMENT_STATUSES ) ? ( {secondInstalmentText} ) : ( @@ -72,7 +100,7 @@ const InstalmentAccordionSections: React.FC = ({ data }) => { <$RowWrap> {renderInstalmentTagPerStatus( t, - data.pendingInstalment?.status as INSTALMENT_STATUSES + data.secondInstalment?.status as INSTALMENT_STATUSES )} {isSecondInstalmentReduced && ( @@ -142,8 +170,8 @@ const InstalmentAccordionSections: React.FC = ({ data }) => { <$Section> <$CalculatorTableRow> <$ViewField isBold> - {data.pendingInstalment?.status === - INSTALMENT_STATUSES.COMPLETED || !data.pendingInstalment + {data.secondInstalment?.status === + INSTALMENT_STATUSES.COMPLETED || !data.secondInstalment ? t(`${translationsBase}.totalPaidSum`) : t(`${translationsBase}.totalPlannedSum`)} @@ -179,6 +207,12 @@ const InstalmentAccordionSections: React.FC = ({ data }) => { {formatFloatToEvenEuros(amounts.totalAfterRecoveries)} + + setShowTaplaModal(false)} + onStatusChange={handleTalpaStatusChange} + /> ); }; diff --git a/frontend/benefit/handler/src/components/applicationReview/handlingView/useInstalmentAccordionSections.ts b/frontend/benefit/handler/src/components/applicationReview/handlingView/useInstalmentAccordionSections.ts index 78647ab50e..3d613e656e 100644 --- a/frontend/benefit/handler/src/components/applicationReview/handlingView/useInstalmentAccordionSections.ts +++ b/frontend/benefit/handler/src/components/applicationReview/handlingView/useInstalmentAccordionSections.ts @@ -20,15 +20,15 @@ type Props = { const useInstalmentAccordionSections = (data: Application): Props => { const amounts = { - firstInstalment: data.pendingInstalment + firstInstalment: data.secondInstalment ? Math.max( 0, parseInt(data.calculation.calculatedBenefitAmount, 10) - - data.pendingInstalment.amount + data.secondInstalment.amount ) : parseInt(data.calculation.calculatedBenefitAmount, 10), - secondInstalment: data.pendingInstalment?.amountAfterRecoveries || 0, - secondInstalmentMax: data.pendingInstalment?.amount || 0, + secondInstalment: data.secondInstalment?.amountAfterRecoveries || 0, + secondInstalmentMax: data.secondInstalment?.amount || 0, total: 0, totalAfterRecoveries: 0, alterations: @@ -47,8 +47,8 @@ const useInstalmentAccordionSections = (data: Application): Props => { formatFloatToEvenEuros(amounts.secondInstalmentMax); const areInstalmentsPaid = - data.pendingInstalment?.status === INSTALMENT_STATUSES.COMPLETED || - !data.pendingInstalment; + data.secondInstalment?.status === INSTALMENT_STATUSES.COMPLETED || + !data.secondInstalment; return { amounts, diff --git a/frontend/benefit/handler/src/constants.ts b/frontend/benefit/handler/src/constants.ts index 50466fa4c9..359c676f05 100644 --- a/frontend/benefit/handler/src/constants.ts +++ b/frontend/benefit/handler/src/constants.ts @@ -235,7 +235,7 @@ export enum APPLICATION_LIST_TABS { ACCEPTED = '4', REJECTED = '5', IN_PAYMENT = '5', - PENDING_INSTALMENTS = '6', + SECOND_INSTALMENTS = '6', } export const DEFAULT_MINIMUM_RECOVERY_AMOUNT = 20; diff --git a/frontend/benefit/handler/src/hooks/useInstalmentStatusTransition.ts b/frontend/benefit/handler/src/hooks/useInstalmentStatusTransition.ts index e2d0ca58d8..5d37f9ea0c 100644 --- a/frontend/benefit/handler/src/hooks/useInstalmentStatusTransition.ts +++ b/frontend/benefit/handler/src/hooks/useInstalmentStatusTransition.ts @@ -32,6 +32,8 @@ const useInstalmentStatusTransition = (): UseMutationResult< { onSuccess: () => { void queryClient.invalidateQueries('applicationsList'); + void queryClient.invalidateQueries('application'); + void queryClient.invalidateQueries('applications'); }, onError: (error: AxiosError>) => { showErrorToast( diff --git a/frontend/benefit/handler/src/types/applicationList.d.ts b/frontend/benefit/handler/src/types/applicationList.d.ts index 4d0f75bd36..7bbb3a5458 100644 --- a/frontend/benefit/handler/src/types/applicationList.d.ts +++ b/frontend/benefit/handler/src/types/applicationList.d.ts @@ -10,7 +10,8 @@ export interface ApplicationListTableTransforms { applicationOrigin?: APPLICATION_ORIGINS; ahjoError: AhjoError; calculatedBenefitAmount?: string; - pendingInstalment?: Instalment; + firstInstalment?: Instalment; + secondInstalment?: Instalment; alterations: ApplicationAlterationData[]; talpaStatus?: TALPA_STATUSES; } diff --git a/frontend/benefit/handler/src/utils/applications.ts b/frontend/benefit/handler/src/utils/applications.ts index fa500b260c..543224981c 100644 --- a/frontend/benefit/handler/src/utils/applications.ts +++ b/frontend/benefit/handler/src/utils/applications.ts @@ -112,6 +112,7 @@ export const getTalpaTagStyleForStatus = ( break; case TALPA_STATUSES.SUCCESFULLY_SENT_TO_TALPA: + case TALPA_STATUSES.PARTIALLY_SENT_TO_TALPA: background = theme.colors.success; text = theme.colors.white; break; diff --git a/frontend/benefit/shared/src/constants.ts b/frontend/benefit/shared/src/constants.ts index 4326fb6061..4a64136299 100644 --- a/frontend/benefit/shared/src/constants.ts +++ b/frontend/benefit/shared/src/constants.ts @@ -155,7 +155,8 @@ export enum APPLICATION_STATUSES { export enum TALPA_STATUSES { NOT_SENT_TO_TALPA = 'not_sent_to_talpa', REJECTED_BY_TALPA = 'rejected_by_talpa', - SUCCESFULLY_SENT_TO_TALPA = 'succesfully_sent_to_talpa', + SUCCESFULLY_SENT_TO_TALPA = 'successfully_sent_to_talpa', + PARTIALLY_SENT_TO_TALPA = 'partially_sent_to_talpa', } export enum APPLICATION_ORIGINS { diff --git a/frontend/benefit/shared/src/types/application.d.ts b/frontend/benefit/shared/src/types/application.d.ts index 9db4e7c6d4..d7f03f76fa 100644 --- a/frontend/benefit/shared/src/types/application.d.ts +++ b/frontend/benefit/shared/src/types/application.d.ts @@ -324,7 +324,8 @@ export type Application = { ahjoStatus?: string; handledByAhjoAutomation?: boolean; batchStatus?: BATCH_STATUSES; - pendingInstalment?: Instalment; + firstInstalment?: Instalment; + secondInstalment?: Instalment; talpaStatus?: TALPA_STATUSES; } & Step1 & Step2; @@ -518,7 +519,8 @@ export type ApplicationData = { handled_by_ahjo_automation?: boolean; alterations: ApplicationAlterationData[]; ahjo_error?: AhjoErrorData; - pending_instalment?: InstalmentData; + first_instalment?: InstalmentData; + second_instalment?: InstalmentData; }; export type EmployeeData = { @@ -619,7 +621,8 @@ export type ApplicationListItemData = { ahjoError?: AhjoError; decisionDate?: string; calculatedBenefitAmount?: string; - pendingInstalment?: Instalment; + firstInstalment?: Instalment; + secondInstalment?: Instalment; }; export type TextProp = 'textFi' | 'textEn' | 'textSv';