From e3697141284431f42545a983e1a8b1aa0a58db37 Mon Sep 17 00:00:00 2001 From: Aishwarya Srivastava Date: Tue, 1 Apr 2025 19:58:49 +0530 Subject: [PATCH] NTP-39292 | Added limit on PTP amount (#1118) --- android/app/build.gradle | 4 ++-- buildFlavor/field/buildNumber.txt | 2 +- buildFlavor/field/buildVersion.txt | 2 +- package.json | 4 ++-- src/action/feedbackActions.ts | 24 +++++++++++++++++++ src/components/form/components/TextInput.tsx | 18 ++++++++++++-- src/components/form/constants.ts | 2 ++ .../form/services/validation.service.ts | 21 +++++++++++++++- src/components/form/utils.ts | 14 +++++++++++ src/reducer/feedbackFormSlice.ts | 24 +++++++++++++++++++ .../caseDetails/CollectionCaseDetail.tsx | 4 +++- src/store/store.ts | 2 ++ 12 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 src/components/form/utils.ts create mode 100644 src/reducer/feedbackFormSlice.ts diff --git a/android/app/build.gradle b/android/app/build.gradle index 7f4a1723..84518593 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -113,8 +113,8 @@ def jscFlavor = 'org.webkit:android-jsc:+' def enableHermes = project.ext.react.get("enableHermes", false); -def VERSION_CODE = 249 -def VERSION_NAME = "2.18.6" +def VERSION_CODE = 250 +def VERSION_NAME = "2.18.7" android { namespace "com.avapp" diff --git a/buildFlavor/field/buildNumber.txt b/buildFlavor/field/buildNumber.txt index cb16690c..8a32cf78 100644 --- a/buildFlavor/field/buildNumber.txt +++ b/buildFlavor/field/buildNumber.txt @@ -1 +1 @@ -249 \ No newline at end of file +250 \ No newline at end of file diff --git a/buildFlavor/field/buildVersion.txt b/buildFlavor/field/buildVersion.txt index ad947d67..394c48dd 100644 --- a/buildFlavor/field/buildVersion.txt +++ b/buildFlavor/field/buildVersion.txt @@ -1 +1 @@ -2.18.6 \ No newline at end of file +2.18.7 \ No newline at end of file diff --git a/package.json b/package.json index 55e3baad..ce474575 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "AV_APP", - "version": "2.18.6", - "buildNumber": "249", + "version": "2.18.7", + "buildNumber": "250", "private": true, "scripts": { "android:dev": "yarn move:dev && react-native run-android", diff --git a/src/action/feedbackActions.ts b/src/action/feedbackActions.ts index c28ba539..52c8da14 100644 --- a/src/action/feedbackActions.ts +++ b/src/action/feedbackActions.ts @@ -2,6 +2,7 @@ import { AppDispatch } from '@store'; import axiosInstance, { ApiKeys, getApiUrl } from '../components/utlis/apiHelper'; import { logError } from '../components/utlis/errorUtils'; import { setTopFeedbacks, setTopFeedbacksLoading } from '@reducers/topFeedbacksSlice'; +import { setForeclosureAmount } from '@reducers/feedbackFormSlice'; interface IPastFeedbacksPayload { loan_account_number: string; @@ -97,3 +98,26 @@ export const getTopFeedbacks = (caseId: string) => (dispatch: AppDispatch) => { }) .finally(() => dispatch(setTopFeedbacksLoading({ caseId, isLoading: false }))); }; + +export const getForeclosureAmount = + (caseId: string, loanAccountNumber: string, preclosureDate: string) => + (dispatch: AppDispatch) => { + const url = getApiUrl( + ApiKeys.GET_FORECLOSURE_AMOUNT, + { loanAccountNumber }, + { preclosureDate } + ); + return axiosInstance + .get(url) + .then((response) => { + dispatch( + setForeclosureAmount({ + caseId, + foreclosureAmount: response?.data, + }) + ); + }) + .catch((err) => { + logError(err); + }); + }; diff --git a/src/components/form/components/TextInput.tsx b/src/components/form/components/TextInput.tsx index 4dd1a0dc..b3024fde 100644 --- a/src/components/form/components/TextInput.tsx +++ b/src/components/form/components/TextInput.tsx @@ -12,6 +12,7 @@ import { addClickstreamEvent } from '../../../services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants'; import { isQuestionMandatory, validateInput } from '../services/validation.service'; import { CaseAllocationType } from '../../../screens/allCases/interface'; +import { validateInputValue } from '../utils'; interface ITextInput { questionType: string; @@ -26,7 +27,12 @@ interface ITextInput { const TextInput: React.FC = (props) => { const { questionId, error, sectionId, widgetId, caseId, questionType } = props; - const template = useAppSelector((state) => state?.case?.templateData[CaseAllocationType.COLLECTION_CASE]); + const template = useAppSelector( + (state) => state?.case?.templateData[CaseAllocationType.COLLECTION_CASE] + ); + const foreclosureAmount = useAppSelector( + (state) => state?.feedbackForm?.foreclosureAmount?.[caseId] + ); const question = template.questions[questionId as keyof typeof template.questions]; if (!question) { return null; @@ -69,7 +75,15 @@ const TextInput: React.FC = (props) => { validateInput(data, question.metadata.validators) }} + rules={{ + validate: (data) => + validateInput( + data, + question.metadata.validators, + () => validateInputValue(data, foreclosureAmount), + question?.tag + ), + }} render={({ field: { onChange, onBlur, value } }) => ( { + showValidationError: boolean; + errMessage: string; + }, + questionTag?: string, +): boolean { let result = true; const currentDate = new Date(); const endOfDayDate = new Date( @@ -62,6 +72,14 @@ export function validateInput(data: { answer: any; type: string }, allRules: any result = rule.message; break; } + } else if (ruleName === Validators.MAX_AMOUNT && rule.value) { + if (questionTag === AMOUNT_PROMISED && isFunction(validateFieldValue)) { + const { showValidationError, errMessage } = validateFieldValue(); + if (showValidationError) { + result = errMessage || rule.message; + break; + } + } } } } @@ -74,4 +92,5 @@ export enum Validators { MIN_TODAY = 'minToday', MAX_DATE = 'maxDate', PHONE_NUMBER = 'phoneNumber', + MAX_AMOUNT = 'maxAmount', } diff --git a/src/components/form/utils.ts b/src/components/form/utils.ts new file mode 100644 index 00000000..238a9d6a --- /dev/null +++ b/src/components/form/utils.ts @@ -0,0 +1,14 @@ +import { formatAmount } from '@rn-ui-lib/utils/amount'; + +export const validateInputValue = ( + data: { answer: any; type: string }, + foreclosureAmount: number +) => { + if (!foreclosureAmount) { + return { showValidationError: false, errMessage: '' }; + } + return { + showValidationError: data?.answer > foreclosureAmount, + errMessage: `PTP cannot exceed foreclosure (${formatAmount(foreclosureAmount)})`, + }; +}; diff --git a/src/reducer/feedbackFormSlice.ts b/src/reducer/feedbackFormSlice.ts new file mode 100644 index 00000000..f5844bde --- /dev/null +++ b/src/reducer/feedbackFormSlice.ts @@ -0,0 +1,24 @@ +import { createSlice } from '@reduxjs/toolkit'; + +interface IFeedbackFormState { + foreclosureAmount: Record; +} + +const initialState: IFeedbackFormState = { + foreclosureAmount: {}, +}; + +const FeedbackFormSlice = createSlice({ + name: 'feedbackForm', + initialState, + reducers: { + setForeclosureAmount: (state, action) => { + const { caseId, foreclosureAmount } = action.payload; + state.foreclosureAmount[caseId] = foreclosureAmount?.totalAmount; + }, + }, +}); + +export const { setForeclosureAmount } = FeedbackFormSlice.actions; + +export default FeedbackFormSlice.reducer; diff --git a/src/screens/caseDetails/CollectionCaseDetail.tsx b/src/screens/caseDetails/CollectionCaseDetail.tsx index c7686072..99b696df 100644 --- a/src/screens/caseDetails/CollectionCaseDetail.tsx +++ b/src/screens/caseDetails/CollectionCaseDetail.tsx @@ -24,9 +24,10 @@ import { getSkipTracingAddress, getUngroupedAddress } from '@actions/addressGeol import EscalationsSection from '@screens/escalations/EscalationsSection'; import TopFeedbacks from './feedback/TopFeedbacks'; import { CaseStatuses } from '@screens/allCases/interface'; -import { BUSINESS_DATE_FORMAT, ISO_DATE_FORMAT } from '@rn-ui-lib/utils/dates'; +import { BUSINESS_DATE_FORMAT, dateFormat, ISO_DATE_FORMAT } from '@rn-ui-lib/utils/dates'; import dayjs from 'dayjs'; import CaseDetailPausedNudge from './CaseDetailPausedNudge'; +import { getForeclosureAmount } from '@actions/feedbackActions'; interface ICaseDetails { route: { @@ -91,6 +92,7 @@ const CollectionCaseDetails: React.FC = (props) => { lan: loanAccountNumber, currentTask: caseDetail?.currentTask?.taskType, }); + dispatch(getForeclosureAmount(caseId, loanAccountNumber, dateFormat(new Date(), ISO_DATE_FORMAT))); }, []); useEffect(() => { diff --git a/src/store/store.ts b/src/store/store.ts index c3c4e7b8..3bb3b1ea 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -38,6 +38,7 @@ import escalationSlice from '@reducers/escalationSlice'; import postOperationalHourRestrictionsSlice from '@reducers/postOperationalHourRestrictionsSlice'; import skipTracingAddressesSlice from '@reducers/skipTracingAddressesSlice'; import trainingMaterialSlice from '@reducers/trainingMaterialSlice'; +import feedbackFormSlice from '@reducers/feedbackFormSlice'; const rootReducer = combineReducers({ case: caseReducer, @@ -76,6 +77,7 @@ const rootReducer = combineReducers({ escalationSlice: escalationSlice, postOperationalHourRestrictionsSlice: postOperationalHourRestrictionsSlice, trainingMaterial: trainingMaterialSlice, + feedbackForm: feedbackFormSlice, }); const persistConfig = {