Skip to content

Commit

Permalink
Merge pull request #1401 from Shelf-nu/1369-bug-overdue-status-revision
Browse files Browse the repository at this point in the history
fix: handing the case of checking out a booking after its end period has p…
  • Loading branch information
DonKoko authored Nov 11, 2024
2 parents a95754d + 5f97bf2 commit 2637182
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 9 deletions.
14 changes: 14 additions & 0 deletions app/components/booking/form.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useMemo } from "react";
import { useLoaderData, useNavigation } from "@remix-run/react";
import { useAtom } from "jotai";
import { DateTime } from "luxon";
import { useZorm } from "react-zorm";
import { z } from "zod";
import { updateDynamicTitleAtom } from "~/atoms/dynamic-title-atom";
Expand Down Expand Up @@ -172,14 +174,26 @@ export function BookingForm({
action: PermissionAction.checkout,
});

/** Checks if this booking is already exipred */
const isExpired = useMemo(() => {
if (!endDate) return false;
const end = DateTime.fromISO(endDate);
const now = DateTime.now();
return end < now;
}, [endDate]);

/** This is used when we have selfSErvice or Base as we are setting the default */
const defaultTeamMember = rawTeamMembers?.find(
(m) => m.userId === custodianUserId
);


return (
<div>
<Form ref={zo.ref} method="post">
{/* Hidden input for expired state. Helps is know what status we should set on the server, when the booking is getting checked out */}
{isExpired && <input type="hidden" name="isExpired" value="true" />}

{/* Render the actions on top only when the form is in edit mode */}
{!isNewBooking ? (
<AbsolutePositionedHeaderActions>
Expand Down
19 changes: 12 additions & 7 deletions app/modules/booking/service.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export async function upsertBooking(
| "custodianTeamMemberId"
| "custodianUserId"
| "description"
> & { assetIds: Asset["id"][] }
> & { assetIds: Asset["id"][]; isExpired: boolean }
>,
hints: ClientHint,
isBaseOrSelfService: boolean = false
Expand All @@ -174,6 +174,7 @@ export async function upsertBooking(
custodianUserId,
id,
description,
isExpired,
...rest
} = booking;
let data: Prisma.BookingUpdateInput = { ...rest };
Expand Down Expand Up @@ -311,14 +312,14 @@ export async function upsertBooking(
});

if (
// For both regular checkouts (ONGOING) and expired checkouts (OVERDUE)
((booking.status === BookingStatus.ONGOING ||
booking.status === BookingStatus.OVERDUE) &&
isExpired) ||
booking.status === BookingStatus.ONGOING ||
(res.status === BookingStatus.ONGOING && booking.assetIds?.length)
) {
//booking status is updated to ongoing or assets added to ongoing booking, make asset checked out
//no need to worry about overdue as the previous state is always ongoing
newAssetStatus = AssetStatus.CHECKED_OUT;

// If booking has some kits, then make them checked out
if (hasKits) {
newKitStatus = AssetStatus.CHECKED_OUT;
}
Expand All @@ -338,7 +339,11 @@ export async function upsertBooking(
);
}

if (res.from && booking.status === BookingStatus.RESERVED) {
if (
res.from &&
booking.status === BookingStatus.RESERVED &&
!booking.isExpired //Only schedule if the booking is not already
) {
promises.push(cancelScheduler(res));
const when = new Date(res.from);
when.setHours(when.getHours() - 1); //1hour before send checkout reminder
Expand Down Expand Up @@ -485,7 +490,7 @@ export async function upsertBooking(
data: data as Prisma.BookingCreateInput,
include: { ...commonInclude, organization: true },
});
if (res.from && booking.status === BookingStatus.RESERVED) {
if (res.from && booking.status === BookingStatus.RESERVED && !isExpired) {
await cancelScheduler(res);
const when = new Date(res.from);
when.setHours(when.getHours() - 1); //1hour before send checkout reminder
Expand Down
22 changes: 20 additions & 2 deletions app/routes/_layout+/bookings.$bookingId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,22 @@ export async function action({ context, request, params }: ActionFunctionArgs) {
checkOut: BookingStatus.ONGOING,
checkIn: BookingStatus.COMPLETE,
};
// Parse the isExpired field
const { isExpired } = parseData(
formData,
z.object({
isExpired: z
.string()
.optional()
.transform((val) => val === "true"),
})
);
// Modify status if expired during checkout
const status =
intent === "checkOut" && isExpired
? BookingStatus.OVERDUE
: intentToStatusMap[intent];

let upsertBookingData = {
organizationId,
id,
Expand Down Expand Up @@ -365,9 +381,11 @@ export async function action({ context, request, params }: ActionFunctionArgs) {

// Add the status if it exists
Object.assign(upsertBookingData, {
...(intentToStatusMap[intent] && {
status: intentToStatusMap[intent],
...(status && {
status,
}),
// Make sure to pass isExpired when checking out
...(intent === "checkOut" && { isExpired }),
});
// Update and save the booking
const booking = await upsertBooking(
Expand Down

0 comments on commit 2637182

Please sign in to comment.