mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1996: fix(crm): consolidate customer feature selection logic
fix(crm): consolidate customer feature selection logic Introduce centralized `getEnabledCustomerFeature` helper to standardize feature selection across components. Replaces inconsistent filtering approaches with unified logic that prioritizes 'd-account' and 'd-no-account' features. Changes: - Add `getEnabledCustomerFeature` helper with unit tests - Add `CustomerFeatureKey` and `CustomerFeatureGroup` enums - Update customer-order-details-header component - Update pickup-shelf-details-header component - Update customer-result-list components - Update order-details-main-view component Ref: #5432
This commit is contained in:
committed by
Lorenz Hilpert
parent
7a04b828c3
commit
f175b5d2af
@@ -0,0 +1,297 @@
|
||||
import { KeyValueOfStringAndString } from '@isa/common/data-access';
|
||||
import { getEnabledCustomerFeature } from './get-enabled-customer-feature.helper';
|
||||
import { CustomerFeatureKey, CustomerFeatureGroup } from '../schemas';
|
||||
|
||||
describe('getEnabledCustomerFeature', () => {
|
||||
it('should return undefined when no features are provided', () => {
|
||||
// Arrange
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined when no features are enabled', () => {
|
||||
// Arrange
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
{
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'test',
|
||||
enabled: false,
|
||||
description: 'test description',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'test',
|
||||
enabled: false,
|
||||
description: 'test description',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return the only enabled feature', () => {
|
||||
// Arrange
|
||||
const enabledFeature: KeyValueOfStringAndString = {
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: 'B2B feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
};
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
enabledFeature,
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'test',
|
||||
enabled: false,
|
||||
description: 'Staff feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(enabledFeature);
|
||||
});
|
||||
|
||||
it('should prefer d-account when multiple features are enabled', () => {
|
||||
// Arrange
|
||||
const dAccountFeature: KeyValueOfStringAndString = {
|
||||
key: CustomerFeatureKey.DAccount,
|
||||
value: 'account-value',
|
||||
enabled: true,
|
||||
description: 'D-Account feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
};
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
{
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'b2b-value',
|
||||
enabled: true,
|
||||
description: 'B2B feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
dAccountFeature,
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'staff-value',
|
||||
enabled: true,
|
||||
description: 'Staff feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(dAccountFeature);
|
||||
});
|
||||
|
||||
it('should prefer d-no-account when multiple features are enabled and d-account is not present', () => {
|
||||
// Arrange
|
||||
const dNoAccountFeature: KeyValueOfStringAndString = {
|
||||
key: CustomerFeatureKey.DNoAccount,
|
||||
value: 'no-account-value',
|
||||
enabled: true,
|
||||
description: 'D-No-Account feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
};
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
{
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'b2b-value',
|
||||
enabled: true,
|
||||
description: 'B2B feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
dNoAccountFeature,
|
||||
{
|
||||
key: CustomerFeatureKey.Webshop,
|
||||
value: 'webshop-value',
|
||||
enabled: true,
|
||||
description: 'Webshop feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(dNoAccountFeature);
|
||||
});
|
||||
|
||||
it('should return first enabled feature when multiple are enabled but neither d-account nor d-no-account are present', () => {
|
||||
// Arrange
|
||||
const firstEnabledFeature: KeyValueOfStringAndString = {
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'b2b-value',
|
||||
enabled: true,
|
||||
description: 'B2B feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
};
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
firstEnabledFeature,
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'staff-value',
|
||||
enabled: true,
|
||||
description: 'Staff feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
{
|
||||
key: CustomerFeatureKey.Webshop,
|
||||
value: 'webshop-value',
|
||||
enabled: true,
|
||||
description: 'Webshop feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(firstEnabledFeature);
|
||||
});
|
||||
|
||||
it('should handle null or undefined customerFeatures gracefully', () => {
|
||||
// Act & Assert
|
||||
expect(getEnabledCustomerFeature(null as any)).toBeUndefined();
|
||||
expect(getEnabledCustomerFeature(undefined as any)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined when enabled features have no description', () => {
|
||||
// Arrange
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
{
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: undefined,
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: '',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should only consider features with both enabled flag and description', () => {
|
||||
// Arrange
|
||||
const validFeature: KeyValueOfStringAndString = {
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: 'Valid B2B feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
};
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: undefined,
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
validFeature,
|
||||
{
|
||||
key: CustomerFeatureKey.Webshop,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: '',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(validFeature);
|
||||
});
|
||||
|
||||
it('should return undefined when enabled features have wrong group', () => {
|
||||
// Arrange
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
{
|
||||
key: CustomerFeatureKey.B2B,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: 'B2B feature',
|
||||
group: CustomerFeatureGroup.CustomerType,
|
||||
},
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: 'Staff feature',
|
||||
group: 'wrong-group',
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should only consider features with d-customertype group', () => {
|
||||
// Arrange
|
||||
const validFeature: KeyValueOfStringAndString = {
|
||||
key: CustomerFeatureKey.DAccount,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: 'Valid D-Account feature',
|
||||
group: CustomerFeatureGroup.DCustomerType,
|
||||
};
|
||||
const customerFeatures: KeyValueOfStringAndString[] = [
|
||||
{
|
||||
key: CustomerFeatureKey.Staff,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: 'Staff feature',
|
||||
group: CustomerFeatureGroup.CustomerType,
|
||||
},
|
||||
validFeature,
|
||||
{
|
||||
key: CustomerFeatureKey.Webshop,
|
||||
value: 'test-value',
|
||||
enabled: true,
|
||||
description: 'Webshop feature',
|
||||
group: 'other-group',
|
||||
},
|
||||
];
|
||||
|
||||
// Act
|
||||
const result = getEnabledCustomerFeature(customerFeatures);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(validFeature);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { KeyValueOfStringAndString } from '@isa/common/data-access';
|
||||
import { CustomerFeatureKey, CustomerFeatureGroup } from '../schemas';
|
||||
|
||||
/**
|
||||
* Retrieves the first enabled customer feature that meets all validation criteria.
|
||||
*
|
||||
* A feature is considered valid if it satisfies ALL of the following conditions:
|
||||
* - `enabled` property must be `true`
|
||||
* - `description` property must exist and be non-empty
|
||||
* - `group` property must be 'd-customertype'
|
||||
*
|
||||
* When multiple valid features are found, the function prioritizes features in the following order:
|
||||
* 1. Features with key 'd-account' (highest priority)
|
||||
* 2. Features with key 'd-no-account' (second priority)
|
||||
* 3. The first valid feature in the array (fallback)
|
||||
*
|
||||
* @param customerFeatures - Array of customer feature objects to filter and evaluate
|
||||
* @returns The first enabled feature matching all criteria, or `undefined` if none are found
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const features = [
|
||||
* { key: 'b2b', enabled: true, description: 'B2B', group: 'd-customertype' },
|
||||
* { key: 'd-account', enabled: true, description: 'Account', group: 'd-customertype' }
|
||||
* ];
|
||||
* const result = getEnabledCustomerFeature(features);
|
||||
* // Returns the 'd-account' feature (prioritized over 'b2b')
|
||||
* ```
|
||||
*/
|
||||
export const getEnabledCustomerFeature = (
|
||||
customerFeatures: KeyValueOfStringAndString[],
|
||||
): KeyValueOfStringAndString | undefined => {
|
||||
const enabledFeatures = customerFeatures?.filter(
|
||||
(f) =>
|
||||
f?.enabled &&
|
||||
!!f?.description &&
|
||||
f?.group === CustomerFeatureGroup.DCustomerType,
|
||||
);
|
||||
|
||||
// If multiple features are enabled, prefer 'd-account' or 'd-no-account'
|
||||
if (enabledFeatures?.length > 1) {
|
||||
const preferredFeature = enabledFeatures.find(
|
||||
(f) =>
|
||||
f.key === CustomerFeatureKey.DAccount ||
|
||||
f.key === CustomerFeatureKey.DNoAccount,
|
||||
);
|
||||
// If a preferred feature is found, return it; otherwise fall back to the first enabled feature
|
||||
return preferredFeature ?? enabledFeatures[0];
|
||||
}
|
||||
|
||||
// Return the first enabled feature if only one is enabled
|
||||
return enabledFeatures?.[0];
|
||||
};
|
||||
@@ -6,3 +6,4 @@ export {
|
||||
} from './deduplicate-addressees.helper';
|
||||
export * from './get-customer-name.component';
|
||||
export * from './get-primary-bonus-card.helper';
|
||||
export * from './get-enabled-customer-feature.helper';
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// Customer feature group literals
|
||||
export const CustomerFeatureGroup = {
|
||||
DCustomerType: 'd-customertype',
|
||||
CustomerType: 'customertype',
|
||||
} as const;
|
||||
|
||||
export const CustomerFeatureGroupSchema = z
|
||||
.enum([CustomerFeatureGroup.DCustomerType, CustomerFeatureGroup.CustomerType])
|
||||
.describe('Customer feature group');
|
||||
|
||||
export type CustomerFeatureGroupType = z.infer<
|
||||
typeof CustomerFeatureGroupSchema
|
||||
>;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// Customer feature key literals
|
||||
export const CustomerFeatureKey = {
|
||||
DAccount: 'd-account',
|
||||
DNoAccount: 'd-no-account',
|
||||
B2B: 'b2b',
|
||||
Staff: 'staff',
|
||||
BookshelfId: 'bookshelfId',
|
||||
P4MAccountId: 'p4mAccountId',
|
||||
P4MUser: 'p4mUser',
|
||||
Webshop: 'webshop',
|
||||
Guest: 'guest',
|
||||
} as const;
|
||||
|
||||
export const CustomerFeatureKeySchema = z
|
||||
.enum([
|
||||
CustomerFeatureKey.DAccount,
|
||||
CustomerFeatureKey.DNoAccount,
|
||||
CustomerFeatureKey.B2B,
|
||||
CustomerFeatureKey.Staff,
|
||||
CustomerFeatureKey.BookshelfId,
|
||||
CustomerFeatureKey.P4MAccountId,
|
||||
CustomerFeatureKey.P4MUser,
|
||||
CustomerFeatureKey.Webshop,
|
||||
CustomerFeatureKey.Guest,
|
||||
])
|
||||
.describe('Customer feature key');
|
||||
|
||||
export type CustomerFeatureKeyType = z.infer<typeof CustomerFeatureKeySchema>;
|
||||
@@ -3,6 +3,8 @@ export * from './attribute.schema';
|
||||
export * from './bonus-card.schema';
|
||||
export * from './branch.schema';
|
||||
export * from './customer.schema';
|
||||
export * from './customer-feature-keys.schema';
|
||||
export * from './customer-feature-groups.schema';
|
||||
export * from './fetch-customer-cards.schema';
|
||||
export * from './fetch-customer-shipping-addresses.schema';
|
||||
export * from './fetch-customer.schema';
|
||||
|
||||
Reference in New Issue
Block a user