saving multiple activities selections at once in the api
This commit is contained in:
@@ -35,24 +35,14 @@ export const handler = safeHandler(async (
|
||||
}
|
||||
}
|
||||
|
||||
const itineraryActivityXid = Number(body.itineraryActivityXid);
|
||||
if (!Number.isInteger(itineraryActivityXid) || itineraryActivityXid <= 0) {
|
||||
throw new ApiError(400, 'itineraryActivityXid is required.');
|
||||
}
|
||||
const activities = Array.isArray(body.activities)
|
||||
? body.activities
|
||||
: body.itineraryActivityXid !== undefined
|
||||
? [body]
|
||||
: [];
|
||||
|
||||
const selectedEquipmentIds = Array.isArray(body.selectedEquipmentIds)
|
||||
? body.selectedEquipmentIds.map((id: unknown) => Number(id))
|
||||
: [];
|
||||
const selectedFoodTypeIds = Array.isArray(body.selectedFoodTypeIds)
|
||||
? body.selectedFoodTypeIds.map((id: unknown) => Number(id))
|
||||
: [];
|
||||
|
||||
if (selectedEquipmentIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedEquipmentIds must contain valid ids.');
|
||||
}
|
||||
|
||||
if (selectedFoodTypeIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedFoodTypeIds must contain valid ids.');
|
||||
if (!activities.length) {
|
||||
throw new ApiError(400, 'activities is required and must be a non-empty array.');
|
||||
}
|
||||
|
||||
const toOptionalId = (value: unknown) => {
|
||||
@@ -68,21 +58,60 @@ export const handler = safeHandler(async (
|
||||
return parsed;
|
||||
};
|
||||
|
||||
const result = await itineraryService.saveItineraryActivitySelections(userId, {
|
||||
itineraryActivityXid,
|
||||
isFoodOpted:
|
||||
body.isFoodOpted === undefined ? false : Boolean(body.isFoodOpted),
|
||||
selectedFoodTypeIds,
|
||||
isTrainerOpted:
|
||||
body.isTrainerOpted === undefined ? false : Boolean(body.isTrainerOpted),
|
||||
isInActivityNavigationOpted:
|
||||
body.isInActivityNavigationOpted === undefined
|
||||
? false
|
||||
: Boolean(body.isInActivityNavigationOpted),
|
||||
selectedNavigationModeXid: toOptionalId(body.selectedNavigationModeXid),
|
||||
selectedEquipmentIds,
|
||||
const normalizedActivities = activities.map((activity: any, index: number) => {
|
||||
const itineraryActivityXid = Number(activity.itineraryActivityXid);
|
||||
if (!Number.isInteger(itineraryActivityXid) || itineraryActivityXid <= 0) {
|
||||
throw new ApiError(400, `activities[${index}].itineraryActivityXid is required.`);
|
||||
}
|
||||
|
||||
const selectedFoodTypeIds: number[] = Array.isArray(activity.selectedFoodTypeIds)
|
||||
? Array.from(
|
||||
new Set(
|
||||
activity.selectedFoodTypeIds.map((id: unknown): number => Number(id)),
|
||||
),
|
||||
)
|
||||
: [];
|
||||
const selectedEquipmentIds: number[] = Array.isArray(activity.selectedEquipmentIds)
|
||||
? Array.from(
|
||||
new Set(
|
||||
activity.selectedEquipmentIds.map((id: unknown): number => Number(id)),
|
||||
),
|
||||
)
|
||||
: [];
|
||||
|
||||
if (selectedEquipmentIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedEquipmentIds must contain valid ids.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedFoodTypeIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedFoodTypeIds must contain valid ids.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
itineraryActivityXid,
|
||||
isFoodOpted: activity.isFoodOpted === undefined ? false : Boolean(activity.isFoodOpted),
|
||||
selectedFoodTypeIds,
|
||||
isTrainerOpted: activity.isTrainerOpted === undefined ? false : Boolean(activity.isTrainerOpted),
|
||||
isInActivityNavigationOpted:
|
||||
activity.isInActivityNavigationOpted === undefined
|
||||
? false
|
||||
: Boolean(activity.isInActivityNavigationOpted),
|
||||
selectedNavigationModeXid: toOptionalId(activity.selectedNavigationModeXid),
|
||||
selectedEquipmentIds,
|
||||
};
|
||||
});
|
||||
|
||||
const result = await itineraryService.saveItineraryActivitySelections(
|
||||
userId,
|
||||
normalizedActivities,
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
|
||||
@@ -2393,7 +2393,7 @@ export class ItineraryService {
|
||||
|
||||
async saveItineraryActivitySelections(
|
||||
userXid: number,
|
||||
payload: {
|
||||
payload: Array<{
|
||||
itineraryActivityXid: number;
|
||||
isFoodOpted?: boolean;
|
||||
selectedFoodTypeIds?: number[];
|
||||
@@ -2401,64 +2401,356 @@ export class ItineraryService {
|
||||
isInActivityNavigationOpted?: boolean;
|
||||
selectedNavigationModeXid?: number | null;
|
||||
selectedEquipmentIds?: number[];
|
||||
},
|
||||
}>,
|
||||
) {
|
||||
const selectedFoodTypeIds = Array.from(
|
||||
new Set((payload.selectedFoodTypeIds ?? []).map(Number)),
|
||||
);
|
||||
const selectedEquipmentIds = Array.from(
|
||||
new Set((payload.selectedEquipmentIds ?? []).map(Number)),
|
||||
);
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
const itineraryActivity = await tx.itineraryActivities.findFirst({
|
||||
where: {
|
||||
id: payload.itineraryActivityXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
itineraryHeader: {
|
||||
ItineraryMembers: {
|
||||
some: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
const result = await Promise.all(
|
||||
payload.map(async (item, index) => {
|
||||
const selectedFoodTypeIds = Array.from(
|
||||
new Set((item.selectedFoodTypeIds ?? []).map(Number)),
|
||||
);
|
||||
const selectedEquipmentIds = Array.from(
|
||||
new Set((item.selectedEquipmentIds ?? []).map(Number)),
|
||||
);
|
||||
|
||||
const itineraryActivity = await tx.itineraryActivities.findFirst({
|
||||
where: {
|
||||
id: item.itineraryActivityXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
itineraryHeader: {
|
||||
ItineraryMembers: {
|
||||
some: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
itineraryHeaderXid: true,
|
||||
itineraryType: true,
|
||||
activityXid: true,
|
||||
activity: {
|
||||
select: {
|
||||
id: true,
|
||||
foodAvailable: true,
|
||||
trainerAvailable: true,
|
||||
inActivityAvailable: true,
|
||||
equipmentAvailable: true,
|
||||
activityFoodTypes: {
|
||||
itineraryHeaderXid: true,
|
||||
itineraryType: true,
|
||||
activityXid: true,
|
||||
activity: {
|
||||
select: {
|
||||
id: true,
|
||||
foodAvailable: true,
|
||||
trainerAvailable: true,
|
||||
inActivityAvailable: true,
|
||||
equipmentAvailable: true,
|
||||
activityFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ActivityNavigationModes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
navigationModeName: true,
|
||||
isInActivityChargeable: true,
|
||||
navigationModesBasePrice: true,
|
||||
navigationModesTotalPrice: true,
|
||||
},
|
||||
},
|
||||
ActivityEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!itineraryActivity) {
|
||||
throw new ApiError(
|
||||
404,
|
||||
`Itinerary activity not found for item ${index}.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.itineraryType !== 'ACTIVITY' ||
|
||||
!itineraryActivity.activityXid ||
|
||||
!itineraryActivity.activity
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'Selections can only be stored for itinerary items linked to an activity.',
|
||||
);
|
||||
}
|
||||
|
||||
const itineraryMember = await tx.itineraryMembers.findFirst({
|
||||
where: {
|
||||
itineraryHeaderXid: itineraryActivity.itineraryHeaderXid,
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!itineraryMember) {
|
||||
throw new ApiError(404, 'Itinerary member record not found.');
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedEquipmentIds must contain valid ids.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedFoodTypeIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedFoodTypeIds must contain valid ids.`,
|
||||
);
|
||||
}
|
||||
|
||||
const isFoodOpted = Boolean(item.isFoodOpted);
|
||||
const isTrainerOpted = Boolean(item.isTrainerOpted);
|
||||
const isInActivityNavigationOpted = Boolean(
|
||||
item.isInActivityNavigationOpted,
|
||||
);
|
||||
|
||||
const selectedNavigationModeXid =
|
||||
item.selectedNavigationModeXid === undefined ||
|
||||
item.selectedNavigationModeXid === null
|
||||
? null
|
||||
: Number(item.selectedNavigationModeXid);
|
||||
|
||||
if (
|
||||
selectedNavigationModeXid !== null &&
|
||||
(!Number.isInteger(selectedNavigationModeXid) ||
|
||||
selectedNavigationModeXid <= 0)
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedNavigationModeXid must be a valid id.`,
|
||||
);
|
||||
}
|
||||
|
||||
const availableFoodTypeIds = new Set(
|
||||
itineraryActivity.activity.activityFoodTypes.map((entry) => entry.id),
|
||||
);
|
||||
const availableNavigationModeIds = new Set(
|
||||
itineraryActivity.activity.ActivityNavigationModes.map((entry) => entry.id),
|
||||
);
|
||||
const availableEquipmentIds = new Set(
|
||||
itineraryActivity.activity.ActivityEquipments.map((entry) => entry.id),
|
||||
);
|
||||
|
||||
if (isFoodOpted) {
|
||||
if (!itineraryActivity.activity.foodAvailable) {
|
||||
throw new ApiError(400, `activities[${index}]: Food is not available for this activity.`);
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.activity.activityFoodTypes.length > 0 &&
|
||||
!selectedFoodTypeIds.length
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedFoodTypeIds is required when food is opted.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedFoodTypeIds.some((id) => !availableFoodTypeIds.has(id))) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}]: One or more selected food types do not belong to this activity.`,
|
||||
);
|
||||
}
|
||||
} else if (selectedFoodTypeIds.length) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedFoodTypeIds cannot be sent when food is not opted.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (isTrainerOpted && !itineraryActivity.activity.trainerAvailable) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}]: Trainer is not available for this activity.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (isInActivityNavigationOpted) {
|
||||
if (!itineraryActivity.activity.inActivityAvailable) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}]: In-activity navigation is not available for this activity.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.activity.ActivityNavigationModes.length > 0 &&
|
||||
!selectedNavigationModeXid
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedNavigationModeXid is required when navigation is opted.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
selectedNavigationModeXid &&
|
||||
!availableNavigationModeIds.has(selectedNavigationModeXid)
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}]: Selected navigation mode does not belong to this activity.`,
|
||||
);
|
||||
}
|
||||
} else if (selectedNavigationModeXid) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}].selectedNavigationModeXid cannot be sent when navigation is not opted.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.length) {
|
||||
if (!itineraryActivity.activity.equipmentAvailable) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}]: Equipment is not available for this activity.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.some((id) => !availableEquipmentIds.has(id))) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
`activities[${index}]: One or more selected equipments do not belong to this activity.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const selection = await tx.itineraryActivitySelection.upsert({
|
||||
where: {
|
||||
itineraryActivityXid_itineraryMemberXid: {
|
||||
itineraryActivityXid: itineraryActivity.id,
|
||||
itineraryMemberXid: itineraryMember.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
itineraryActivityXid: itineraryActivity.id,
|
||||
itineraryMemberXid: itineraryMember.id,
|
||||
isFoodOpted,
|
||||
isTrainerOpted,
|
||||
isInActivityNavigationOpted,
|
||||
activityNavigationModeXid: isInActivityNavigationOpted
|
||||
? selectedNavigationModeXid
|
||||
: null,
|
||||
isActive: true,
|
||||
},
|
||||
update: {
|
||||
isFoodOpted,
|
||||
isTrainerOpted,
|
||||
isInActivityNavigationOpted,
|
||||
activityNavigationModeXid: isInActivityNavigationOpted
|
||||
? selectedNavigationModeXid
|
||||
: null,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.itineraryActivitySelectionFoodType.deleteMany({
|
||||
where: {
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.itineraryActivitySelectionEquipment.deleteMany({
|
||||
where: {
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (selectedFoodTypeIds.length) {
|
||||
await tx.itineraryActivitySelectionFoodType.createMany({
|
||||
data: selectedFoodTypeIds.map((activityFoodTypeXid) => ({
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
activityFoodTypeXid,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.length) {
|
||||
await tx.itineraryActivitySelectionEquipment.createMany({
|
||||
data: selectedEquipmentIds.map((activityEquipmentXid) => ({
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
activityEquipmentXid,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
return tx.itineraryActivitySelection.findUnique({
|
||||
where: {
|
||||
id: selection.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
itineraryActivityXid: true,
|
||||
itineraryMemberXid: true,
|
||||
isFoodOpted: true,
|
||||
isTrainerOpted: true,
|
||||
isInActivityNavigationOpted: true,
|
||||
activityNavigationModeXid: true,
|
||||
selectedFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
activityFoodTypeXid: true,
|
||||
activityFoodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ActivityNavigationModes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
activityNavigationMode: {
|
||||
select: {
|
||||
id: true,
|
||||
navigationModeName: true,
|
||||
@@ -2467,303 +2759,31 @@ export class ItineraryService {
|
||||
navigationModesTotalPrice: true,
|
||||
},
|
||||
},
|
||||
ActivityEquipments: {
|
||||
selectedEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!itineraryActivity) {
|
||||
throw new ApiError(404, 'Itinerary activity not found for this user.');
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.itineraryType !== 'ACTIVITY' ||
|
||||
!itineraryActivity.activityXid ||
|
||||
!itineraryActivity.activity
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'Selections can only be stored for itinerary items linked to an activity.',
|
||||
);
|
||||
}
|
||||
|
||||
const itineraryMember = await tx.itineraryMembers.findFirst({
|
||||
where: {
|
||||
itineraryHeaderXid: itineraryActivity.itineraryHeaderXid,
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!itineraryMember) {
|
||||
throw new ApiError(404, 'Itinerary member record not found.');
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedEquipmentIds must contain valid ids.');
|
||||
}
|
||||
|
||||
if (selectedFoodTypeIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedFoodTypeIds must contain valid ids.');
|
||||
}
|
||||
|
||||
const isFoodOpted = Boolean(payload.isFoodOpted);
|
||||
const isTrainerOpted = Boolean(payload.isTrainerOpted);
|
||||
const isInActivityNavigationOpted = Boolean(
|
||||
payload.isInActivityNavigationOpted,
|
||||
);
|
||||
|
||||
const selectedNavigationModeXid =
|
||||
payload.selectedNavigationModeXid === undefined ||
|
||||
payload.selectedNavigationModeXid === null
|
||||
? null
|
||||
: Number(payload.selectedNavigationModeXid);
|
||||
|
||||
if (
|
||||
selectedNavigationModeXid !== null &&
|
||||
(!Number.isInteger(selectedNavigationModeXid) ||
|
||||
selectedNavigationModeXid <= 0)
|
||||
) {
|
||||
throw new ApiError(400, 'selectedNavigationModeXid must be a valid id.');
|
||||
}
|
||||
|
||||
const availableFoodTypeIds = new Set(
|
||||
itineraryActivity.activity.activityFoodTypes.map((item) => item.id),
|
||||
);
|
||||
const availableNavigationModeIds = new Set(
|
||||
itineraryActivity.activity.ActivityNavigationModes.map((item) => item.id),
|
||||
);
|
||||
const availableEquipmentIds = new Set(
|
||||
itineraryActivity.activity.ActivityEquipments.map((item) => item.id),
|
||||
);
|
||||
|
||||
if (isFoodOpted) {
|
||||
if (!itineraryActivity.activity.foodAvailable) {
|
||||
throw new ApiError(400, 'Food is not available for this activity.');
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.activity.activityFoodTypes.length > 0 &&
|
||||
!selectedFoodTypeIds.length
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedFoodTypeIds is required when food is opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
selectedFoodTypeIds.some((id) => !availableFoodTypeIds.has(id))
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'One or more selected food types do not belong to this activity.',
|
||||
);
|
||||
}
|
||||
} else if (selectedFoodTypeIds.length) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedFoodTypeIds cannot be sent when food is not opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (isTrainerOpted && !itineraryActivity.activity.trainerAvailable) {
|
||||
throw new ApiError(400, 'Trainer is not available for this activity.');
|
||||
}
|
||||
|
||||
if (isInActivityNavigationOpted) {
|
||||
if (!itineraryActivity.activity.inActivityAvailable) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'In-activity navigation is not available for this activity.',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.activity.ActivityNavigationModes.length > 0 &&
|
||||
!selectedNavigationModeXid
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedNavigationModeXid is required when navigation is opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
selectedNavigationModeXid &&
|
||||
!availableNavigationModeIds.has(selectedNavigationModeXid)
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'Selected navigation mode does not belong to this activity.',
|
||||
);
|
||||
}
|
||||
} else if (selectedNavigationModeXid) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedNavigationModeXid cannot be sent when navigation is not opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.length) {
|
||||
if (!itineraryActivity.activity.equipmentAvailable) {
|
||||
throw new ApiError(400, 'Equipment is not available for this activity.');
|
||||
}
|
||||
|
||||
if (
|
||||
selectedEquipmentIds.some((id) => !availableEquipmentIds.has(id))
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'One or more selected equipments do not belong to this activity.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const selection = await tx.itineraryActivitySelection.upsert({
|
||||
where: {
|
||||
itineraryActivityXid_itineraryMemberXid: {
|
||||
itineraryActivityXid: itineraryActivity.id,
|
||||
itineraryMemberXid: itineraryMember.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
itineraryActivityXid: itineraryActivity.id,
|
||||
itineraryMemberXid: itineraryMember.id,
|
||||
isFoodOpted,
|
||||
isTrainerOpted,
|
||||
isInActivityNavigationOpted,
|
||||
activityNavigationModeXid: isInActivityNavigationOpted
|
||||
? selectedNavigationModeXid
|
||||
: null,
|
||||
isActive: true,
|
||||
},
|
||||
update: {
|
||||
isFoodOpted,
|
||||
isTrainerOpted,
|
||||
isInActivityNavigationOpted,
|
||||
activityNavigationModeXid: isInActivityNavigationOpted
|
||||
? selectedNavigationModeXid
|
||||
: null,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.itineraryActivitySelectionFoodType.deleteMany({
|
||||
where: {
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.itineraryActivitySelectionEquipment.deleteMany({
|
||||
where: {
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (selectedFoodTypeIds.length) {
|
||||
await tx.itineraryActivitySelectionFoodType.createMany({
|
||||
data: selectedFoodTypeIds.map((activityFoodTypeXid) => ({
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
activityFoodTypeXid,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.length) {
|
||||
await tx.itineraryActivitySelectionEquipment.createMany({
|
||||
data: selectedEquipmentIds.map((activityEquipmentXid) => ({
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
activityEquipmentXid,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
return tx.itineraryActivitySelection.findUnique({
|
||||
where: {
|
||||
id: selection.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
itineraryActivityXid: true,
|
||||
itineraryMemberXid: true,
|
||||
isFoodOpted: true,
|
||||
isTrainerOpted: true,
|
||||
isInActivityNavigationOpted: true,
|
||||
activityNavigationModeXid: true,
|
||||
selectedFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityFoodTypeXid: true,
|
||||
activityFoodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
activityEquipmentXid: true,
|
||||
activityEquipment: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
activityNavigationMode: {
|
||||
select: {
|
||||
id: true,
|
||||
navigationModeName: true,
|
||||
isInActivityChargeable: true,
|
||||
navigationModesBasePrice: true,
|
||||
navigationModesTotalPrice: true,
|
||||
},
|
||||
},
|
||||
selectedEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityEquipmentXid: true,
|
||||
activityEquipment: {
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user