Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4388591
HDPI-3764: Add income and expenditure journey steps with bilingual su…
arun-hmcts Mar 22, 2026
d6866fd
HDPI-3764: Add UC routing tests, fix moneyFromElsewhere hint text, an…
arun-hmcts Mar 24, 2026
9ba67c9
HDPI-3764: Add UC conditional routing, prefix/suffix field support, a…
arun-hmcts Mar 24, 2026
34c61f8
HDPI-3764: Remove local OIDC development code from modules/index.ts
arun-hmcts Mar 24, 2026
e1e2c62
HDPI-3764: Merge origin/master to incorporate tar security update
arun-hmcts Mar 24, 2026
3b08446
HDPI-3764: Remove unwanted steps - keep only income-and-expenditure a…
arun-hmcts Mar 24, 2026
42e5d57
HDPI-3764: Fix Universal Credit field mapping to match pcs-api backen…
arun-hmcts Mar 25, 2026
ae01f5f
HDPI-3764: Enable Universal Credit amount and frequency mapping
arun-hmcts Mar 25, 2026
9fa0a5a
HDPI-3764: Add conditional UC routing and upload-docs placeholder step
arun-hmcts Mar 25, 2026
e30d7ed
HDPI-3764: Use req.session.formData directly for consistency with exi…
arun-hmcts Mar 25, 2026
292ff60
HDPI-3764: Use currentStepData (req.body) for stateless routing decis…
arun-hmcts Mar 25, 2026
69693e4
HDPI-3764: Remove placeholder translation files to sync with master
arun-hmcts Mar 25, 2026
96bcf8a
HDPI-3764: Add conditional routing between income-and-expenditure and…
arun-hmcts Mar 25, 2026
493dbca
HDPI-3764: Update income and expenditure page content and add third p…
arun-hmcts Mar 25, 2026
28f2fd5
HDPI-3764: Refactor income and expenditure routing to use helper func…
arun-hmcts Mar 25, 2026
77e39b9
HDPI-3764: Add end-now step to journey flow aligned with master
arun-hmcts Mar 25, 2026
6df27ab
HDPI-3764: Remove dependantChildren from HouseholdCircumstances inter…
arun-hmcts Mar 25, 2026
71be550
HDPI-3764: Revert modules/index.ts to master version removing local O…
arun-hmcts Mar 25, 2026
8cce697
HDPI-3764: Merge origin/master into HDPI-3764-income-expenditure-fron…
arun-hmcts Mar 26, 2026
c08adaf
HDPI-3764: Merge origin/master with HDPI-4000 button macro updates
arun-hmcts Mar 26, 2026
b27c92c
HDPI-3764: Merge origin/master into income-expenditure branch
arun-hmcts Mar 26, 2026
5d07c1c
HDPI-3764: Change frequency values from week/month to WEEKLY/MONTHLY
arun-hmcts Mar 26, 2026
aeefb8e
HDPI-3764: Standardize Yes/No enum format and add type safety for mon…
arun-hmcts Mar 27, 2026
39cc693
HDPI-3764: Merge master and apply Yes/No standardization to tenancy-t…
arun-hmcts Mar 27, 2026
f401783
HDPI-3764: Update tenancy-type-details tests to use Yes/No standardiz…
arun-hmcts Mar 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"caption": "cyResponding to the possession claim",
"pageTitle": "cyIncome and expenses",
"infoParagraph1": "cyDetails about your income and expenses can be used by a judge to reach a decision on your case, and also to decide how much you could afford to repay if you have rent arrears.",
"infoParagraph2": "cyOn the day of the hearing, there may be a duty adviser at the court who can give you advice and represent you for free. They can also use this information to see if there's any additional benefits or support you might be entitled to.",
"infoParagraph3": "cyThe claimant will be able to see your answers.",
"question": "cyDo you want to provide details of your income and expenses?",
"options": {
"yes": "cyYes",
"no": "cyNo"
},
"errors": {
"provideFinanceDetails": "cySelect if you want to provide details of your income and expenses"
}
}
60 changes: 60 additions & 0 deletions src/main/assets/locales/cy/respondToClaim/regularIncome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"caption": "cyRespond to a property possession claim",
"pageTitle": "cyWhat regular income do you receive? (Optional)",
"hintText": "cySelect all that apply. Enter total amount in pounds and pence, for example £148.00 or £148.50. The information you provide must be truthful and accurate.",
"options": {
"incomeFromJobs": "cyIncome from all jobs you do",
"pension": "cyPension – state and private",
"universalCredit": "cyUniversal Credit",
"otherBenefits": "cyOther benefits and credits",
"moneyFromElsewhere": "cyMoney from somewhere else (for example, child maintenance payments or someone in your household gives you money)"
},
"subFields": {
"amount": "cyTotal amount received",
"frequency": "cyReceived every:",
"moneyFromElsewhereDetailsLabel": "cyGive details",
"moneyFromElsewhereDetailsHint": "cyGive details about the other sources of income and how much you usually receive"
},
"frequency": {
"week": "cyWeek",
"month": "cyMonth"
},
"errors": {
"incomeFromJobsAmount": {
"required": "cyEnter the total amount you receive from all jobs you do",
"largeAmount": "cyThe total amount you receive from all jobs you do each week or month must be less than £1 billion"
},
"incomeFromJobsFrequency": {
"required": "cySelect how frequently you receive income from all jobs you do"
},
"pensionAmount": {
"required": "cyEnter the total amount you receive from a state or private pension",
"largeAmount": "cyThe total amount you receive from pension (state and private) each week or month must be less than £1 billion"
},
"pensionFrequency": {
"required": "cySelect how frequently you receive income from a state or private pension"
},
"universalCreditAmount": {
"required": "cyEnter the total amount you receive from Universal Credit",
"largeAmount": "cyThe total amount you receive from Universal Credit each week or month must be less than £1 billion"
},
"universalCreditFrequency": {
"required": "cySelect how frequently you receive Universal Credit"
},
"otherBenefitsAmount": {
"required": "cyEnter the total amount you receive from other benefits and credits",
"largeAmount": "cyThe total amount you receive from other benefits and credit each week or month must be less than £1 billion"
},
"otherBenefitsFrequency": {
"required": "cySelect how frequently you receive income from other benefits and credits"
},
"moneyFromElsewhereDetails": {
"required": "cyGive details about the other sources of income and how much you usually receive"
},
"amount": {
"negative": "cyAmount cannot be negative",
"tooLarge": "cyAmount is too large",
"invalidFormat": "cyEnter amount in pounds and pence, for example 148.00 or 148.50"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"caption": "Responding to the possession claim",
"pageTitle": "Income and expenses",
"infoParagraph1": "Details about your income and expenses can be used by a judge to reach a decision on your case, and also to decide how much you could afford to repay if you have rent arrears.",
"infoParagraph2": "On the day of the hearing, there may be a duty adviser at the court who can give you advice and represent you for free. They can also use this information to see if there's any additional benefits or support you might be entitled to.",
"infoParagraph3": "The claimant will be able to see your answers.",
"question": "Do you want to provide details of your income and expenses?",
"options": {
"yes": "Yes",
"no": "No"
},
"errors": {
"provideFinanceDetails": "Select if you want to provide details of your income and expenses"
}
}
60 changes: 60 additions & 0 deletions src/main/assets/locales/en/respondToClaim/regularIncome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"caption": "Respond to a property possession claim",
"pageTitle": "What regular income do you receive? (Optional)",
"hintText": "Select all that apply. Enter total amount in pounds and pence, for example £148.00 or £148.50. The information you provide must be truthful and accurate.",
"options": {
"incomeFromJobs": "Income from all jobs you do",
"pension": "Pension – state and private",
"universalCredit": "Universal Credit",
"otherBenefits": "Other benefits and credits",
"moneyFromElsewhere": "Money from somewhere else (for example, child maintenance payments or someone in your household gives you money)"
},
"subFields": {
"amount": "Total amount received",
"frequency": "Received every:",
"moneyFromElsewhereDetailsLabel": "Give details",
"moneyFromElsewhereDetailsHint": "Give details about the other sources of income and how much you usually receive"
},
"frequency": {
"week": "Week",
"month": "Month"
},
"errors": {
"incomeFromJobsAmount": {
"required": "Enter the total amount you receive from all jobs you do",
"largeAmount": "The total amount you receive from all jobs you do each week or month must be less than £1 billion"
},
"incomeFromJobsFrequency": {
"required": "Select how frequently you receive income from all jobs you do"
},
"pensionAmount": {
"required": "Enter the total amount you receive from a state or private pension",
"largeAmount": "The total amount you receive from pension (state and private) each week or month must be less than £1 billion"
},
"pensionFrequency": {
"required": "Select how frequently you receive income from a state or private pension"
},
"universalCreditAmount": {
"required": "Enter the total amount you receive from Universal Credit",
"largeAmount": "The total amount you receive from Universal Credit each week or month must be less than £1 billion"
},
"universalCreditFrequency": {
"required": "Select how frequently you receive Universal Credit"
},
"otherBenefitsAmount": {
"required": "Enter the total amount you receive from other benefits and credits",
"largeAmount": "The total amount you receive from other benefits and credit each week or month must be less than £1 billion"
},
"otherBenefitsFrequency": {
"required": "Select how frequently you receive income from other benefits and credits"
},
"moneyFromElsewhereDetails": {
"required": "Give details about the other sources of income and how much you usually receive"
},
"amount": {
"negative": "Amount cannot be negative",
"tooLarge": "Amount is too large",
"invalidFormat": "Enter amount in pounds and pence, for example 148.00 or 148.50"
}
}
}
10 changes: 10 additions & 0 deletions src/main/constants/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Validation constants for income and expenditure forms.
* Shared across all income/expense related steps.
*/

