NTP-39292 | Added limit on PTP amount (#1118)

This commit is contained in:
Aishwarya Srivastava
2025-04-01 19:58:49 +05:30
committed by GitHub
parent 31b7f6fc61
commit e369714128
12 changed files with 111 additions and 10 deletions

View File

@@ -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"

View File

@@ -1 +1 @@
249
250

View File

@@ -1 +1 @@
2.18.6
2.18.7

View File

@@ -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",

View File

@@ -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);
});
};

View File

@@ -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<ITextInput> = (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<ITextInput> = (props) => {
</Text>
<Controller
control={props.control}
rules={{ validate: (data) => validateInput(data, question.metadata.validators) }}
rules={{
validate: (data) =>
validateInput(
data,
question.metadata.validators,
() => validateInputValue(data, foreclosureAmount),
question?.tag
),
}}
render={({ field: { onChange, onBlur, value } }) => (
<RNTextInput
maskType={question.metadata.maskType || null}

View File

@@ -13,3 +13,5 @@ export const NUDGE_BOTTOM_SHEET_DEFAULT_STATE = {
showNudgeBottomSheet: false,
suspiciousFeedbackMessage: '',
};
export const AMOUNT_PROMISED = 'AMOUNT_PROMISED';

View File

@@ -1,4 +1,6 @@
import { isFunction } from '@components/utlis/commonFunctions';
import { QuestionV1 } from '../../../types/template.types';
import { AMOUNT_PROMISED } from '../constants';
export function isQuestionMandatory(question: QuestionV1): boolean {
return !!question.metadata?.validators?.['required']?.value;
@@ -6,7 +8,15 @@ export function isQuestionMandatory(question: QuestionV1): boolean {
const DEFAULT_ERROR_MESSAGE = 'This is a mandatory question';
export function validateInput(data: { answer: any; type: string }, allRules: any): boolean {
export function validateInput(
data: { answer: any; type: string },
allRules: any,
validateFieldValue?: () => {
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',
}

View File

@@ -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)})`,
};
};

View File

@@ -0,0 +1,24 @@
import { createSlice } from '@reduxjs/toolkit';
interface IFeedbackFormState {
foreclosureAmount: Record<string, number>;
}
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;

View File

@@ -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<ICaseDetails> = (props) => {
lan: loanAccountNumber,
currentTask: caseDetail?.currentTask?.taskType,
});
dispatch(getForeclosureAmount(caseId, loanAccountNumber, dateFormat(new Date(), ISO_DATE_FORMAT)));
}, []);
useEffect(() => {

View File

@@ -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 = {