mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
feat: Implement return process questions and validators for Tolino categories
- Added questions and validation logic for the Tolino return process. - Introduced Ton-/Datenträger return process questions and validation. - Created a registry to map product categories to their respective questions and validators. - Developed unit tests for the new return process questions and validators. - Removed deprecated return process questions and validators to streamline the codebase.
This commit is contained in:
@@ -3,6 +3,11 @@ export const ReturnProcessQuestionKey = {
|
||||
ReturnReason: 'return_reason',
|
||||
DeliveredItem: 'delivered_item',
|
||||
ItemDefective: 'item_defective',
|
||||
DisplayCondition: 'display_condition',
|
||||
DevicePower: 'device_power',
|
||||
PackageComplete: 'package_complete',
|
||||
CaseCondition: 'case_condition',
|
||||
UsbPort: 'usb_port',
|
||||
} as const;
|
||||
|
||||
export type ReturnProcessQuestionKey =
|
||||
|
||||
73
libs/oms/data-access/src/lib/questions/book-calendar.spec.ts
Normal file
73
libs/oms/data-access/src/lib/questions/book-calendar.spec.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
validateBookCalendarQuestions,
|
||||
bookCalendarQuestions,
|
||||
} from './book-calendar';
|
||||
import { EligibleForReturnState, ReturnProcessQuestionKey } from '../models';
|
||||
import { ProductCategory } from './constants';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './registry';
|
||||
|
||||
describe('Book Calendar Return Process', () => {
|
||||
describe('validateBookCalendarQuestions', () => {
|
||||
it('should return Eligible when item is ovp and return reason is dislike', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'dislike',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when item is ovp, return reason is wrong_item and delivered item is provided', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: 'someProduct',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Pending when item is ovp, return reason is wrong_item but delivered item is missing', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return Eligible when item is damaged', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible for any invalid answers', () => {
|
||||
const answers = {};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Registry mappings', () => {
|
||||
it('should map BookCalendar category to book calendar questions', () => {
|
||||
expect(CategoryQuestions[ProductCategory.BookCalendar]).toBe(
|
||||
bookCalendarQuestions,
|
||||
);
|
||||
});
|
||||
|
||||
it('should map BookCalendar category to book calendar validator', () => {
|
||||
expect(CategoryQuestionValidators[ProductCategory.BookCalendar]).toBe(
|
||||
validateBookCalendarQuestions,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
72
libs/oms/data-access/src/lib/questions/book-calendar.ts
Normal file
72
libs/oms/data-access/src/lib/questions/book-calendar.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import { ItemConditionValue, ReturnReasonValue } from './constants';
|
||||
import { validateOvpReturn } from './validators';
|
||||
|
||||
/**
|
||||
* Questions for the return process of books and calendars.
|
||||
*/
|
||||
export const bookCalendarQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Neuwertig/Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{ label: 'Beschädigt/Fehldruck', value: ItemConditionValue.Damaged },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ReturnReason,
|
||||
description: 'Warum möchtest du den Artikel zurücksenden?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonValue.Dislike },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DeliveredItem,
|
||||
description: 'Welcher Artikel wurde geliefert?',
|
||||
type: ReturnProcessQuestionType.Product,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the book and calendar return process.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateBookCalendarQuestions(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.OriginalPackaging
|
||||
) {
|
||||
return validateOvpReturn(answers);
|
||||
} else if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.Damaged
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
};
|
||||
}
|
||||
35
libs/oms/data-access/src/lib/questions/constants.ts
Normal file
35
libs/oms/data-access/src/lib/questions/constants.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Constants for product categories used in the return process.
|
||||
*/
|
||||
export const enum ProductCategory {
|
||||
BookCalendar = 'Buch/Kalender',
|
||||
TonDatentraeger = 'Ton-/Datenträger',
|
||||
SpielwarenPuzzle = 'Spielwaren/Puzzle',
|
||||
SonstigesNonbook = 'Sonstiges und Nonbook',
|
||||
ElektronischeGeraete = 'Andere Elektronische Geräte',
|
||||
Tolino = 'Tolino', // Added Tolino category
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants for item condition values.
|
||||
*/
|
||||
export const enum ItemConditionValue {
|
||||
OriginalPackaging = 'ovp',
|
||||
Damaged = 'damaged',
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants for return reason values.
|
||||
*/
|
||||
export const enum ReturnReasonValue {
|
||||
Dislike = 'dislike',
|
||||
WrongItem = 'wrong_item',
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants for item defective values.
|
||||
*/
|
||||
export const enum ItemDefectiveValue {
|
||||
Yes = 'yes',
|
||||
No = 'no',
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import {
|
||||
validateElektronischeGeraeteQuestions,
|
||||
elektronischeGeraeteQuestions,
|
||||
} from './elektronische-geraete';
|
||||
import { EligibleForReturnState, ReturnProcessQuestionKey } from '../models';
|
||||
import { ProductCategory } from './constants';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './registry';
|
||||
|
||||
describe('Elektronische Geräte Return Process', () => {
|
||||
describe('validateElektronischeGeraeteQuestions', () => {
|
||||
it('should return Eligible when item is ovp and return reason is dislike', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'dislike',
|
||||
};
|
||||
const result = validateElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when item is ovp, return reason is wrong_item and delivered item provided', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: 'item123',
|
||||
};
|
||||
const result = validateElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Pending when delivered item is missing for wrong_item reason', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
};
|
||||
const result = validateElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return Eligible when damaged and item is defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'yes',
|
||||
};
|
||||
const result = validateElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible when damaged and item is not defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'no',
|
||||
};
|
||||
const result = validateElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Item not defective',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return NotEligible for invalid answers', () => {
|
||||
const answers = {};
|
||||
const result = validateElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Registry mappings', () => {
|
||||
it('should map ElektronischeGeraete category to elektronische-geraete questions', () => {
|
||||
expect(CategoryQuestions[ProductCategory.ElektronischeGeraete]).toBe(
|
||||
elektronischeGeraeteQuestions,
|
||||
);
|
||||
});
|
||||
|
||||
it('should map ElektronischeGeraete category to elektronische-geraete validator', () => {
|
||||
expect(
|
||||
CategoryQuestionValidators[ProductCategory.ElektronischeGeraete],
|
||||
).toBe(validateElektronischeGeraeteQuestions);
|
||||
});
|
||||
});
|
||||
});
|
||||
106
libs/oms/data-access/src/lib/questions/elektronische-geraete.ts
Normal file
106
libs/oms/data-access/src/lib/questions/elektronische-geraete.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import {
|
||||
ItemConditionValue,
|
||||
ReturnReasonValue,
|
||||
ItemDefectiveValue,
|
||||
} from './constants';
|
||||
import { validateOvpReturn } from './validators';
|
||||
|
||||
/**
|
||||
* Questions for the return process of other electronic devices.
|
||||
*/
|
||||
export const elektronischeGeraeteQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet / Beschädigt',
|
||||
value: ItemConditionValue.Damaged,
|
||||
nextQuestion: ReturnProcessQuestionKey.ItemDefective,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ReturnReason,
|
||||
description: 'Warum möchtest du den Artikel zurückgeben?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonValue.Dislike },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemDefective,
|
||||
description: 'Ist der Artikel defekt?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DeliveredItem,
|
||||
description: 'Welcher Artikel wurde geliefert?',
|
||||
type: ReturnProcessQuestionType.Product,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the return process of other electronic devices.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateElektronischeGeraeteQuestions(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.OriginalPackaging
|
||||
) {
|
||||
return validateOvpReturn(answers);
|
||||
} else if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.Damaged
|
||||
) {
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ItemDefective] === ItemDefectiveValue.Yes
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
} else if (
|
||||
answers[ReturnProcessQuestionKey.ItemDefective] === ItemDefectiveValue.No
|
||||
) {
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Item not defective',
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
};
|
||||
}
|
||||
17
libs/oms/data-access/src/lib/questions/index.ts
Normal file
17
libs/oms/data-access/src/lib/questions/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// Export constants
|
||||
export * from './constants';
|
||||
|
||||
// Export types
|
||||
export * from './types';
|
||||
|
||||
// Export validators
|
||||
export * from './validators';
|
||||
|
||||
// Export category-specific questions and validators
|
||||
export * from './book-calendar';
|
||||
export * from './ton-datentraeger';
|
||||
export * from './nonbook';
|
||||
export * from './elektronische-geraete';
|
||||
|
||||
// Export registry
|
||||
export * from './registry';
|
||||
82
libs/oms/data-access/src/lib/questions/nonbook.spec.ts
Normal file
82
libs/oms/data-access/src/lib/questions/nonbook.spec.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { validateNonbookQuestions, nonbookQuestions } from './nonbook';
|
||||
import { EligibleForReturnState, ReturnProcessQuestionKey } from '../models';
|
||||
import { ProductCategory } from './constants';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './registry';
|
||||
|
||||
describe('Nonbook Return Process', () => {
|
||||
describe('validateNonbookQuestions', () => {
|
||||
it('should return Eligible when item is ovp and return reason is dislike', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'dislike',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when item is ovp, return reason is wrong_item and delivered item is provided', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: 'product',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Pending when delivered item is missing for wrong_item reason', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return Eligible when item is damaged', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible for invalid answers', () => {
|
||||
const answers = {};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Registry mappings', () => {
|
||||
it('should map SpielwarenPuzzle category to nonbook questions', () => {
|
||||
expect(CategoryQuestions[ProductCategory.SpielwarenPuzzle]).toBe(
|
||||
nonbookQuestions,
|
||||
);
|
||||
});
|
||||
|
||||
it('should map SonstigesNonbook category to nonbook questions', () => {
|
||||
expect(CategoryQuestions[ProductCategory.SonstigesNonbook]).toBe(
|
||||
nonbookQuestions,
|
||||
);
|
||||
});
|
||||
|
||||
it('should map SpielwarenPuzzle category to nonbook validator', () => {
|
||||
expect(CategoryQuestionValidators[ProductCategory.SpielwarenPuzzle]).toBe(
|
||||
validateNonbookQuestions,
|
||||
);
|
||||
});
|
||||
|
||||
it('should map SonstigesNonbook category to nonbook validator', () => {
|
||||
expect(CategoryQuestionValidators[ProductCategory.SonstigesNonbook]).toBe(
|
||||
validateNonbookQuestions,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
72
libs/oms/data-access/src/lib/questions/nonbook.ts
Normal file
72
libs/oms/data-access/src/lib/questions/nonbook.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import { ItemConditionValue, ReturnReasonValue } from './constants';
|
||||
import { validateOvpReturn } from './validators';
|
||||
|
||||
/**
|
||||
* Questions for the return process of Spielwaren/Puzzle and other nonbook items.
|
||||
*/
|
||||
export const nonbookQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Neuwertig/Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{ label: 'Beschädigt/Fehldruck', value: ItemConditionValue.Damaged },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ReturnReason,
|
||||
description: 'Warum möchtest du den Artikel zurückgeben?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonValue.Dislike },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DeliveredItem,
|
||||
description: 'Welcher Artikel wurde geliefert?',
|
||||
type: ReturnProcessQuestionType.Product,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the nonbook return process.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateNonbookQuestions(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.OriginalPackaging
|
||||
) {
|
||||
return validateOvpReturn(answers);
|
||||
} else if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.Damaged
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
};
|
||||
}
|
||||
81
libs/oms/data-access/src/lib/questions/registry.spec.ts
Normal file
81
libs/oms/data-access/src/lib/questions/registry.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './registry';
|
||||
import { ProductCategory } from './constants';
|
||||
import {
|
||||
bookCalendarQuestions,
|
||||
validateBookCalendarQuestions,
|
||||
} from './book-calendar';
|
||||
import {
|
||||
tonDatentraegerQuestions,
|
||||
validateTonDatentraegerQuestions,
|
||||
} from './ton-datentraeger';
|
||||
import { nonbookQuestions, validateNonbookQuestions } from './nonbook';
|
||||
import {
|
||||
elektronischeGeraeteQuestions,
|
||||
validateElektronischeGeraeteQuestions,
|
||||
} from './elektronische-geraete';
|
||||
|
||||
describe('Category Registry', () => {
|
||||
describe('CategoryQuestions mappings', () => {
|
||||
it('should map each category to its respective questions array', () => {
|
||||
// Test all category mappings
|
||||
expect(CategoryQuestions[ProductCategory.BookCalendar]).toBe(
|
||||
bookCalendarQuestions,
|
||||
);
|
||||
expect(CategoryQuestions[ProductCategory.TonDatentraeger]).toBe(
|
||||
tonDatentraegerQuestions,
|
||||
);
|
||||
expect(CategoryQuestions[ProductCategory.SpielwarenPuzzle]).toBe(
|
||||
nonbookQuestions,
|
||||
);
|
||||
expect(CategoryQuestions[ProductCategory.SonstigesNonbook]).toBe(
|
||||
nonbookQuestions,
|
||||
);
|
||||
expect(CategoryQuestions[ProductCategory.ElektronischeGeraete]).toBe(
|
||||
elektronischeGeraeteQuestions,
|
||||
);
|
||||
});
|
||||
|
||||
it('should provide the same nonbook questions for both nonbook categories', () => {
|
||||
// Both nonbook categories should point to the same question array
|
||||
expect(CategoryQuestions[ProductCategory.SpielwarenPuzzle]).toBe(
|
||||
CategoryQuestions[ProductCategory.SonstigesNonbook],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CategoryQuestionValidators mappings', () => {
|
||||
it('should map each category to its respective validator function', () => {
|
||||
// Test all validator mappings
|
||||
expect(CategoryQuestionValidators[ProductCategory.BookCalendar]).toBe(
|
||||
validateBookCalendarQuestions,
|
||||
);
|
||||
expect(CategoryQuestionValidators[ProductCategory.TonDatentraeger]).toBe(
|
||||
validateTonDatentraegerQuestions,
|
||||
);
|
||||
expect(CategoryQuestionValidators[ProductCategory.SpielwarenPuzzle]).toBe(
|
||||
validateNonbookQuestions,
|
||||
);
|
||||
expect(CategoryQuestionValidators[ProductCategory.SonstigesNonbook]).toBe(
|
||||
validateNonbookQuestions,
|
||||
);
|
||||
expect(
|
||||
CategoryQuestionValidators[ProductCategory.ElektronischeGeraete],
|
||||
).toBe(validateElektronischeGeraeteQuestions);
|
||||
});
|
||||
|
||||
it('should provide the same validator for both nonbook categories', () => {
|
||||
// Both nonbook categories should point to the same validator function
|
||||
expect(CategoryQuestionValidators[ProductCategory.SpielwarenPuzzle]).toBe(
|
||||
CategoryQuestionValidators[ProductCategory.SonstigesNonbook],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have matching keys in both CategoryQuestions and CategoryQuestionValidators', () => {
|
||||
// Make sure both registries have the same set of keys
|
||||
const questionKeys = Object.keys(CategoryQuestions);
|
||||
const validatorKeys = Object.keys(CategoryQuestionValidators);
|
||||
|
||||
expect(questionKeys.sort()).toEqual(validatorKeys.sort());
|
||||
});
|
||||
});
|
||||
47
libs/oms/data-access/src/lib/questions/registry.ts
Normal file
47
libs/oms/data-access/src/lib/questions/registry.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { ProductCategory } from './constants';
|
||||
import { QuestionValidator } from './types';
|
||||
import { ReturnProcessQuestion } from '../models';
|
||||
import {
|
||||
bookCalendarQuestions,
|
||||
validateBookCalendarQuestions,
|
||||
} from './book-calendar';
|
||||
import {
|
||||
tonDatentraegerQuestions,
|
||||
validateTonDatentraegerQuestions,
|
||||
} from './ton-datentraeger';
|
||||
import { nonbookQuestions, validateNonbookQuestions } from './nonbook';
|
||||
import {
|
||||
elektronischeGeraeteQuestions,
|
||||
validateElektronischeGeraeteQuestions,
|
||||
} from './elektronische-geraete';
|
||||
import { tolinoQuestions, validateTolinoQuestions } from './tolino';
|
||||
|
||||
/**
|
||||
* A mapping of categories to their respective return process questions.
|
||||
*/
|
||||
export const CategoryQuestions: Record<
|
||||
ProductCategory,
|
||||
ReturnProcessQuestion[]
|
||||
> = {
|
||||
[ProductCategory.BookCalendar]: bookCalendarQuestions,
|
||||
[ProductCategory.TonDatentraeger]: tonDatentraegerQuestions,
|
||||
[ProductCategory.SpielwarenPuzzle]: nonbookQuestions,
|
||||
[ProductCategory.SonstigesNonbook]: nonbookQuestions,
|
||||
[ProductCategory.ElektronischeGeraete]: elektronischeGeraeteQuestions,
|
||||
[ProductCategory.Tolino]: tolinoQuestions,
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of categories to their respective validation functions.
|
||||
*/
|
||||
export const CategoryQuestionValidators: Record<
|
||||
ProductCategory,
|
||||
QuestionValidator
|
||||
> = {
|
||||
[ProductCategory.BookCalendar]: validateBookCalendarQuestions,
|
||||
[ProductCategory.TonDatentraeger]: validateTonDatentraegerQuestions,
|
||||
[ProductCategory.SpielwarenPuzzle]: validateNonbookQuestions,
|
||||
[ProductCategory.SonstigesNonbook]: validateNonbookQuestions,
|
||||
[ProductCategory.ElektronischeGeraete]: validateElektronischeGeraeteQuestions,
|
||||
[ProductCategory.Tolino]: validateTolinoQuestions,
|
||||
};
|
||||
61
libs/oms/data-access/src/lib/questions/tolino.spec.ts
Normal file
61
libs/oms/data-access/src/lib/questions/tolino.spec.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { validateTolinoQuestions, tolinoQuestions } from './tolino';
|
||||
import { EligibleForReturnState, ReturnProcessQuestionKey } from '../models';
|
||||
import { ProductCategory } from './constants';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './registry';
|
||||
|
||||
describe('Tolino Return Process', () => {
|
||||
describe('validateTolinoQuestions', () => {
|
||||
it('should return Eligible when item is ovp', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
};
|
||||
const result = validateTolinoQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when damaged and has significant defects', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.DevicePower]: 'yes',
|
||||
};
|
||||
const result = validateTolinoQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible when damaged but no significant defects', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.DevicePower]: 'no',
|
||||
[ReturnProcessQuestionKey.PackageComplete]: 'no',
|
||||
[ReturnProcessQuestionKey.CaseCondition]: 'no',
|
||||
[ReturnProcessQuestionKey.UsbPort]: 'no',
|
||||
};
|
||||
const result = validateTolinoQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'No significant defects found',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return NotEligible for invalid answers', () => {
|
||||
const answers = {};
|
||||
const result = validateTolinoQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Registry mappings', () => {
|
||||
it('should map Tolino category to tolino questions', () => {
|
||||
expect(CategoryQuestions[ProductCategory.Tolino]).toBe(tolinoQuestions);
|
||||
});
|
||||
|
||||
it('should map Tolino category to tolino validator', () => {
|
||||
expect(CategoryQuestionValidators[ProductCategory.Tolino]).toBe(
|
||||
validateTolinoQuestions,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
171
libs/oms/data-access/src/lib/questions/tolino.ts
Normal file
171
libs/oms/data-access/src/lib/questions/tolino.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import {
|
||||
ItemConditionValue,
|
||||
ItemDefectiveValue,
|
||||
ReturnReasonValue,
|
||||
} from './constants';
|
||||
|
||||
/**
|
||||
* Questions for the return process of Tolino devices.
|
||||
*/
|
||||
export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet/Beschädigt',
|
||||
value: ItemConditionValue.Damaged,
|
||||
nextQuestion: ReturnProcessQuestionKey.DevicePower,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DevicePower,
|
||||
description: 'Lässt sich das Gerät einschalten?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.PackageComplete,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.PackageComplete,
|
||||
description: 'Ist die Verpackung vollständig?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.CaseCondition,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.CaseCondition,
|
||||
description: 'Hat das Gehäuse Mängel oder ist zerkratzt?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.DisplayCondition,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.UsbPort,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DisplayCondition,
|
||||
description: 'Hat das Display Mängel oder ist gebrochen?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.UsbPort,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.UsbPort,
|
||||
description: 'Funktioniert die USB Buchse?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ReturnReason,
|
||||
description: 'Warum wird der Artikel zurückgegeben?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Gefällt nicht/Wiederruf',
|
||||
value: ReturnReasonValue.Dislike,
|
||||
},
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the Tolino return process.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateTolinoQuestions(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.OriginalPackaging
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
} else if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.Damaged
|
||||
) {
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.DevicePower] ===
|
||||
ItemDefectiveValue.Yes ||
|
||||
answers[ReturnProcessQuestionKey.PackageComplete] ===
|
||||
ItemDefectiveValue.Yes ||
|
||||
answers[ReturnProcessQuestionKey.CaseCondition] ===
|
||||
ItemDefectiveValue.Yes ||
|
||||
answers[ReturnProcessQuestionKey.UsbPort] === ItemDefectiveValue.Yes
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'No significant defects found',
|
||||
};
|
||||
}
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
validateTonDatentraegerQuestions,
|
||||
tonDatentraegerQuestions,
|
||||
} from './ton-datentraeger';
|
||||
import { EligibleForReturnState, ReturnProcessQuestionKey } from '../models';
|
||||
import { ProductCategory } from './constants';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './registry';
|
||||
|
||||
describe('Ton-/Datenträger Return Process', () => {
|
||||
describe('validateTonDatentraegerQuestions', () => {
|
||||
it('should return Eligible when item is ovp', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when damaged and item is defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'yes',
|
||||
};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible when damaged and item is not defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'no',
|
||||
};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Item not defective',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return NotEligible with invalid answers if keys are missing', () => {
|
||||
const answers = {};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Registry mappings', () => {
|
||||
it('should map TonDatentraeger category to ton-datentraeger questions', () => {
|
||||
expect(CategoryQuestions[ProductCategory.TonDatentraeger]).toBe(
|
||||
tonDatentraegerQuestions,
|
||||
);
|
||||
});
|
||||
|
||||
it('should map TonDatentraeger category to ton-datentraeger validator', () => {
|
||||
expect(CategoryQuestionValidators[ProductCategory.TonDatentraeger]).toBe(
|
||||
validateTonDatentraegerQuestions,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
89
libs/oms/data-access/src/lib/questions/ton-datentraeger.ts
Normal file
89
libs/oms/data-access/src/lib/questions/ton-datentraeger.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import { ItemConditionValue, ItemDefectiveValue } from './constants';
|
||||
|
||||
/**
|
||||
* Questions for the return process of Ton-/Datenträger (audio/data carriers).
|
||||
*/
|
||||
export const tonDatentraegerQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Versiegelt/Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet',
|
||||
value: ItemConditionValue.Damaged,
|
||||
nextQuestion: ReturnProcessQuestionKey.ItemDefective,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemDefective,
|
||||
description: 'Ist der Artikel Defekt?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the Ton-/Datenträger return process.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateTonDatentraegerQuestions(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
// Check if the item is in original condition
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.OriginalPackaging
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
// Check if the item is damaged
|
||||
else if (
|
||||
answers[ReturnProcessQuestionKey.ItemCondition] ===
|
||||
ItemConditionValue.Damaged
|
||||
) {
|
||||
// Check if the item is defective
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ItemDefective] === ItemDefectiveValue.Yes
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
// Check if the item is not defective
|
||||
else if (
|
||||
answers[ReturnProcessQuestionKey.ItemDefective] === ItemDefectiveValue.No
|
||||
) {
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Item not defective',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
};
|
||||
}
|
||||
9
libs/oms/data-access/src/lib/questions/types.ts
Normal file
9
libs/oms/data-access/src/lib/questions/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ReturnProcessAnswers, EligibleForReturn } from '../models';
|
||||
|
||||
/**
|
||||
* Type definition for question validator functions.
|
||||
* These functions take answer records and return eligibility results.
|
||||
*/
|
||||
export type QuestionValidator = (
|
||||
answers: ReturnProcessAnswers,
|
||||
) => EligibleForReturn;
|
||||
54
libs/oms/data-access/src/lib/questions/validators.spec.ts
Normal file
54
libs/oms/data-access/src/lib/questions/validators.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { validateOvpReturn } from './validators';
|
||||
import { EligibleForReturnState, ReturnProcessQuestionKey } from '../models';
|
||||
|
||||
describe('Shared Validators', () => {
|
||||
describe('validateOvpReturn', () => {
|
||||
it('should return Eligible when return reason is dislike', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'dislike',
|
||||
};
|
||||
const result = validateOvpReturn(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when return reason is wrong_item and delivered item is provided', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: 'someProduct',
|
||||
};
|
||||
const result = validateOvpReturn(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Pending when return reason is wrong_item but delivered item is missing', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
};
|
||||
const result = validateOvpReturn(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return NotEligible for invalid or unspecified return reasons', () => {
|
||||
// Test with empty answers
|
||||
const emptyAnswers = {};
|
||||
const emptyResult = validateOvpReturn(emptyAnswers);
|
||||
expect(emptyResult).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
|
||||
// Test with invalid return reason
|
||||
const invalidAnswers = {
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'not_a_valid_reason',
|
||||
};
|
||||
const invalidResult = validateOvpReturn(invalidAnswers);
|
||||
expect(invalidResult).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
36
libs/oms/data-access/src/lib/questions/validators.ts
Normal file
36
libs/oms/data-access/src/lib/questions/validators.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestionKey,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import { ReturnReasonValue, ItemDefectiveValue } from './constants';
|
||||
|
||||
/**
|
||||
* Helper function to validate return eligibility for items in original condition.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateOvpReturn(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
if (
|
||||
answers[ReturnProcessQuestionKey.ReturnReason] === ReturnReasonValue.Dislike
|
||||
) {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
} else if (
|
||||
answers[ReturnProcessQuestionKey.ReturnReason] ===
|
||||
ReturnReasonValue.WrongItem
|
||||
) {
|
||||
return answers[ReturnProcessQuestionKey.DeliveredItem]
|
||||
? { state: EligibleForReturnState.Eligible }
|
||||
: {
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
};
|
||||
}
|
||||
return {
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
};
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
import {
|
||||
bookCalendarQuestions,
|
||||
nonbookQuestions,
|
||||
tonDatentraegerQuestions,
|
||||
andereElektronischeGeraete,
|
||||
validateBookCalendarQuestions,
|
||||
validateNonbookQuestions,
|
||||
validateTonDatentraegerQuestions,
|
||||
validateAndereElektronischeGeraeteQuestions,
|
||||
CategoryQuestions,
|
||||
CategoryQuestionValidators,
|
||||
} from './return-process-questions';
|
||||
import { EligibleForReturnState, ReturnProcessQuestionKey } from './models';
|
||||
|
||||
describe('ReturnProcessQuestions Validators', () => {
|
||||
describe('validateBookCalendarQuestions', () => {
|
||||
it('should return Eligible when item is ovp and return reason is dislike', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'dislike',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when item is ovp, return reason is wrong_item and delivered item is provided', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: 'someProduct',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Pending when item is ovp, return reason is wrong_item but delivered item is missing', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return Eligible when item is damaged', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible for any invalid answers', () => {
|
||||
const answers = {};
|
||||
const result = validateBookCalendarQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateTonDatentraegerQuestions', () => {
|
||||
it('should return Eligible when item is ovp', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when damaged and item is defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'yes',
|
||||
};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible when damaged and item is not defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'no',
|
||||
};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Item not defective',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return NotEligible with invalid answers if keys are missing', () => {
|
||||
const answers = {};
|
||||
const result = validateTonDatentraegerQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateNonbookQuestions', () => {
|
||||
it('should return Eligible when item is ovp and return reason is dislike', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'dislike',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when item is ovp, return reason is wrong_item and delivered item is provided', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: 'product',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Pending when delivered item is missing for wrong_item reason', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return Eligible when item is damaged', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible for invalid answers', () => {
|
||||
const answers = {};
|
||||
const result = validateNonbookQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateAndereElektronischeGeraeteQuestions', () => {
|
||||
it('should return Eligible when item is ovp and return reason is dislike', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'dislike',
|
||||
};
|
||||
const result = validateAndereElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Eligible when item is ovp, return reason is wrong_item and delivered item provided', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: 'item123',
|
||||
};
|
||||
const result = validateAndereElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return Pending when delivered item is missing for wrong_item reason', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'ovp',
|
||||
[ReturnProcessQuestionKey.ReturnReason]: 'wrong_item',
|
||||
};
|
||||
const result = validateAndereElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Missing delivered item',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return Eligible when damaged and item is defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'yes',
|
||||
};
|
||||
const result = validateAndereElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({ state: EligibleForReturnState.Eligible });
|
||||
});
|
||||
|
||||
it('should return NotEligible when damaged and item is not defective', () => {
|
||||
const answers = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: 'damaged',
|
||||
[ReturnProcessQuestionKey.ItemDefective]: 'no',
|
||||
};
|
||||
const result = validateAndereElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Item not defective',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return NotEligible for invalid answers', () => {
|
||||
const answers = {};
|
||||
const result = validateAndereElektronischeGeraeteQuestions(answers);
|
||||
expect(result).toEqual({
|
||||
state: EligibleForReturnState.NotEligible,
|
||||
reason: 'Invalid answers',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('CategoryQuestions and Validators mapping', () => {
|
||||
it('should map each category to its questions', () => {
|
||||
expect(CategoryQuestions['Buch/Kalender']).toBe(bookCalendarQuestions);
|
||||
expect(CategoryQuestions['Ton-/Datenträger']).toBe(tonDatentraegerQuestions);
|
||||
expect(CategoryQuestions['Spielwaren/Puzzle']).toBe(nonbookQuestions);
|
||||
expect(CategoryQuestions['Sonstiges und Nonbook']).toBe(nonbookQuestions);
|
||||
expect(CategoryQuestions['Andere Elektronische Geräte']).toBe(andereElektronischeGeraete);
|
||||
});
|
||||
|
||||
it('should map each category to its validator', () => {
|
||||
expect(CategoryQuestionValidators['Buch/Kalender']).toBe(validateBookCalendarQuestions);
|
||||
expect(CategoryQuestionValidators['Ton-/Datenträger']).toBe(validateTonDatentraegerQuestions);
|
||||
expect(CategoryQuestionValidators['Spielwaren/Puzzle']).toBe(validateNonbookQuestions);
|
||||
expect(CategoryQuestionValidators['Sonstiges und Nonbook']).toBe(validateNonbookQuestions);
|
||||
expect(CategoryQuestionValidators['Andere Elektronische Geräte']).toBe(
|
||||
validateAndereElektronischeGeraeteQuestions,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,299 +0,0 @@
|
||||
import {
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
} from './models';
|
||||
|
||||
/**
|
||||
* Questions for the return process of books and calendars.
|
||||
*/
|
||||
export const bookCalendarQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Neuwertig/Originalverpackt',
|
||||
value: 'ovp',
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{ label: 'Beschädigt/Fehldruck', value: 'damaged' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ReturnReason,
|
||||
description: 'Warum möchtest du den Artikel zurücksenden?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: 'dislike' },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: 'wrong_item',
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DeliveredItem,
|
||||
description: 'Welcher Artikel wurde geliefert?',
|
||||
type: ReturnProcessQuestionType.Product,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Helper function to validate return eligibility for items in original condition.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
function validateOvpReturn(answers: ReturnProcessAnswers): EligibleForReturn {
|
||||
if (answers[ReturnProcessQuestionKey.ReturnReason] === 'dislike') {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
} else if (answers[ReturnProcessQuestionKey.ReturnReason] === 'wrong_item') {
|
||||
return answers[ReturnProcessQuestionKey.DeliveredItem]
|
||||
? { state: EligibleForReturnState.Eligible }
|
||||
: { state: EligibleForReturnState.Pending, reason: 'Missing delivered item' };
|
||||
}
|
||||
return { state: EligibleForReturnState.NotEligible, reason: 'Invalid answers' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the answers for the book and calendar return process.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateBookCalendarQuestions(answers: ReturnProcessAnswers): EligibleForReturn {
|
||||
if (answers[ReturnProcessQuestionKey.ItemCondition] === 'ovp') {
|
||||
return validateOvpReturn(answers);
|
||||
} else if (answers[ReturnProcessQuestionKey.ItemCondition] === 'damaged') {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
return { state: EligibleForReturnState.NotEligible, reason: 'Invalid answers' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Questions for the return process of Ton-/Datenträger (audio/data carriers).
|
||||
*/
|
||||
export const tonDatentraegerQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Versiegelt/Originalverpackt',
|
||||
value: 'ovp',
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet',
|
||||
value: 'damaged',
|
||||
nextQuestion: ReturnProcessQuestionKey.ItemDefective,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemDefective,
|
||||
description: 'Ist der Artikel Defekt?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: 'yes',
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: 'no',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the Ton-/Datenträger return process.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateTonDatentraegerQuestions(answers: ReturnProcessAnswers): EligibleForReturn {
|
||||
// Check if the item is in original condition
|
||||
if (answers[ReturnProcessQuestionKey.ItemCondition] === 'ovp') {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
// Check if the item is damaged
|
||||
else if (answers[ReturnProcessQuestionKey.ItemCondition] === 'damaged') {
|
||||
// Check if the item is defective
|
||||
if (answers[ReturnProcessQuestionKey.ItemDefective] === 'yes') {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
// Check if the item is not defective
|
||||
else if (answers[ReturnProcessQuestionKey.ItemDefective] === 'no') {
|
||||
return { state: EligibleForReturnState.NotEligible, reason: 'Item not defective' };
|
||||
}
|
||||
}
|
||||
|
||||
return { state: EligibleForReturnState.NotEligible, reason: 'Invalid answers' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Questions for the return process of Spielwaren/Puzzle and other nonbook items.
|
||||
*/
|
||||
export const nonbookQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Neuwertig/Originalverpackt',
|
||||
value: 'ovp',
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{ label: 'Beschädigt/Fehldruck', value: 'damaged' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ReturnReason,
|
||||
description: 'Warum möchtest du den Artikel zurückgeben?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: 'dislike' },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: 'wrong_item',
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DeliveredItem,
|
||||
description: 'Welcher Artikel wurde geliefert?',
|
||||
type: ReturnProcessQuestionType.Product,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the nonbook return process.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateNonbookQuestions(answers: ReturnProcessAnswers): EligibleForReturn {
|
||||
if (answers[ReturnProcessQuestionKey.ItemCondition] === 'ovp') {
|
||||
return validateOvpReturn(answers);
|
||||
} else if (answers[ReturnProcessQuestionKey.ItemCondition] === 'damaged') {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
return { state: EligibleForReturnState.NotEligible, reason: 'Invalid answers' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Questions for the return process of other electronic devices.
|
||||
*/
|
||||
export const andereElektronischeGeraete: ReturnProcessQuestion[] = [
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemCondition,
|
||||
description: 'Wie ist der Artikelzustand?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Originalverpackt',
|
||||
value: 'ovp',
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet / Beschädigt',
|
||||
value: 'damaged',
|
||||
nextQuestion: ReturnProcessQuestionKey.ItemDefective,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ReturnReason,
|
||||
description: 'Warum möchtest du den Artikel zurückgeben?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: 'dislike' },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: 'wrong_item',
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.ItemDefective,
|
||||
description: 'Ist der Artikel defekt?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: 'yes',
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: 'no',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: ReturnProcessQuestionKey.DeliveredItem,
|
||||
description: 'Welcher Artikel wurde geliefert?',
|
||||
type: ReturnProcessQuestionType.Product,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the return process of other electronic devices.
|
||||
* @param answers - A record of answers keyed by question keys.
|
||||
* @returns The eligibility state for the return process.
|
||||
*/
|
||||
export function validateAndereElektronischeGeraeteQuestions(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
if (answers[ReturnProcessQuestionKey.ItemCondition] === 'ovp') {
|
||||
return validateOvpReturn(answers);
|
||||
} else if (answers[ReturnProcessQuestionKey.ItemCondition] === 'damaged') {
|
||||
if (answers[ReturnProcessQuestionKey.ItemDefective] === 'yes') {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
} else if (answers[ReturnProcessQuestionKey.ItemDefective] === 'no') {
|
||||
return { state: EligibleForReturnState.NotEligible, reason: 'Item not defective' };
|
||||
}
|
||||
}
|
||||
return { state: EligibleForReturnState.NotEligible, reason: 'Invalid answers' };
|
||||
}
|
||||
|
||||
/**
|
||||
* A mapping of categories to their respective return process questions.
|
||||
*/
|
||||
export const CategoryQuestions = {
|
||||
'Buch/Kalender': bookCalendarQuestions,
|
||||
'Ton-/Datenträger': tonDatentraegerQuestions,
|
||||
'Spielwaren/Puzzle': nonbookQuestions,
|
||||
'Sonstiges und Nonbook': nonbookQuestions,
|
||||
'Andere Elektronische Geräte': andereElektronischeGeraete,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* A type alias for representing the keys of the CategoryQuestions object.
|
||||
*
|
||||
* This type is used to ensure that the keys of the CategoryQuestions object
|
||||
* and the references to them remain in sync. Any updates to the keys defined
|
||||
* in CategoryQuestions will automatically be reflected in CategoryQuestionsKey,
|
||||
* thereby helping to maintain consistency across the codebase.
|
||||
*/
|
||||
type CategoryQuestionsKey = keyof typeof CategoryQuestions;
|
||||
|
||||
/**
|
||||
* A mapping of categories to their respective validation functions.
|
||||
*/
|
||||
export const CategoryQuestionValidators: Record<
|
||||
CategoryQuestionsKey,
|
||||
(answers: ReturnProcessAnswers) => EligibleForReturn
|
||||
> = {
|
||||
'Buch/Kalender': validateBookCalendarQuestions,
|
||||
'Ton-/Datenträger': validateTonDatentraegerQuestions,
|
||||
'Spielwaren/Puzzle': validateNonbookQuestions,
|
||||
'Sonstiges und Nonbook': validateNonbookQuestions,
|
||||
'Andere Elektronische Geräte': validateAndereElektronischeGeraeteQuestions,
|
||||
};
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionType,
|
||||
} from './models';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './return-process-questions';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './questions';
|
||||
import { KeyValue } from '@angular/common';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -17,11 +17,14 @@ export class ReturnProcessService {
|
||||
});
|
||||
}
|
||||
|
||||
returnProcessQuestions(process: ReturnProcess): ReturnProcessQuestion[] | undefined {
|
||||
const category = process.productCategory || process.receiptItem.features?.['category'];
|
||||
returnProcessQuestions(
|
||||
process: ReturnProcess,
|
||||
): ReturnProcessQuestion[] | undefined {
|
||||
const category =
|
||||
process.productCategory || process.receiptItem.features?.['category'];
|
||||
|
||||
if (category) {
|
||||
return CategoryQuestions[category];
|
||||
return CategoryQuestions[category as keyof typeof CategoryQuestions];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -29,7 +32,8 @@ export class ReturnProcessService {
|
||||
returnProcessQuestionValidator(
|
||||
process: ReturnProcess,
|
||||
): ((answers: Record<string, unknown>) => EligibleForReturn) | undefined {
|
||||
const category = process.productCategory || process.receiptItem.features?.['category'];
|
||||
const category =
|
||||
process.productCategory || process.receiptItem.features?.['category'];
|
||||
|
||||
if (category) {
|
||||
return CategoryQuestionValidators[category];
|
||||
@@ -37,7 +41,9 @@ export class ReturnProcessService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
activeReturnProcessQuestions(process: ReturnProcess): ReturnProcessQuestion[] | undefined {
|
||||
activeReturnProcessQuestions(
|
||||
process: ReturnProcess,
|
||||
): ReturnProcessQuestion[] | undefined {
|
||||
const questions = this.returnProcessQuestions(process);
|
||||
|
||||
if (!questions) {
|
||||
@@ -60,10 +66,14 @@ export class ReturnProcessService {
|
||||
result.push(question);
|
||||
|
||||
if (question.type === ReturnProcessQuestionType.Select) {
|
||||
const option = question.options.find((o) => o.value === process.answers[question.key]);
|
||||
const option = question.options.find(
|
||||
(o) => o.value === process.answers[question.key],
|
||||
);
|
||||
questionKey = option?.nextQuestion;
|
||||
} else if (question.type === ReturnProcessQuestionType.Product) {
|
||||
questionKey = process.answers[question.key] ? question.nextQuestion : undefined;
|
||||
questionKey = process.answers[question.key]
|
||||
? question.nextQuestion
|
||||
: undefined;
|
||||
} else {
|
||||
console.error('Unknown question type', question);
|
||||
break;
|
||||
@@ -92,7 +102,10 @@ export class ReturnProcessService {
|
||||
}
|
||||
|
||||
const visited = new Set<string>();
|
||||
function computeLongestPath(questions: ReturnProcessQuestion[], startKey: string) {
|
||||
function computeLongestPath(
|
||||
questions: ReturnProcessQuestion[],
|
||||
startKey: string,
|
||||
) {
|
||||
if (visited.has(startKey)) return 0;
|
||||
|
||||
visited.add(startKey);
|
||||
@@ -101,16 +114,30 @@ export class ReturnProcessService {
|
||||
if (!key) return 0;
|
||||
const question = questions.find((q) => q.key === key);
|
||||
if (!question) return 0;
|
||||
if (question.type === ReturnProcessQuestionType.Select && question.options) {
|
||||
if (
|
||||
question.type === ReturnProcessQuestionType.Select &&
|
||||
question.options
|
||||
) {
|
||||
if (returnProcess.answers[question.key]) {
|
||||
const chosen = question.options.find(
|
||||
(o) => o.value === returnProcess.answers[question.key],
|
||||
);
|
||||
return 1 + findDepth(chosen?.nextQuestion);
|
||||
}
|
||||
return 1 + Math.max(...question.options.map((o) => findDepth(o.nextQuestion)));
|
||||
} else if (question.type === ReturnProcessQuestionType.Product && question.nextQuestion) {
|
||||
return 1 + (returnProcess.answers[question.key] ? findDepth(question.nextQuestion) : 0);
|
||||
return (
|
||||
1 +
|
||||
Math.max(...question.options.map((o) => findDepth(o.nextQuestion)))
|
||||
);
|
||||
} else if (
|
||||
question.type === ReturnProcessQuestionType.Product &&
|
||||
question.nextQuestion
|
||||
) {
|
||||
return (
|
||||
1 +
|
||||
(returnProcess.answers[question.key]
|
||||
? findDepth(question.nextQuestion)
|
||||
: 0)
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
@@ -141,15 +168,23 @@ export class ReturnProcessService {
|
||||
return { state: EligibleForReturnState.Pending };
|
||||
}
|
||||
|
||||
const everyQuestionAnswered = questions.every((q) => q.key in returnProcess.answers);
|
||||
const everyQuestionAnswered = questions.every(
|
||||
(q) => q.key in returnProcess.answers,
|
||||
);
|
||||
if (!everyQuestionAnswered) {
|
||||
return { state: EligibleForReturnState.Pending, reason: 'Not all questions answered' };
|
||||
return {
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Not all questions answered',
|
||||
};
|
||||
}
|
||||
|
||||
const validator = this.returnProcessQuestionValidator(returnProcess);
|
||||
|
||||
if (!validator) {
|
||||
return { state: EligibleForReturnState.Pending, reason: 'No validator found' };
|
||||
return {
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'No validator found',
|
||||
};
|
||||
}
|
||||
|
||||
return validator(returnProcess.answers);
|
||||
|
||||
Reference in New Issue
Block a user