Skip to content

Commit 1297531

Browse files
committed
fix: add cross-field validation for registration account fields
- prevent using internal order and profit center at the same time - require project when both internal order and profit center are empty - add localized validation messages for fi/en/sv - add tests for new registration account validation rules Refs: LINK-2475
1 parent 7baf444 commit 1297531

6 files changed

Lines changed: 73 additions & 14 deletions

File tree

src/domain/app/i18n/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,8 @@ export enum VALIDATION_MESSAGE_KEYS {
3939
URL = 'form.validation.string.url',
4040
ZIP = 'form.validation.string.zip',
4141
}
42+
43+
export enum REGISTRATION_ACCOUNT_VALIDATION_MESSAGE_KEYS {
44+
INTERNAL_ORDER_OR_PROFIT_CENTER = 'form.validation.registrationAccount.internalOrderOrProfitCenter',
45+
PROJECT_REQUIRED = 'form.validation.registrationAccount.projectRequired',
46+
}

src/domain/app/i18n/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,10 @@
11851185
"offersRequired": "At least 1 price information must be added",
11861186
"priceInvalid": "Maximum of two decimal places is allowed",
11871187
"priceGroupsRequired": "At least 1 customer group must be added",
1188+
"registrationAccount": {
1189+
"internalOrderOrProfitCenter": "Provide either internal order or profit center, not both.",
1190+
"projectRequired": "Project is required when neither internal order nor profit center is provided."
1191+
},
11881192
"soMeLinkDuplicate": "SoMe links cannot be the same",
11891193
"string": {
11901194
"charsLeft": "{{count}} character left",

src/domain/app/i18n/fi.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,10 @@
11651165
"offersRequired": "Vähintään 1 hintatieto vaaditaan",
11661166
"priceInvalid": "Enintään kaksi desimaalia on sallittu",
11671167
"priceGroupsRequired": "Vähintään 1 asiakasryhmä vaaditaan",
1168+
"registrationAccount": {
1169+
"internalOrderOrProfitCenter": "Anna joko sisäinen tilaus tai tulosyksikkö, ei molempia.",
1170+
"projectRequired": "Projekti on pakollinen, jos sisäistä tilausta tai tulosyksikköä ei ole annettu."
1171+
},
11681172
"soMeLinkDuplicate": "SoMe-linkit eivät voi olla samoja",
11691173
"string": {
11701174
"charsLeft": "{{count}} merkki jäljellä",

src/domain/app/i18n/sv.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,10 @@
11851185
"offersRequired": "Minst 1 prisinformation måste läggas till",
11861186
"priceInvalid": "Högst två decimaler är tillåtna",
11871187
"priceGroupsRequired": "Minst 1 kundgrupp måste läggas till",
1188+
"registrationAccount": {
1189+
"internalOrderOrProfitCenter": "Ange antingen intern order eller resultatenhet, inte båda.",
1190+
"projectRequired": "Projekt krävs när varken intern order eller resultatenhet har angetts."
1191+
},
11881192
"soMeLinkDuplicate": "SoMe-länkar kan inte vara desamma",
11891193
"string": {
11901194
"charsLeft": "{{count}} tecken kvar",

src/domain/registration/__tests__/validation.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('registrationSchema', () => {
3636
mainLedgerAccount: '3503',
3737
operationArea: '',
3838
profitCenter: '',
39-
project: '',
39+
project: 'project',
4040
};
4141
const validRegistrationValues: RegistrationFormFields = {
4242
...REGISTRATION_INITIAL_VALUES,
@@ -219,11 +219,13 @@ describe('registrationSchema', () => {
219219
[{ balanceProfitCenter: mockString(11) }],
220220
[{ companyCode: '' }],
221221
[{ companyCode: mockString(5) }],
222+
[{ internalOrder: 'internal order', profitCenter: 'profit center' }],
222223
[{ internalOrder: mockString(11) }],
223224
[{ mainLedgerAccount: '' }],
224225
[{ mainLedgerAccount: mockString(7) }],
225226
[{ operationArea: mockString(7) }],
226227
[{ profitCenter: mockString(8) }],
228+
[{ internalOrder: '', profitCenter: '', project: '' }],
227229
[{ project: mockString(17) }],
228230
])('should return false if account is invalid', async (accountFields) => {
229231
expect(

src/domain/registration/validation.ts

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import {
1111
isValidTime,
1212
transformNumber,
1313
} from '../../utils/validationUtils';
14-
import { VALIDATION_MESSAGE_KEYS } from '../app/i18n/constants';
14+
import {
15+
REGISTRATION_ACCOUNT_VALIDATION_MESSAGE_KEYS,
16+
VALIDATION_MESSAGE_KEYS,
17+
} from '../app/i18n/constants';
1518
import { ACCOUNT_TEXT_FIELD_MAX_LENGTH } from '../organization/constants';
1619
import { PriceGroupOption } from '../priceGroup/types';
1720
import {
@@ -62,6 +65,37 @@ const priceGroupsSchema = (
6265
: schema;
6366
};
6467

68+
const hasNonEmptyValue = (value?: string | null): boolean =>
69+
Boolean(value?.trim());
70+
71+
// Prevent submitting both internal order and profit center at the same time.
72+
const internalOrderOrProfitCenterTest = {
73+
name: 'internalOrderOrProfitCenter',
74+
message:
75+
REGISTRATION_ACCOUNT_VALIDATION_MESSAGE_KEYS.INTERNAL_ORDER_OR_PROFIT_CENTER,
76+
test(this: Yup.TestContext) {
77+
const { internalOrder, profitCenter } = this.parent;
78+
return !(hasNonEmptyValue(internalOrder) && hasNonEmptyValue(profitCenter));
79+
},
80+
};
81+
82+
// If neither internal order nor profit center is given, project must be provided.
83+
const projectRequiredWhenNoInternalOrderOrProfitCenterTest = {
84+
name: 'projectRequiredWhenNoInternalOrderOrProfitCenter',
85+
message: REGISTRATION_ACCOUNT_VALIDATION_MESSAGE_KEYS.PROJECT_REQUIRED,
86+
test(this: Yup.TestContext) {
87+
const { internalOrder, profitCenter, project } = this.parent;
88+
return (
89+
hasNonEmptyValue(internalOrder) ||
90+
hasNonEmptyValue(profitCenter) ||
91+
hasNonEmptyValue(project)
92+
);
93+
},
94+
};
95+
96+
// Registration account business rules:
97+
// 1) internal order and profit center are mutually exclusive
98+
// 2) project is required when both internal order and profit center are empty
6599
const registrationAccountSchema = Yup.object().shape({
66100
[REGISTRATION_ACCOUNT_FIELDS.ACCOUNT]: Yup.string().required(
67101
VALIDATION_MESSAGE_KEYS.STRING_REQUIRED
@@ -88,18 +122,24 @@ const registrationAccountSchema = Yup.object().shape({
88122
],
89123
createStringMaxErrorMessage
90124
),
91-
[REGISTRATION_ACCOUNT_FIELDS.INTERNAL_ORDER]: Yup.string().max(
92-
ACCOUNT_TEXT_FIELD_MAX_LENGTH[REGISTRATION_ACCOUNT_FIELDS.INTERNAL_ORDER],
93-
createStringMaxErrorMessage
94-
),
95-
[REGISTRATION_ACCOUNT_FIELDS.PROFIT_CENTER]: Yup.string().max(
96-
ACCOUNT_TEXT_FIELD_MAX_LENGTH[REGISTRATION_ACCOUNT_FIELDS.PROFIT_CENTER],
97-
createStringMaxErrorMessage
98-
),
99-
[REGISTRATION_ACCOUNT_FIELDS.PROJECT]: Yup.string().max(
100-
ACCOUNT_TEXT_FIELD_MAX_LENGTH[REGISTRATION_ACCOUNT_FIELDS.PROJECT],
101-
createStringMaxErrorMessage
102-
),
125+
[REGISTRATION_ACCOUNT_FIELDS.INTERNAL_ORDER]: Yup.string()
126+
.max(
127+
ACCOUNT_TEXT_FIELD_MAX_LENGTH[REGISTRATION_ACCOUNT_FIELDS.INTERNAL_ORDER],
128+
createStringMaxErrorMessage
129+
)
130+
.test(internalOrderOrProfitCenterTest),
131+
[REGISTRATION_ACCOUNT_FIELDS.PROFIT_CENTER]: Yup.string()
132+
.max(
133+
ACCOUNT_TEXT_FIELD_MAX_LENGTH[REGISTRATION_ACCOUNT_FIELDS.PROFIT_CENTER],
134+
createStringMaxErrorMessage
135+
)
136+
.test(internalOrderOrProfitCenterTest),
137+
[REGISTRATION_ACCOUNT_FIELDS.PROJECT]: Yup.string()
138+
.max(
139+
ACCOUNT_TEXT_FIELD_MAX_LENGTH[REGISTRATION_ACCOUNT_FIELDS.PROJECT],
140+
createStringMaxErrorMessage
141+
)
142+
.test(projectRequiredWhenNoInternalOrderOrProfitCenterTest),
103143
[REGISTRATION_ACCOUNT_FIELDS.OPERATION_AREA]: Yup.string().max(
104144
ACCOUNT_TEXT_FIELD_MAX_LENGTH[REGISTRATION_ACCOUNT_FIELDS.OPERATION_AREA],
105145
createStringMaxErrorMessage

0 commit comments

Comments
 (0)