refactor(shifts): changed to conflictException build for error management for the create function

This commit is contained in:
Matthieu Haineault 2025-11-04 15:59:03 -05:00
parent eda1f86235
commit 95f369fcbc

View File

@ -39,12 +39,15 @@ export class ShiftsUpsertService {
try {
const normed = await this.normalizeShiftDto(dto);
if (normed.end_time <= normed.start_time) {
return {
index,
error: new BadRequestException(
`end_time must be greater than start_time (index ${index})`
),
};
const error = new ConflictException({
error_code: 'SHIFT_OVERLAP',
conflicts: [{
start_time: toStringFromHHmm(normed.start_time),
end_time: toStringFromHHmm(normed.end_time),
date: toStringFromDate(normed.date),
}],
});
return { index, error };
}
const timesheet = await this.prisma.timesheets.findUnique({
@ -52,7 +55,15 @@ export class ShiftsUpsertService {
select: timesheet_select,
});
if (!timesheet) {
return { index, error: new NotFoundException(`Timesheet not found`) };
const error = new ConflictException({
error_code: 'INVALID_TIMESHEET',
conflicts: [{
start_time: toStringFromHHmm(normed.start_time),
end_time: toStringFromHHmm(normed.end_time),
date: toStringFromDate(normed.date),
}],
});
return { index, error };
}
return {
@ -107,21 +118,17 @@ export class ShiftsUpsertService {
)
) {
const error = new ConflictException({
error_code: 'SHIFT_OVERLAP_BATCH',
message: 'New shift overlaps with another shift in the same batch (same day).',
error_code: 'SHIFT_OVERLAP',
conflicts: [{
start_time: toStringFromHHmm(ordered[j].start),
end_time: toStringFromHHmm(ordered[j].end),
date: toStringFromDate(ordered[j].date),
}],
});
return dtos.map((_dto, key) =>
indices.includes(key)
? ({
ok: false,
error
} as CreateShiftResult)
: ({
ok: false,
error: new BadRequestException(
'Batch aborted due to overlaps in another date group'
),
}),
? ({ ok: false, error } as CreateShiftResult)
: ({ ok: false, error }),
);
}
}
@ -139,10 +146,7 @@ export class ShiftsUpsertService {
where: { timesheet_id, date: day_date },
select: { start_time: true, end_time: true, id: true, date: true },
});
existing_map.set(
key,
rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date })),
);
existing_map.set( key, rows.map((row) => ({ start_time: row.start_time, end_time: row.end_time, date: row.date })));
}
normed_shifts.forEach((x, i) => {
@ -158,18 +162,13 @@ export class ShiftsUpsertService {
existing = [];
existing_map.set(map_key, existing);
}
const hit = existing.find(exist => overlaps({
start: exist.start_time, end: exist.end_time, date: exist.date
}, {
start: normed.start_time, end: normed.end_time, date:normed.date
})
);
const hit = existing.find(exist => overlaps({ start: exist.start_time, end: exist.end_time, date: exist.date },
{ start: normed.start_time, end: normed.end_time, date:normed.date}));
if (hit) {
results[index] = {
ok: false,
error: new ConflictException({
error_code: 'SHIFT_OVERLAP',
message: 'New shift overlaps with existing shift(s)',
conflicts: [{
start_time: toStringFromHHmm(hit.start_time),
end_time: toStringFromHHmm(hit.end_time),
@ -248,9 +247,7 @@ export class ShiftsUpsertService {
const updates: UpdateShiftPayload[] = await Promise.all(dtos.map((item) => {
const { id, ...rest } = item;
if (!Number.isInteger(id)) {
throw new BadRequestException('Update shift payload is missing a valid id');
}
if (!Number.isInteger(id)) throw new ConflictException({ error_code: 'INVALID_SHIFT'});
const changes: UpdateShiftChanges = {};
if (rest.date !== undefined) changes.date = rest.date;
@ -338,8 +335,11 @@ export class ShiftsUpsertService {
? ({
ok: false, id: exist.id, error: new ConflictException({
error_code: 'SHIFT_OVERLAP',
message: 'New shift overlaps with existing shift(s)',
conflicts: [{ start_time: toStringFromHHmm(conflict.start), end_time: toStringFromHHmm(conflict.end), type: 'UNKNOWN' }],
conflicts: [{
start_time: toStringFromHHmm(conflict.start),
end_time: toStringFromHHmm(conflict.end),
date: toStringFromDate(conflict.date),
}],
})
} as UpdateShiftResult)
: ({ ok: false, id: exist.id, error: new BadRequestException('Batch aborted due to overlap in another update') })
@ -347,17 +347,34 @@ export class ShiftsUpsertService {
}
}
const regoup_by_day = new Map<string, { id: number; start: Date; end: Date }[]>();
const regoup_by_day = new Map<string, { id: number; start: Date; end: Date; date: Date }[]>();
for (const planned of planned_updates) {
const keys = key(planned.exist_shift.timesheet_id, planned.normed.date);
if (!regoup_by_day.has(keys)) regoup_by_day.set(keys, []);
regoup_by_day.get(keys)!.push({ id: planned.exist_shift.id, start: planned.normed.start_time, end: planned.normed.end_time });
regoup_by_day.get(keys)!.push({
id: planned.exist_shift.id,
start: planned.normed.start_time,
end: planned.normed.end_time,
date: planned.normed.date
});
}
for (const arr of regoup_by_day.values()) {
arr.sort((a, b) => a.start.getTime() - b.start.getTime());
for (let i = 1; i < arr.length; i++) {
if (overlaps({ start: arr[i - 1].start, end: arr[i - 1].end }, { start: arr[i].start, end: arr[i].end })) {
const error = new ConflictException({ error_code: 'SHIFT_OVERLAP_BATCH', message: 'Overlaps between updates within the same day.' });
if (overlaps(
{ start: arr[i - 1].start, end: arr[i - 1].end, date: arr[i - 1].date },
{ start: arr[i].start, end: arr[i].end, date: arr[i].date })
) {
const error = new ConflictException({
error_code: 'SHIFT_OVERLAP',
conflicts: [{
start_time: toStringFromHHmm(arr[i].start),
end_time: toStringFromHHmm(arr[i].end),
date: toStringFromDate(arr[i].date),
}],
});
return updates.map(exist => ({ ok: false, id: exist.id, error: error }));
}
}