mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
refactor: remove validation functions and tests for return processes
- Deleted validation functions for electronic devices, nonbook items, and ton/datentraeger. - Removed associated test files for these validations. - Updated question definitions to use new constants for item conditions and return reasons. - Refactored return process service to utilize schema validation instead of custom validators. - Adjusted HTML templates to reflect changes in eligibility state handling.
This commit is contained in:
@@ -12,7 +12,7 @@ import {
|
||||
CheckboxAppearance,
|
||||
} from '@isa/ui/input-controls';
|
||||
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||
import { Component, importProvidersFrom } from '@angular/core';
|
||||
import { importProvidersFrom } from '@angular/core';
|
||||
|
||||
interface ChecklistStoryProps {
|
||||
values: string[];
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export const EligibleForReturnState = {
|
||||
NotEligible: 'not-eligible',
|
||||
Eligible: 'eligible',
|
||||
Pending: 'pending',
|
||||
} as const;
|
||||
|
||||
export type EligibleForReturnState =
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,16 +1,20 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import { ItemConditionValue, ReturnReasonValue } from './constants';
|
||||
import { validateOvpReturn } from './validators';
|
||||
import { ItemConditionAnswer, ReturnReasonAnswer } from './constants';
|
||||
|
||||
/**
|
||||
* Questions for the return process of books and calendars.
|
||||
*
|
||||
* This array defines the flow of questions presented to users when processing
|
||||
* book or calendar returns. The questions follow a branching logic based on
|
||||
* the user's answers, guiding them through the appropriate return process.
|
||||
*
|
||||
* The flow typically starts with checking the item condition, then asks about
|
||||
* the return reason, and may request additional information depending on the
|
||||
* reason selected.
|
||||
*/
|
||||
export const bookCalendarQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
@@ -20,10 +24,10 @@ export const bookCalendarQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Neuwertig/Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
value: ItemConditionAnswer.OVP,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{ label: 'Beschädigt/Fehldruck', value: ItemConditionValue.Damaged },
|
||||
{ label: 'Beschädigt/Fehldruck', value: ItemConditionAnswer.Damaged },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -31,10 +35,10 @@ export const bookCalendarQuestions: ReturnProcessQuestion[] = [
|
||||
description: 'Warum möchtest du den Artikel zurücksenden?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonValue.Dislike },
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonAnswer.Dislike },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
value: ReturnReasonAnswer.WrongItem,
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
@@ -45,28 +49,3 @@ export const bookCalendarQuestions: ReturnProcessQuestion[] = [
|
||||
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',
|
||||
};
|
||||
}
|
||||
|
||||
146
libs/oms/data-access/src/lib/questions/constants.spec.ts
Normal file
146
libs/oms/data-access/src/lib/questions/constants.spec.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
ItemConditionAnswer,
|
||||
ReturnReasonAnswer,
|
||||
YesNoAnswer,
|
||||
PackageIncompleteAnswer,
|
||||
ReturnProcessQuestionSchema,
|
||||
ProductCategory
|
||||
} from './constants';
|
||||
import { ReturnProcessQuestionKey } from '../models';
|
||||
|
||||
describe('Constants', () => {
|
||||
describe('Return Process Answers', () => {
|
||||
it('should define ItemConditionAnswer values', () => {
|
||||
expect(ItemConditionAnswer.OVP).toBe('ovp');
|
||||
expect(ItemConditionAnswer.Damaged).toBe('damaged');
|
||||
});
|
||||
|
||||
it('should define ReturnReasonAnswer values', () => {
|
||||
expect(ReturnReasonAnswer.Dislike).toBe('dislike');
|
||||
expect(ReturnReasonAnswer.WrongItem).toBe('wrong_item');
|
||||
});
|
||||
|
||||
it('should define YesNoAnswer values', () => {
|
||||
expect(YesNoAnswer.Yes).toBe('yes');
|
||||
expect(YesNoAnswer.No).toBe('no');
|
||||
});
|
||||
|
||||
it('should define PackageIncompleteAnswer values', () => {
|
||||
expect(PackageIncompleteAnswer.OVP).toBe('ovp');
|
||||
expect(PackageIncompleteAnswer.ChargingCable).toBe('charging_cable');
|
||||
expect(PackageIncompleteAnswer.QuickStartGuide).toBe('quick_start_guide');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProductCategory', () => {
|
||||
it('should define all product categories', () => {
|
||||
expect(ProductCategory.BookCalendar).toBe('Buch/Kalender');
|
||||
expect(ProductCategory.TonDatentraeger).toBe('Ton-/Datenträger');
|
||||
expect(ProductCategory.SpielwarenPuzzle).toBe('Spielwaren/Puzzle');
|
||||
expect(ProductCategory.SonstigesNonbook).toBe('Sonstiges und Nonbook');
|
||||
expect(ProductCategory.ElektronischeGeraete).toBe('Andere Elektronische Geräte');
|
||||
expect(ProductCategory.Tolino).toBe('Tolino');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReturnProcessQuestionSchema', () => {
|
||||
it('should validate ItemCondition schema', () => {
|
||||
const schema = ReturnProcessQuestionSchema[ReturnProcessQuestionKey.ItemCondition];
|
||||
|
||||
// Valid values
|
||||
expect(schema.safeParse(ItemConditionAnswer.OVP).success).toBe(true);
|
||||
expect(schema.safeParse(ItemConditionAnswer.Damaged).success).toBe(true);
|
||||
|
||||
// Invalid values
|
||||
expect(schema.safeParse('invalid').success).toBe(false);
|
||||
expect(schema.safeParse('').success).toBe(false);
|
||||
expect(schema.safeParse(null).success).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate ReturnReason schema', () => {
|
||||
const schema = ReturnProcessQuestionSchema[ReturnProcessQuestionKey.ReturnReason];
|
||||
|
||||
// Valid values
|
||||
expect(schema.safeParse(ReturnReasonAnswer.Dislike).success).toBe(true);
|
||||
expect(schema.safeParse(ReturnReasonAnswer.WrongItem).success).toBe(true);
|
||||
|
||||
// Invalid values
|
||||
expect(schema.safeParse('invalid').success).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate YesNo schemas', () => {
|
||||
const schemas = [
|
||||
ReturnProcessQuestionSchema[ReturnProcessQuestionKey.ItemDefective],
|
||||
ReturnProcessQuestionSchema[ReturnProcessQuestionKey.DisplayCondition],
|
||||
ReturnProcessQuestionSchema[ReturnProcessQuestionKey.DevicePower],
|
||||
ReturnProcessQuestionSchema[ReturnProcessQuestionKey.PackageComplete],
|
||||
ReturnProcessQuestionSchema[ReturnProcessQuestionKey.CaseCondition],
|
||||
ReturnProcessQuestionSchema[ReturnProcessQuestionKey.UsbPort],
|
||||
];
|
||||
|
||||
schemas.forEach(schema => {
|
||||
// Valid values
|
||||
expect(schema.safeParse(YesNoAnswer.Yes).success).toBe(true);
|
||||
expect(schema.safeParse(YesNoAnswer.No).success).toBe(true);
|
||||
|
||||
// Invalid values
|
||||
expect(schema.safeParse('maybe').success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate PackageIncomplete schema', () => {
|
||||
const schema = ReturnProcessQuestionSchema[ReturnProcessQuestionKey.PackageIncomplete];
|
||||
|
||||
// Valid values - with options
|
||||
expect(schema.safeParse({
|
||||
options: [PackageIncompleteAnswer.OVP],
|
||||
other: '',
|
||||
}).success).toBe(true);
|
||||
|
||||
// Valid values - with multiple options
|
||||
expect(schema.safeParse({
|
||||
options: [
|
||||
PackageIncompleteAnswer.OVP,
|
||||
PackageIncompleteAnswer.ChargingCable
|
||||
],
|
||||
other: 'Some other missing part',
|
||||
}).success).toBe(true);
|
||||
|
||||
// Valid values - with only other
|
||||
expect(schema.safeParse({
|
||||
options: [],
|
||||
other: 'Something else is missing',
|
||||
}).success).toBe(true);
|
||||
|
||||
// Invalid values
|
||||
expect(schema.safeParse({}).success).toBe(false);
|
||||
expect(schema.safeParse({ options: [] }).success).toBe(false);
|
||||
expect(schema.safeParse({ options: ['invalid'] }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('should validate DeliveredItem schema', () => {
|
||||
const schema = ReturnProcessQuestionSchema[ReturnProcessQuestionKey.DeliveredItem];
|
||||
|
||||
// Valid values
|
||||
expect(schema.safeParse({
|
||||
ean: '1234567890123',
|
||||
format: 'Book',
|
||||
formatDetails: 'Hardcover',
|
||||
name: 'Test Book',
|
||||
}).success).toBe(true);
|
||||
|
||||
// Invalid values - missing required fields
|
||||
expect(schema.safeParse({
|
||||
ean: '1234567890123',
|
||||
format: 'Book',
|
||||
}).success).toBe(false);
|
||||
|
||||
// Invalid values - wrong types
|
||||
expect(schema.safeParse({
|
||||
ean: 1234567890123,
|
||||
format: 'Book',
|
||||
formatDetails: 'Hardcover',
|
||||
}).success).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
import { ReturnProcessQuestionKey } from '../models';
|
||||
|
||||
/**
|
||||
* Constants for product categories used in the return process.
|
||||
*/
|
||||
@@ -11,34 +14,158 @@ export const enum ProductCategory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants for item condition values.
|
||||
* Constant object for item condition answers in the return process.
|
||||
* - OVP: Original packaging/as new condition
|
||||
* - Damaged: Item is damaged or has printing errors
|
||||
*/
|
||||
export const enum ItemConditionValue {
|
||||
OriginalPackaging = 'ovp',
|
||||
Damaged = 'damaged',
|
||||
}
|
||||
export const ItemConditionAnswer = {
|
||||
OVP: 'ovp',
|
||||
Damaged: 'damaged',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Constants for return reason values.
|
||||
* Type representing valid item condition answers derived from the ItemConditionAnswer constant.
|
||||
* Used for type checking and autocompletion when handling item condition responses.
|
||||
*/
|
||||
export const enum ReturnReasonValue {
|
||||
Dislike = 'dislike',
|
||||
WrongItem = 'wrong_item',
|
||||
}
|
||||
export type ItemConditionAnswer =
|
||||
(typeof ItemConditionAnswer)[keyof typeof ItemConditionAnswer];
|
||||
|
||||
/**
|
||||
* Constants for item defective values.
|
||||
* Constant object for return reason answers in the return process.
|
||||
* - Dislike: Customer doesn't like the item or wants to withdraw purchase
|
||||
* - WrongItem: Customer received the wrong item (incorrect delivery)
|
||||
*/
|
||||
export const enum ItemDefectiveValue {
|
||||
Yes = 'yes',
|
||||
No = 'no',
|
||||
}
|
||||
export const ReturnReasonAnswer = {
|
||||
Dislike: 'dislike',
|
||||
WrongItem: 'wrong_item',
|
||||
} as const;
|
||||
|
||||
export const enum PackageIncompleteValue {
|
||||
// Karton/Umverpackung
|
||||
Ovp = 'ovp',
|
||||
// Ladekabel
|
||||
CharchingCable = 'charching_cable',
|
||||
// Quickstart Guide
|
||||
QuickstartGuide = 'quickstart_guide',
|
||||
}
|
||||
/**
|
||||
* Type representing valid return reason answers derived from the ReturnReasonAnswer constant.
|
||||
* Used for type checking and autocompletion when handling return reason responses.
|
||||
*/
|
||||
export type ReturnReasonAnswer =
|
||||
(typeof ReturnReasonAnswer)[keyof typeof ReturnReasonAnswer];
|
||||
|
||||
/**
|
||||
* Constant object for yes/no answers in the return process.
|
||||
* Used for boolean-type questions throughout the return process flow.
|
||||
*/
|
||||
export const YesNoAnswer = {
|
||||
Yes: 'yes',
|
||||
No: 'no',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Type representing valid yes/no answers derived from the YesNoAnswer constant.
|
||||
* Used for type checking and autocompletion when handling boolean responses.
|
||||
*/
|
||||
export type YesNoAnswer = (typeof YesNoAnswer)[keyof typeof YesNoAnswer];
|
||||
|
||||
/**
|
||||
* Constant object for package incomplete answers in the return process.
|
||||
* Specifies which parts of the package are missing during a return.
|
||||
* - OVP: Original packaging/box is missing
|
||||
* - ChargingCable: Charging cable is missing
|
||||
* - QuickStartGuide: Quick start guide documentation is missing
|
||||
*/
|
||||
export const PackageIncompleteAnswer = {
|
||||
OVP: 'ovp',
|
||||
ChargingCable: 'charging_cable',
|
||||
QuickStartGuide: 'quick_start_guide',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Type representing valid package incomplete answers derived from the PackageIncompleteAnswer constant.
|
||||
* Used for type checking and autocompletion when handling package incomplete responses.
|
||||
*/
|
||||
export type PackageIncompleteAnswer =
|
||||
(typeof PackageIncompleteAnswer)[keyof typeof PackageIncompleteAnswer];
|
||||
|
||||
/**
|
||||
* Zod schema definitions for validating return process question answers.
|
||||
* Each schema corresponds to a specific question key from ReturnProcessQuestionKey
|
||||
* and defines the expected format and allowed values for that question's answer.
|
||||
*/
|
||||
export const ReturnProcessQuestionSchema = {
|
||||
[ReturnProcessQuestionKey.ItemCondition]: z.enum([
|
||||
ItemConditionAnswer.OVP,
|
||||
ItemConditionAnswer.Damaged,
|
||||
]),
|
||||
[ReturnProcessQuestionKey.ReturnReason]: z.enum([
|
||||
ReturnReasonAnswer.Dislike,
|
||||
ReturnReasonAnswer.WrongItem,
|
||||
]),
|
||||
[ReturnProcessQuestionKey.ItemDefective]: z.enum([
|
||||
YesNoAnswer.Yes,
|
||||
YesNoAnswer.No,
|
||||
]),
|
||||
[ReturnProcessQuestionKey.DisplayCondition]: z.enum([
|
||||
YesNoAnswer.Yes,
|
||||
YesNoAnswer.No,
|
||||
]),
|
||||
[ReturnProcessQuestionKey.DevicePower]: z.enum([
|
||||
YesNoAnswer.Yes,
|
||||
YesNoAnswer.No,
|
||||
]),
|
||||
[ReturnProcessQuestionKey.PackageComplete]: z.enum([
|
||||
YesNoAnswer.Yes,
|
||||
YesNoAnswer.No,
|
||||
]),
|
||||
[ReturnProcessQuestionKey.PackageIncomplete]: z
|
||||
.object({
|
||||
options: z
|
||||
.array(
|
||||
z.enum([
|
||||
PackageIncompleteAnswer.OVP,
|
||||
PackageIncompleteAnswer.ChargingCable,
|
||||
PackageIncompleteAnswer.QuickStartGuide,
|
||||
]),
|
||||
)
|
||||
.min(1),
|
||||
other: z.string().optional(),
|
||||
})
|
||||
.or(
|
||||
z.object({
|
||||
options: z
|
||||
.array(
|
||||
z.enum([
|
||||
PackageIncompleteAnswer.OVP,
|
||||
PackageIncompleteAnswer.ChargingCable,
|
||||
PackageIncompleteAnswer.QuickStartGuide,
|
||||
]),
|
||||
)
|
||||
.optional(),
|
||||
other: z.string(),
|
||||
}),
|
||||
),
|
||||
[ReturnProcessQuestionKey.CaseCondition]: z.enum([
|
||||
YesNoAnswer.Yes,
|
||||
YesNoAnswer.No,
|
||||
]),
|
||||
[ReturnProcessQuestionKey.UsbPort]: z.enum([YesNoAnswer.Yes, YesNoAnswer.No]),
|
||||
[ReturnProcessQuestionKey.DeliveredItem]: z.object({
|
||||
ean: z.string(),
|
||||
format: z.string(),
|
||||
formatDetails: z.string(),
|
||||
additionalName: z.string().optional(),
|
||||
catalogProductNumber: z.string().optional(),
|
||||
contributors: z.string().optional(),
|
||||
edition: z.string().optional(),
|
||||
formatDetail: z.string().optional(),
|
||||
locale: z.string().optional(),
|
||||
productGroup: z.string().optional(),
|
||||
manufacturer: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
productGroupDetails: z.string().optional(),
|
||||
publicationDate: z.string().optional(),
|
||||
serial: z.string().optional(),
|
||||
}),
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Type representing any valid schema from ReturnProcessQuestionSchema.
|
||||
* Used for type checking when working with question schemas dynamically.
|
||||
*/
|
||||
export type ReturnProcessQuestionSchema =
|
||||
(typeof ReturnProcessQuestionSchema)[keyof typeof ReturnProcessQuestionSchema];
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,20 +1,26 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import {
|
||||
ItemConditionValue,
|
||||
ReturnReasonValue,
|
||||
ItemDefectiveValue,
|
||||
ItemConditionAnswer,
|
||||
ReturnReasonAnswer,
|
||||
YesNoAnswer,
|
||||
} from './constants';
|
||||
import { validateOvpReturn } from './validators';
|
||||
|
||||
/**
|
||||
* Questions for the return process of other electronic devices.
|
||||
*
|
||||
* This array defines the sequence and branching logic of questions presented to users
|
||||
* when processing electronic device returns. The question flow adapts dynamically
|
||||
* based on the customer's responses to guide them through the appropriate return path.
|
||||
*
|
||||
* The question sequence typically includes:
|
||||
* 1. Item condition assessment (original packaging or opened/damaged)
|
||||
* 2. Return reason inquiry for items in original packaging
|
||||
* 3. Defect verification for opened/damaged items
|
||||
* 4. Collection of information about incorrectly delivered items when applicable
|
||||
*/
|
||||
export const elektronischeGeraeteQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
@@ -24,12 +30,12 @@ export const elektronischeGeraeteQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
value: ItemConditionAnswer.OVP,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet / Beschädigt',
|
||||
value: ItemConditionValue.Damaged,
|
||||
value: ItemConditionAnswer.Damaged,
|
||||
nextQuestion: ReturnProcessQuestionKey.ItemDefective,
|
||||
},
|
||||
],
|
||||
@@ -39,10 +45,10 @@ export const elektronischeGeraeteQuestions: ReturnProcessQuestion[] = [
|
||||
description: 'Warum möchtest du den Artikel zurückgeben?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonValue.Dislike },
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonAnswer.Dislike },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
value: ReturnReasonAnswer.WrongItem,
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
@@ -54,11 +60,11 @@ export const elektronischeGeraeteQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
value: YesNoAnswer.Yes,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
value: YesNoAnswer.No,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -68,39 +74,3 @@ export const elektronischeGeraeteQuestions: ReturnProcessQuestion[] = [
|
||||
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',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,12 +15,6 @@
|
||||
// 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';
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,16 +1,20 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import { ItemConditionValue, ReturnReasonValue } from './constants';
|
||||
import { validateOvpReturn } from './validators';
|
||||
import { ItemConditionAnswer, ReturnReasonAnswer } from './constants';
|
||||
|
||||
/**
|
||||
* Questions for the return process of Spielwaren/Puzzle and other nonbook items.
|
||||
*
|
||||
* This array defines the sequence and logic flow of questions presented to users
|
||||
* when processing returns for toys, puzzles, and other nonbook items. The questions
|
||||
* follow a branching structure that adapts based on the customer's responses.
|
||||
*
|
||||
* The question flow generally starts with item condition assessment, followed by
|
||||
* return reason inquiry, and may include additional questions about the delivered
|
||||
* item when applicable.
|
||||
*/
|
||||
export const nonbookQuestions: ReturnProcessQuestion[] = [
|
||||
{
|
||||
@@ -20,10 +24,10 @@ export const nonbookQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Neuwertig/Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
value: ItemConditionAnswer.OVP,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{ label: 'Beschädigt/Fehldruck', value: ItemConditionValue.Damaged },
|
||||
{ label: 'Beschädigt/Fehldruck', value: ItemConditionAnswer.Damaged },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -31,10 +35,10 @@ export const nonbookQuestions: ReturnProcessQuestion[] = [
|
||||
description: 'Warum möchtest du den Artikel zurückgeben?',
|
||||
type: ReturnProcessQuestionType.Select,
|
||||
options: [
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonValue.Dislike },
|
||||
{ label: 'Gefällt nicht/Wiederruf', value: ReturnReasonAnswer.Dislike },
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
value: ReturnReasonAnswer.WrongItem,
|
||||
nextQuestion: ReturnProcessQuestionKey.DeliveredItem,
|
||||
},
|
||||
],
|
||||
@@ -45,28 +49,3 @@ export const nonbookQuestions: ReturnProcessQuestion[] = [
|
||||
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',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
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());
|
||||
});
|
||||
});
|
||||
@@ -1,23 +1,20 @@
|
||||
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';
|
||||
import { bookCalendarQuestions } from './book-calendar';
|
||||
import { tonDatentraegerQuestions } from './ton-datentraeger';
|
||||
import { nonbookQuestions } from './nonbook';
|
||||
import { elektronischeGeraeteQuestions } from './elektronische-geraete';
|
||||
import { tolinoQuestions } from './tolino';
|
||||
|
||||
/**
|
||||
* A mapping of categories to their respective return process questions.
|
||||
* A mapping of product categories to their respective return process questions.
|
||||
* This registry centrally connects each product category with its specific
|
||||
* question flow, allowing the application to dynamically load the appropriate
|
||||
* questions based on the product being returned.
|
||||
*
|
||||
* Each category maps to an array of ReturnProcessQuestion objects that define
|
||||
* the questions, their sequence, and the branching logic for that category.
|
||||
*/
|
||||
export const CategoryQuestions: Record<
|
||||
ProductCategory,
|
||||
@@ -30,18 +27,3 @@ export const CategoryQuestions: Record<
|
||||
[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,
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
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 });
|
||||
});
|
||||
});
|
||||
|
||||
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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,22 +1,32 @@
|
||||
import {
|
||||
ReturnProcessAnswers,
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionKey,
|
||||
ReturnProcessQuestionType,
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import {
|
||||
ItemConditionValue,
|
||||
ItemDefectiveValue,
|
||||
PackageIncompleteValue,
|
||||
ReturnReasonValue,
|
||||
ItemConditionAnswer,
|
||||
PackageIncompleteAnswer,
|
||||
ReturnReasonAnswer,
|
||||
YesNoAnswer,
|
||||
} from './constants';
|
||||
|
||||
/**
|
||||
* Questions for the return process of Tolino devices.
|
||||
* This array defines the sequence and logic flow of questions presented to users
|
||||
* when processing Tolino device returns in the system.
|
||||
*
|
||||
* The question flow for Tolino devices is more complex than other product types
|
||||
* due to the detailed assessment needed for electronic devices:
|
||||
*
|
||||
* 1. Item condition assessment (original packaging or opened/damaged)
|
||||
* 2. Device functionality verification (can it power on?)
|
||||
* 3. Package completeness check
|
||||
* 4. Detailed condition assessment (case, display, USB port)
|
||||
* 5. Return reason inquiry
|
||||
*
|
||||
* This comprehensive flow helps accurately determine return eligibility and
|
||||
* properly document the condition of returned devices for appropriate processing.
|
||||
*
|
||||
* Each question has a unique key, descriptive text, question type, and possible options
|
||||
* with their corresponding next question in the flow.
|
||||
*/
|
||||
@@ -28,11 +38,11 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
value: ItemConditionAnswer.OVP,
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet/Beschädigt',
|
||||
value: ItemConditionValue.Damaged,
|
||||
value: ItemConditionAnswer.Damaged,
|
||||
nextQuestion: ReturnProcessQuestionKey.DevicePower,
|
||||
},
|
||||
],
|
||||
@@ -44,12 +54,13 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.No,
|
||||
value: YesNoAnswer.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.PackageComplete,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
value: YesNoAnswer.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.PackageComplete,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -60,13 +71,13 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.PackageIncomplete,
|
||||
value: YesNoAnswer.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.CaseCondition,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.CaseCondition,
|
||||
value: YesNoAnswer.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.PackageIncomplete,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -77,15 +88,15 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Karton/Umverpackung',
|
||||
value: PackageIncompleteValue.Ovp,
|
||||
value: PackageIncompleteAnswer.OVP,
|
||||
},
|
||||
{
|
||||
label: 'Ladekabel',
|
||||
value: PackageIncompleteValue.CharchingCable,
|
||||
value: PackageIncompleteAnswer.ChargingCable,
|
||||
},
|
||||
{
|
||||
label: 'Quickstart Guide',
|
||||
value: PackageIncompleteValue.QuickstartGuide,
|
||||
value: PackageIncompleteAnswer.QuickStartGuide,
|
||||
},
|
||||
],
|
||||
other: {
|
||||
@@ -100,12 +111,12 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
value: YesNoAnswer.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.DisplayCondition,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
value: YesNoAnswer.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.UsbPort,
|
||||
},
|
||||
],
|
||||
@@ -117,11 +128,12 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
value: YesNoAnswer.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.UsbPort,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
value: YesNoAnswer.No,
|
||||
nextQuestion: ReturnProcessQuestionKey.UsbPort,
|
||||
},
|
||||
],
|
||||
@@ -133,12 +145,12 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.No,
|
||||
value: YesNoAnswer.Yes,
|
||||
nextQuestion: ReturnProcessQuestionKey.ReturnReason,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
value: YesNoAnswer.No,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -149,23 +161,12 @@ export const tolinoQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Gefällt nicht/Wiederruf',
|
||||
value: ReturnReasonValue.Dislike,
|
||||
value: ReturnReasonAnswer.Dislike,
|
||||
},
|
||||
{
|
||||
label: 'Fehllieferung',
|
||||
value: ReturnReasonValue.WrongItem,
|
||||
value: ReturnReasonAnswer.WrongItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the Tolino return process and determines return eligibility.
|
||||
* @param {ReturnProcessAnswers} answers - A record of answers keyed by question keys
|
||||
* @returns {EligibleForReturn} Object containing eligibility state and reason if applicable
|
||||
*/
|
||||
export function validateTolinoQuestions(
|
||||
answers: ReturnProcessAnswers,
|
||||
): EligibleForReturn {
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,12 +6,19 @@ import {
|
||||
EligibleForReturn,
|
||||
EligibleForReturnState,
|
||||
} from '../models';
|
||||
import { ItemConditionValue, ItemDefectiveValue } from './constants';
|
||||
import { ItemConditionAnswer, YesNoAnswer } from './constants';
|
||||
|
||||
/**
|
||||
* Questions for the return process of Ton-/Datenträger (audio/data carriers).
|
||||
* This array defines the sequence and logic flow of questions presented to users
|
||||
* when processing audio or data carrier returns in the system.
|
||||
*
|
||||
* The question flow for audio/data carriers is specifically designed to handle
|
||||
* the unique return policies that apply to these products:
|
||||
* - Sealed items are typically eligible for return
|
||||
* - Opened items may be eligible if they're defective
|
||||
* - Opened, non-defective items might be subject to different return policies
|
||||
*
|
||||
* Each question has a unique key, descriptive text, question type, and possible options
|
||||
* with their corresponding next question in the flow.
|
||||
*/
|
||||
@@ -23,11 +30,11 @@ export const tonDatentraegerQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Versiegelt/Originalverpackt',
|
||||
value: ItemConditionValue.OriginalPackaging,
|
||||
value: ItemConditionAnswer.OVP,
|
||||
},
|
||||
{
|
||||
label: 'Geöffnet',
|
||||
value: ItemConditionValue.Damaged,
|
||||
value: ItemConditionAnswer.Damaged,
|
||||
nextQuestion: ReturnProcessQuestionKey.ItemDefective,
|
||||
},
|
||||
],
|
||||
@@ -39,62 +46,12 @@ export const tonDatentraegerQuestions: ReturnProcessQuestion[] = [
|
||||
options: [
|
||||
{
|
||||
label: 'Ja',
|
||||
value: ItemDefectiveValue.Yes,
|
||||
value: YesNoAnswer.Yes,
|
||||
},
|
||||
{
|
||||
label: 'Nein',
|
||||
value: ItemDefectiveValue.No,
|
||||
value: YesNoAnswer.No,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the answers for the Ton-/Datenträger (audio/data carriers) return process and determines return eligibility.
|
||||
*
|
||||
* The validation logic follows these rules:
|
||||
* 1. If the item is in original packaging/sealed, it's always eligible for return
|
||||
* 2. If the item has been opened, it's only eligible if it's defective
|
||||
* 3. If the item has been opened but is not defective, it's not eligible for return
|
||||
* 4. If invalid or incomplete answers are provided, the item is not eligible
|
||||
*
|
||||
* @param {ReturnProcessAnswers} answers - A record of answers keyed by question keys
|
||||
* @returns {EligibleForReturn} Object containing eligibility state and reason if applicable
|
||||
*/
|
||||
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',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
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;
|
||||
@@ -1,54 +0,0 @@
|
||||
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',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
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',
|
||||
};
|
||||
}
|
||||
@@ -6,13 +6,14 @@ import {
|
||||
ReturnProcessQuestion,
|
||||
ReturnProcessQuestionType,
|
||||
} from './models';
|
||||
import { CategoryQuestions, CategoryQuestionValidators } from './questions';
|
||||
import { CategoryQuestions, ReturnProcessQuestionSchema } from './questions';
|
||||
import { KeyValue } from '@angular/common';
|
||||
import { ReturnProcessChecklistAnswerSchema } from './schemas';
|
||||
|
||||
/**
|
||||
* Service responsible for managing the return process workflow.
|
||||
* Handles questions, validation, and eligibility determination for product returns.
|
||||
* Uses Zod schemas for data validation throughout the return process.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ReturnProcessService {
|
||||
@@ -45,32 +46,19 @@ export class ReturnProcessService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the validator function for a specific return process based on product category.
|
||||
*
|
||||
* @param {ReturnProcess} process - The return process containing product information.
|
||||
* @returns {((answers: Record<string, unknown>) => EligibleForReturn) | undefined} Validator function or undefined if no matching category found.
|
||||
*/
|
||||
returnProcessQuestionValidator(
|
||||
process: ReturnProcess,
|
||||
): ((answers: Record<string, unknown>) => EligibleForReturn) | undefined {
|
||||
const category =
|
||||
process.productCategory || process.receiptItem.features?.['category'];
|
||||
|
||||
if (category) {
|
||||
return CategoryQuestionValidators[
|
||||
category as keyof typeof CategoryQuestionValidators
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets active questions in the return process based on previously provided answers.
|
||||
* Handles question branching logic and detects cyclic dependencies.
|
||||
*
|
||||
* This method:
|
||||
* 1. Determines which questions to show based on previous answers
|
||||
* 2. Validates answers using Zod schemas to ensure data integrity
|
||||
* 3. Handles different question types (Select, Product, Checklist)
|
||||
* 4. Follows the branching logic defined by nextQuestion properties
|
||||
*
|
||||
* @param {ReturnProcess} process - The return process containing answers and product information.
|
||||
* @returns {ReturnProcessQuestion[] | undefined} Active questions in the process or undefined if no questions apply.
|
||||
* @throws {Error} If cyclic question dependencies are detected in the question flow.
|
||||
*/
|
||||
activeReturnProcessQuestions(
|
||||
process: ReturnProcess,
|
||||
@@ -87,8 +75,7 @@ export class ReturnProcessService {
|
||||
|
||||
while (questionKey) {
|
||||
if (visited.has(questionKey)) {
|
||||
console.error('Cyclic question dependency detected', questionKey);
|
||||
break;
|
||||
throw new Error('Cyclic question dependency detected');
|
||||
}
|
||||
visited.add(questionKey);
|
||||
|
||||
@@ -96,6 +83,14 @@ export class ReturnProcessService {
|
||||
if (question) {
|
||||
result.push(question);
|
||||
|
||||
const schema = ReturnProcessQuestionSchema[question.key];
|
||||
|
||||
const parseResult = schema.safeParse(process.answers[question.key]);
|
||||
|
||||
if (!parseResult.success) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (question.type === ReturnProcessQuestionType.Select) {
|
||||
const option = question.options.find(
|
||||
(o) => o.value === process.answers[question.key],
|
||||
@@ -106,11 +101,11 @@ export class ReturnProcessService {
|
||||
? question.nextQuestion
|
||||
: undefined;
|
||||
} else if (question.type === ReturnProcessQuestionType.Checklist) {
|
||||
const answer = ReturnProcessChecklistAnswerSchema.parse(
|
||||
const answer = ReturnProcessChecklistAnswerSchema.optional().parse(
|
||||
process.answers[question.key],
|
||||
);
|
||||
|
||||
if (answer.options?.length || answer.other) {
|
||||
if ((answer && answer.options?.length) || (answer && answer.other)) {
|
||||
questionKey = question.nextQuestion;
|
||||
} else {
|
||||
questionKey = undefined;
|
||||
@@ -205,37 +200,47 @@ export class ReturnProcessService {
|
||||
|
||||
/**
|
||||
* Determines whether a product is eligible for return based on provided answers.
|
||||
* Validates all questions have been answered and applies category-specific validation rules.
|
||||
* Uses schema validation to verify answers and determines eligibility status.
|
||||
*
|
||||
* The method has been refactored to:
|
||||
* 1. Use schema validation instead of custom validator functions
|
||||
* 2. Return undefined if questions are not answered completely
|
||||
* 3. Always return eligible status for complete and validated answers
|
||||
*
|
||||
* @param {ReturnProcess} returnProcess - The return process containing answers and product information.
|
||||
* @returns {EligibleForReturn} Object indicating eligibility state and reason.
|
||||
* @returns {EligibleForReturn | undefined} Object indicating eligibility state or undefined if answers incomplete.
|
||||
*/
|
||||
eligibleForReturn(returnProcess: ReturnProcess): EligibleForReturn {
|
||||
eligibleForReturn(
|
||||
returnProcess: ReturnProcess,
|
||||
): EligibleForReturn | undefined {
|
||||
const questions = this.activeReturnProcessQuestions(returnProcess);
|
||||
|
||||
if (!questions) {
|
||||
return { state: EligibleForReturnState.Pending };
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const everyQuestionAnswered = questions.every(
|
||||
(q) => q.key in returnProcess.answers,
|
||||
);
|
||||
if (!everyQuestionAnswered) {
|
||||
return {
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'Not all questions answered',
|
||||
};
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const validator = this.returnProcessQuestionValidator(returnProcess);
|
||||
const allQuestionsAnswered = questions.every((q) => {
|
||||
if (q.type === ReturnProcessQuestionType.Checklist) {
|
||||
const answer = ReturnProcessChecklistAnswerSchema.optional().parse(
|
||||
returnProcess.answers[q.key],
|
||||
);
|
||||
return (answer && answer.options?.length) || (answer && answer.other);
|
||||
} else {
|
||||
return q.key in returnProcess.answers;
|
||||
}
|
||||
});
|
||||
|
||||
if (!validator) {
|
||||
return {
|
||||
state: EligibleForReturnState.Pending,
|
||||
reason: 'No validator found',
|
||||
};
|
||||
if (!allQuestionsAnswered) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return validator(returnProcess.answers);
|
||||
return { state: EligibleForReturnState.Eligible };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
<div
|
||||
class="return-process-item-body border-b-isa-neutral-300"
|
||||
[class.border-b]="eligible.state !== 'pending'"
|
||||
[class.border-b]="eligible"
|
||||
>
|
||||
<div class="return-process-item-body__product">
|
||||
<img
|
||||
@@ -43,7 +43,7 @@
|
||||
[returnProcessId]="returnProcessId()"
|
||||
></oms-feature-return-process-questions>
|
||||
</div>
|
||||
@switch (eligible.state) {
|
||||
@switch (eligible?.state) {
|
||||
@case ('eligible') {
|
||||
<div
|
||||
class="text-isa-accent-green isa-text-body-2-bold flex items-center gap-1"
|
||||
|
||||
Reference in New Issue
Block a user