/** Maximum income/expense amount: £1 billion in pence */
export const MAX_INCOME_AMOUNT = 1_000_000_000;

/** Amount format: up to 10 digits, exactly 2 decimal places */
export const AMOUNT_FORMAT_REGEX = /^\d{1,10}\.\d{2}$/;
25 changes: 24 additions & 1 deletion src/main/interfaces/ccdCase.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ export enum CaseState {
SUBMITTED = 'Submitted',
}

export type YesNoValue = 'YES' | 'NO' | null;
export type YesNoValue = 'Yes' | 'No' | null;
export type TenancyTypeCorrectValue = YesNoValue | 'NOT_SURE';
export type ContactPreference = 'EMAIL' | 'POST' | null;

export type YesNoNotSureValue = 'YES' | 'NO' | 'NOT_SURE';

export type FrequencyValue = 'WEEKLY' | 'MONTHLY';
export type PenceAmount = string;

export interface CcdUserCase {
id: string;
state: CaseState;
Expand Down Expand Up @@ -37,6 +40,25 @@ export interface Address {
Country?: string;
}

export interface HouseholdCircumstances {
shareIncomeExpenseDetails?: YesNoValue;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These HouseHoldCirumstances fields should match the backend entity/domain and match the "Yes"/"No" values. I've changed this on my PR already here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved now

incomeFromJobs?: YesNoValue;
incomeFromJobsAmount?: PenceAmount;
incomeFromJobsFrequency?: FrequencyValue;
pension?: YesNoValue;
pensionAmount?: PenceAmount;
pensionFrequency?: FrequencyValue;
universalCredit?: YesNoValue;
universalCreditAmount?: PenceAmount;
universalCreditFrequency?: FrequencyValue;
ucApplicationDate?: string;
otherBenefits?: YesNoValue;
otherBenefitsAmount?: PenceAmount;
otherBenefitsFrequency?: FrequencyValue;
moneyFromElsewhere?: YesNoValue;
moneyFromElsewhereDetails?: string;
}

export interface PossessionClaimResponse {
defendantContactDetails?: {
party?: {
Expand All @@ -61,6 +83,7 @@ export interface PossessionClaimResponse {
dateOfBirth?: string;
landlordRegistered?: YesNoNotSureValue;
landlordLicensed?: YesNoNotSureValue;
householdCircumstances?: HouseholdCircumstances;
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/interfaces/formFieldConfig.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export interface FormFieldConfig {
classes?: string;
attributes?: Record<string, unknown>;
legendClasses?: string;
prefix?: { text: string };
suffix?: { text: string };
// Pre-built component config for Nunjucks template rendering
component?: Record<string, unknown>;
componentType?: ComponentType;
Expand Down
6 changes: 6 additions & 0 deletions src/main/modules/steps/formBuilder/componentBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export function buildComponentConfig(
switch (field.type) {
case 'text': {
component.value = (fieldValue as string) || '';
if (field.prefix) {
component.prefix = field.prefix;
}
if (field.suffix) {
component.suffix = field.suffix;
}
componentType = 'input';
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { PossessionClaimResponse } from '../../../interfaces/ccdCase.interface';
import type { StepDefinition } from '../../../interfaces/stepFormData.interface';
import { createFormStep } from '../../../modules/steps';
import { toYesNoEnum } from '../../utils';
import { buildCcdCaseForPossessionClaimResponse as buildAndSubmitPossessionClaimResponse } from '../../utils/populateResponseToClaimPayloadmap';
import { flowConfig } from '../flow.config';

Expand Down Expand Up @@ -94,7 +95,7 @@ export const step: StepDefinition = createFormStep({
},
},
defendantResponses: {
contactByPhone: telephoneForm.contactByTelephone === 'yes' ? 'YES' : 'NO',
contactByPhone: toYesNoEnum(telephoneForm.contactByTelephone),
},
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { PossessionClaimResponse } from '../../../interfaces/ccdCase.interface';
import type { StepDefinition } from '../../../interfaces/stepFormData.interface';
import { createFormStep } from '../../../modules/steps';
import { toYesNoEnum } from '../../utils';
import { buildCcdCaseForPossessionClaimResponse as buildAndSubmitPossessionClaimResponse } from '../../utils/populateResponseToClaimPayloadmap';
import { flowConfig } from '../flow.config';

Expand Down Expand Up @@ -47,7 +48,7 @@ export const step: StepDefinition = createFormStep({

const possessionClaimResponse: PossessionClaimResponse = {
defendantResponses: {
contactByText: textForm.contactByTextMessage === 'yes' ? 'YES' : 'NO',
contactByText: toYesNoEnum(textForm.contactByTextMessage),
},
};

Expand Down
63 changes: 56 additions & 7 deletions src/main/steps/respond-to-claim/flow.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { type Request } from 'express';
import type { JourneyFlowConfig } from '../../interfaces/stepFlow.interface';
import {
getPreviousPageForArrears,
hasSelectedUniversalCredit,
isDefendantNameKnown,
isFinanceDetailsProvided,
isFromIncomeAndExpenditure,
isNoticeDateProvided,
isNoticeServed,
isRentArrearsClaim,
isTenancyStartDateKnown,
isUniversalCreditSelected,
isWelshProperty,
} from '../utils';

Expand Down Expand Up @@ -50,11 +54,12 @@ export const flowConfig: JourneyFlowConfig = {
'your-circumstances',
'exceptional-hardship',
'income-and-expenditure',
'what-regular-income-do-you-receive',
'regular-income',
'have-you-applied-for-universal-credit',
'priority-debts',
'priority-debt-details',
'what-other-regular-expenses-do-you-have',
'upload-docs',
'end-now',
],
steps: {
Expand Down Expand Up @@ -356,26 +361,70 @@ export const flowConfig: JourneyFlowConfig = {
},
'income-and-expenditure': {
previousStep: 'exceptional-hardship',
defaultNext: 'what-regular-income-do-you-receive',
routes: [
{
condition: async (req: Request): Promise<boolean> => {
return !(await isFinanceDetailsProvided(req));
},
nextStep: 'upload-docs',
},
{
condition: async (req: Request): Promise<boolean> => {
return isFinanceDetailsProvided(req);
},
nextStep: 'regular-income',
},
],
defaultNext: 'regular-income',
},
'what-regular-income-do-you-receive': {
'regular-income': {
previousStep: 'income-and-expenditure',
routes: [
{
condition: async (req: Request): Promise<boolean> => {
return isUniversalCreditSelected(req);
},
nextStep: 'priority-debts',
},
{
condition: async (req: Request): Promise<boolean> => {
return !(await isUniversalCreditSelected(req));
},
nextStep: 'have-you-applied-for-universal-credit',
},
],
defaultNext: 'have-you-applied-for-universal-credit',
},
'have-you-applied-for-universal-credit': {
previousStep: 'what-regular-income-do-you-receive',
previousStep: 'regular-income',
defaultNext: 'priority-debts',
},
'priority-debts': {
previousStep: 'have-you-applied-for-universal-credit',
previousStep: async (req: Request): Promise<string> => {
const selectedUniversalCredit = await hasSelectedUniversalCredit(req);
return selectedUniversalCredit ? 'regular-income' : 'have-you-applied-for-universal-credit';
},
defaultNext: 'priority-debt-details',
},
'priority-debt-details': {
previousStep: 'priority-debts',
defaultNext: 'what-other-regular-expenses-do-you-have',
},
'what-other-regular-expenses-do-you-have': {
previousStep: 'priority-debt-details',
defaultNext: 'upload-docs',
},
'upload-docs': {
previousStep: async (req: Request): Promise<string> => {
const fromIncomeExpenditure = await isFromIncomeAndExpenditure(req);
return fromIncomeExpenditure ? 'income-and-expenditure' : 'what-other-regular-expenses-do-you-have';
},
routes: [
{
condition: async (req: Request): Promise<boolean> => {
return isFromIncomeAndExpenditure(req);
},
nextStep: 'income-and-expenditure',
},
],
defaultNext: 'end-now',
},
},
Expand Down
Loading
Loading