diff --git a/backend/benefit/applications/api/v1/serializers/application.py b/backend/benefit/applications/api/v1/serializers/application.py index f41dd9a122..9f42b2ad03 100755 --- a/backend/benefit/applications/api/v1/serializers/application.py +++ b/backend/benefit/applications/api/v1/serializers/application.py @@ -41,6 +41,7 @@ ApplicationBatchStatus, ApplicationOrigin, ApplicationStatus, + ApplicationTalpaStatus, AttachmentRequirement, AttachmentType, BenefitType, @@ -1904,6 +1905,7 @@ class Meta: "ahjo_case_id", "batch", "ahjo_error", + "talpa_status", ] read_only_fields = [ @@ -1927,6 +1929,7 @@ class Meta: "ahjo_case_id", "batch", "ahjo_error", + "talpa_status", ] archived = serializers.BooleanField() @@ -1971,10 +1974,13 @@ def get_latest_ahjo_error(self, obj) -> Union[Dict, None]: ), ) - batch = serializers.SerializerMethodField("get_batch_status") + batch = serializers.SerializerMethodField("get_batch_info") - def get_batch_status(self, obj): - return {"status": getattr(obj.batch, "status", None)} + def get_batch_info(self, obj): + return { + "status": getattr(obj.batch, "status", None), + "decision_date": getattr(obj.batch, "decision_date", None), + } ahjo_case_id = serializers.CharField() application_number = serializers.IntegerField() @@ -1982,7 +1988,12 @@ def get_batch_status(self, obj): status = serializers.ChoiceField( choices=ApplicationStatus.choices, validators=[ApplicantApplicationStatusValidator()], - help_text="Status of the application, visible to the applicant", + help_text="Status of the application", + ) + + talpa_status = serializers.ChoiceField( + choices=ApplicationTalpaStatus.choices, + help_text="Talpa status of the application", ) application_origin = serializers.CharField() diff --git a/backend/benefit/calculator/api/v1/serializers.py b/backend/benefit/calculator/api/v1/serializers.py index d2ff13b2a6..91ff22903a 100644 --- a/backend/benefit/calculator/api/v1/serializers.py +++ b/backend/benefit/calculator/api/v1/serializers.py @@ -357,8 +357,18 @@ class Meta: class CalculationSearchSerializer(serializers.ModelSerializer): class Meta: model = Calculation - fields = ["start_date", "end_date", "handler_details"] - read_only_fields = ["start_date", "end_date", "handler_details"] + fields = [ + "start_date", + "end_date", + "handler_details", + "calculated_benefit_amount", + ] + read_only_fields = [ + "start_date", + "end_date", + "handler_details", + "calculated_benefit_amount", + ] handler_details = UserSerializer( help_text=( diff --git a/frontend/benefit/handler/public/locales/en/common.json b/frontend/benefit/handler/public/locales/en/common.json index b30e17843b..399eeb28cf 100644 --- a/frontend/benefit/handler/public/locales/en/common.json +++ b/frontend/benefit/handler/public/locales/en/common.json @@ -188,6 +188,7 @@ "statusArchive": "Päätös", "ahjoStatus": "Päätös", "talpaStatus": "Maksun tila", + "decisionDate": "Päätöspäivä", "benefitAmount": "Tuen määrä", "statuses": { "cancelled": "Peruutettu", @@ -213,7 +214,13 @@ "rejected": "Kielteinen", "archival": "Myönteinen" }, - "calculationEndDate": "Viim. tukipäivä" + "talpaStatuses": { + "not_sent_to_talpa": "Odottaa maksua", + "rejected_by_talpa": "Virhe maksussa", + "successfully_sent_to_talpa": "Lähetetty maksuun" + }, + "calculationEndDate": "Viim. tukipäivä", + "calculatedBenefitAmount": "Tukisumma" }, "messages": { "empty": { @@ -1144,7 +1151,7 @@ "cancelled": "Hakemus peruttiin", "decisionMakerName": "Päättäjä", "decisionMakerTitle": "Päättäjän titteli", - "decisionDate": "Päätöspäivämäärä", + "decisionDate": "Päätöspäivä", "sectionOfTheLaw": "Pykälä", "p2pTitle": "P2P-tarkastuksen tiedot", "p2pInspector": "Tarkastaja, P2P", diff --git a/frontend/benefit/handler/public/locales/fi/common.json b/frontend/benefit/handler/public/locales/fi/common.json index a557755219..23742966d1 100644 --- a/frontend/benefit/handler/public/locales/fi/common.json +++ b/frontend/benefit/handler/public/locales/fi/common.json @@ -188,6 +188,7 @@ "statusArchive": "Päätös tai tila", "ahjoStatus": "Päätös", "talpaStatus": "Maksun tila", + "decisionDate": "Päätöspäivä", "benefitAmount": "Tuen määrä", "statuses": { "cancelled": "Peruutettu", @@ -213,7 +214,13 @@ "rejected": "Kielteinen", "archival": "Myönteinen" }, - "calculationEndDate": "Viim. tukipäivä" + "talpaStatuses": { + "not_sent_to_talpa": "Odottaa maksua", + "rejected_by_talpa": "Virhe maksussa", + "successfully_sent_to_talpa": "Lähetetty maksuun" + }, + "calculationEndDate": "Viim. tukipäivä", + "calculatedBenefitAmount": "Tukisumma" }, "messages": { "empty": { @@ -1144,7 +1151,7 @@ "cancelled": "Hakemus peruttiin", "decisionMakerName": "Päättäjä", "decisionMakerTitle": "Päättäjän titteli", - "decisionDate": "Päätöspäivämäärä", + "decisionDate": "Päätöspäivä", "sectionOfTheLaw": "Pykälä", "p2pTitle": "P2P-tarkastuksen tiedot", "p2pInspector": "Tarkastaja, P2P", diff --git a/frontend/benefit/handler/public/locales/sv/common.json b/frontend/benefit/handler/public/locales/sv/common.json index b30e17843b..399eeb28cf 100644 --- a/frontend/benefit/handler/public/locales/sv/common.json +++ b/frontend/benefit/handler/public/locales/sv/common.json @@ -188,6 +188,7 @@ "statusArchive": "Päätös", "ahjoStatus": "Päätös", "talpaStatus": "Maksun tila", + "decisionDate": "Päätöspäivä", "benefitAmount": "Tuen määrä", "statuses": { "cancelled": "Peruutettu", @@ -213,7 +214,13 @@ "rejected": "Kielteinen", "archival": "Myönteinen" }, - "calculationEndDate": "Viim. tukipäivä" + "talpaStatuses": { + "not_sent_to_talpa": "Odottaa maksua", + "rejected_by_talpa": "Virhe maksussa", + "successfully_sent_to_talpa": "Lähetetty maksuun" + }, + "calculationEndDate": "Viim. tukipäivä", + "calculatedBenefitAmount": "Tukisumma" }, "messages": { "empty": { @@ -1144,7 +1151,7 @@ "cancelled": "Hakemus peruttiin", "decisionMakerName": "Päättäjä", "decisionMakerTitle": "Päättäjän titteli", - "decisionDate": "Päätöspäivämäärä", + "decisionDate": "Päätöspäivä", "sectionOfTheLaw": "Pykälä", "p2pTitle": "P2P-tarkastuksen tiedot", "p2pInspector": "Tarkastaja, P2P", diff --git a/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx b/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx index 33a6965a83..c9a66b7387 100644 --- a/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx +++ b/frontend/benefit/handler/src/components/applicationList/ApplicationList.tsx @@ -5,7 +5,10 @@ import { } from 'benefit/handler/types/applicationList'; import { getTagStyleForStatus } from 'benefit/handler/utils/applications'; import { APPLICATION_STATUSES } from 'benefit-shared/constants'; -import { ApplicationListItemData } from 'benefit-shared/types/application'; +import { + AhjoError, + ApplicationListItemData, +} from 'benefit-shared/types/application'; import { IconSpeechbubbleText, Table, Tag, Tooltip } from 'hds-react'; import * as React from 'react'; import LoadingSkeleton from 'react-loading-skeleton'; @@ -34,6 +37,7 @@ export interface ApplicationListProps { status: APPLICATION_STATUSES[]; list?: ApplicationListItemData[]; isLoading: boolean; + inPayment?: boolean; } const buildApplicationUrl = ( @@ -61,6 +65,7 @@ const ApplicationList: React.FC = ({ status, list = [], isLoading = true, + inPayment = false, }) => { const { t, translationsBase, getHeader } = useApplicationList(); const theme = useTheme(); @@ -83,6 +88,59 @@ const ApplicationList: React.FC = ({ [isAllStatuses, status] ); + const renderTableActions = React.useCallback( + ( + id: string, + applicationStatus: APPLICATION_STATUSES, + unreadMessagesCount: number, + ahjoError: AhjoError + ): JSX.Element => ( + <$TableActions> + {Number(unreadMessagesCount) > 0 ? ( + <$ActionMessages> + <$Link href={buildApplicationUrl(id, applicationStatus, true)}> + + <$UnreadMessagesCount> + {Number(unreadMessagesCount)} + + + + ) : null} + {ahjoError?.errorFromAhjo && ( + <$ActionErrors + $errorText={t( + 'common:applications.list.errors.ahjoError.buttonText' + )} + > + +
+ + Ahjo, {convertToUIDateAndTimeFormat(ahjoError?.modifiedAt)} + +
+
    + {ahjoError?.errorFromAhjo?.map(({ message }) => ( +
  • {message}
  • + ))} +
+
+ + )} + + ), + [t, theme.colors.coatOfArms] + ); + const columns = React.useMemo(() => { const cols: ApplicationListTableColumns[] = [ { @@ -133,7 +191,6 @@ const ApplicationList: React.FC = ({ isSortable: true, }); } - if ( (!status.includes(APPLICATION_STATUSES.DRAFT) && !status.includes(APPLICATION_STATUSES.ACCEPTED) && @@ -148,7 +205,10 @@ const ApplicationList: React.FC = ({ }); } - if (isVisibleOnlyForStatus.accepted || isVisibleOnlyForStatus.rejected) { + if ( + (!inPayment && isVisibleOnlyForStatus.accepted) || + isVisibleOnlyForStatus.rejected + ) { cols.push({ headerName: getHeader('handledAt'), key: 'handledAt', @@ -167,34 +227,56 @@ const ApplicationList: React.FC = ({ } if ( - isVisibleOnlyForStatus.accepted || - isVisibleOnlyForStatus.rejected || - isVisibleOnlyForStatus.infoRequired || - isAllStatuses + !inPayment && + (isVisibleOnlyForStatus.accepted || + isVisibleOnlyForStatus.rejected || + isVisibleOnlyForStatus.infoRequired || + isVisibleOnlyForStatus.handling || + isAllStatuses) ) { - cols.push({ - transform: ({ - status: applicationStatus, - additionalInformationNeededBy, - }: ApplicationListTableTransforms) => ( - <$TagWrapper $colors={getTagStyleForStatus(applicationStatus)}> - - {t( - `common:applications.list.columns.applicationStatuses.${String( - applicationStatus - )}` - )} - {applicationStatus === APPLICATION_STATUSES.INFO_REQUIRED && - dateForAdditionalInformationNeededBy( - additionalInformationNeededBy + cols.push( + { + transform: ({ + status: applicationStatus, + additionalInformationNeededBy, + }: ApplicationListTableTransforms) => ( + <$TagWrapper $colors={getTagStyleForStatus(applicationStatus)}> + + {t( + `common:applications.list.columns.applicationStatuses.${String( + applicationStatus + )}` )} - - - ), - headerName: getHeader('applicationStatus'), - key: 'status', - isSortable: true, - }); + {applicationStatus === APPLICATION_STATUSES.INFO_REQUIRED && + dateForAdditionalInformationNeededBy( + additionalInformationNeededBy + )} + + + ), + headerName: getHeader('applicationStatus'), + key: 'status', + isSortable: true, + }, + + { + transform: ({ + id, + status: applicationStatus, + unreadMessagesCount, + ahjoError, + }: ApplicationListTableTransforms) => + renderTableActions( + id, + applicationStatus, + unreadMessagesCount, + ahjoError + ), + headerName: getHeader('unreadMessagesCount'), + key: 'unreadMessagesCount', + isSortable: false, + } + ); } if (isVisibleOnlyForStatus.received) { @@ -216,63 +298,45 @@ const ApplicationList: React.FC = ({ }); } - cols.push({ - transform: ({ - unreadMessagesCount, - id, - status: applicationStatus, - ahjoError, - }: ApplicationListTableTransforms) => ( - <$TableActions> - {Number(unreadMessagesCount) > 0 ? ( - <$ActionMessages> - <$Link href={buildApplicationUrl(id, applicationStatus, true)}> - - <$UnreadMessagesCount> - {Number(unreadMessagesCount)} - - - - ) : null} - {ahjoError?.errorFromAhjo && ( - <$ActionErrors - $errorText={t( - 'common:applications.list.errors.ahjoError.buttonText' - )} - > - -
- - Ahjo, {convertToUIDateAndTimeFormat(ahjoError?.modifiedAt)} - -
-
    - {ahjoError?.errorFromAhjo?.map(({ message }) => ( -
  • {message}
  • - ))} -
-
- - )} - - ), - headerName: getHeader('unreadMessagesCount'), - key: 'unreadMessagesCount', - isSortable: false, - }); + if (inPayment) { + cols.push( + { + headerName: getHeader('decisionDate'), + key: 'decisionDate', + isSortable: true, + }, + { + headerName: getHeader('talpaStatus'), + key: 'talpaStatus', + isSortable: true, + transform: ({ talpaStatus }) => + t(`applications.list.columns.talpaStatuses.${String(talpaStatus)}`), + }, + { + headerName: getHeader('calculatedBenefitAmount'), + key: 'calculatedBenefitAmount', + transform: ({ + calculatedBenefitAmount, + }: ApplicationListTableTransforms) => calculatedBenefitAmount, + } + ); + } return cols.filter(Boolean); - }, [t, getHeader, status, theme, isAllStatuses, isVisibleOnlyForStatus]); + }, [ + getHeader, + isVisibleOnlyForStatus.handling, + isVisibleOnlyForStatus.infoRequired, + isVisibleOnlyForStatus.accepted, + isVisibleOnlyForStatus.rejected, + isVisibleOnlyForStatus.draft, + isVisibleOnlyForStatus.received, + status, + isAllStatuses, + inPayment, + t, + renderTableActions, + ]); if (isLoading) { return ( @@ -293,7 +357,6 @@ const ApplicationList: React.FC = ({ } const statusAsString = isAllStatuses ? 'all' : status.join(','); - return ( <$ApplicationList data-testid={`application-list-${statusAsString}`}> {list.length > 0 ? ( diff --git a/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx b/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx index 3a7a5685e5..3f4eb88354 100644 --- a/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx +++ b/frontend/benefit/handler/src/components/applicationList/HandlerIndex.tsx @@ -116,6 +116,11 @@ const HandlerIndex: React.FC = ({ const updateTabToUrl = (tabNumber: APPLICATION_LIST_TABS): void => window.history.pushState({ tab }, '', `/?tab=${tabNumber}`); + const isInPayment = (application: ApplicationListItemData): boolean => + [APPLICATION_STATUSES.ACCEPTED].includes(application.status) && + !isString(application.batch) && + [BATCH_STATUSES.DECIDED_ACCEPTED].includes(application?.batch?.status); + return ( <$BackgroundWrapper backgroundColor={layoutBackgroundColor}> @@ -241,14 +246,8 @@ const HandlerIndex: React.FC = ({ - [APPLICATION_STATUSES.ACCEPTED].includes(app.status) && - !isString(app.batch) && - [BATCH_STATUSES.DECIDED_ACCEPTED].includes( - app?.batch?.status - ) - )} + list={list.filter((app) => isInPayment(app))} + inPayment={!!list.filter((app) => isInPayment(app))} heading={t(`${translationBase}.inPayment`)} status={[APPLICATION_STATUSES.ACCEPTED]} /> diff --git a/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts b/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts index 02e576e759..20f88e0db0 100644 --- a/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts +++ b/frontend/benefit/handler/src/components/applicationList/useApplicationListData.ts @@ -11,6 +11,7 @@ import { convertToUIDateAndTimeFormat, convertToUIDateFormat, } from 'shared/utils/date.utils'; +import { formatFloatToCurrency } from 'shared/utils/string.utils'; interface ApplicationListProps { list: ApplicationListItemData[]; @@ -73,6 +74,13 @@ const useApplicationListData = ( handledByAhjoAutomation: handled_by_ahjo_automation, handledAt: convertToUIDateFormat(handledAt) || '-', ahjoError: camelcaseKeys(ahjo_error, { deep: true }) || null, + decisionDate: convertToUIDateFormat(batch?.decision_date) || '-', + calculatedBenefitAmount: formatFloatToCurrency( + calculation?.calculated_benefit_amount || 0, + 'EUR', + 'fi-FI', + 0 + ), }; }) .filter( diff --git a/frontend/benefit/handler/src/types/applicationList.d.ts b/frontend/benefit/handler/src/types/applicationList.d.ts index 3ae79cee57..2bc017550c 100644 --- a/frontend/benefit/handler/src/types/applicationList.d.ts +++ b/frontend/benefit/handler/src/types/applicationList.d.ts @@ -9,6 +9,7 @@ export interface ApplicationListTableTransforms { status?: APPLICATION_STATUSES; applicationOrigin?: APPLICATION_ORIGINS; ahjoError: AhjoError; + calculatedBenefitAmount?: string; } export interface ApplicationListTableColumns { diff --git a/frontend/benefit/shared/src/types/application.d.ts b/frontend/benefit/shared/src/types/application.d.ts index 44828b1e31..04d030d35b 100644 --- a/frontend/benefit/shared/src/types/application.d.ts +++ b/frontend/benefit/shared/src/types/application.d.ts @@ -589,6 +589,8 @@ export type ApplicationListItemData = { handledByAhjoAutomation?: boolean; alterations?: ApplicationAlterationData[]; ahjoError?: AhjoError; + decisionDate?: string; + calculatedBenefitAmount?: string; }; export type TextProp = 'textFi' | 'textEn' | 'textSv';