diff --git a/src/common/models.ts b/src/common/models.ts index 9df77b9..347ed9d 100644 --- a/src/common/models.ts +++ b/src/common/models.ts @@ -8,7 +8,7 @@ import { MentorOfficeHours } from "../services/mentor/mentor-schemas"; import { Event, EventAttendance, EventFollowers } from "../services/event/event-schemas"; import { NewsletterSubscription } from "../services/newsletter/newsletter-schemas"; import { RegistrationApplication } from "../services/registration/registration-schemas"; -import { ShopItem } from "../services/shop/shop-schemas"; +import { ShopItem, ShopOrder } from "../services/shop/shop-schemas"; import { UserAttendance, UserFollowing, UserInfo } from "../services/user/user-schemas"; import { AnyParamConstructor, IModelOptions } from "@typegoose/typegoose/lib/types"; import { StaffShift } from "../services/staff/staff-schemas"; @@ -73,6 +73,7 @@ enum RegistrationCollection { enum ShopCollection { ITEMS = "items", + ORDERS = "orders" } enum StaffCollection { @@ -152,6 +153,7 @@ export default class Models { // Shop static ShopItem: Model = getModel(ShopItem, Group.SHOP, ShopCollection.ITEMS); + static ShopOrder: Model = getModel(ShopOrder, Group.SHOP, ShopCollection.ORDERS); // Staff static StaffShift: Model = getModel(StaffShift, Group.STAFF, StaffCollection.SHIFT); diff --git a/src/services/shop/shop-router.ts b/src/services/shop/shop-router.ts index 89d27c2..5304ff9 100644 --- a/src/services/shop/shop-router.ts +++ b/src/services/shop/shop-router.ts @@ -14,6 +14,11 @@ import { ShopItemSchema, ShopItemsSchema, ShopItemUpdateRequestSchema, + ShopItemGenerateOrderSchema, + ShopItemFulfillOrderSchema, + SuccessSchema, + ShopOrder, + OrderQRCodesSchema, } from "./shop-schemas"; import { Router } from "express"; import { StatusCode } from "status-code-enum"; @@ -188,7 +193,7 @@ shopRouter.get( method: "get", path: "/shop/item/qr/{id}/", tag: Tag.SHOP, - role: Role.STAFF, + role: null, //Role.STAFF summary: "Gets the QR codes for a shop item", parameters: z.object({ id: ShopItemIdSchema, @@ -218,6 +223,130 @@ shopRouter.get( }, ); + +shopRouter.post( + "/item/generateorder", + specification({ + method: "post", + path: "/shop/item/generateorder/", + tag: Tag.SHOP, + role: null, + summary: "Generates order and returns qr code", + body: ShopItemGenerateOrderSchema, + responses: { + [StatusCode.SuccessOK]: { + description: "The qr codes", + schema: OrderQRCodesSchema, + }, + [StatusCode.ClientErrorNotFound]: { + description: "Item doesn't exist", + schema: ShopItemNotFoundErrorSchema, + }, + [StatusCode.ClientErrorBadRequest]: { + description: "Not enough quantity in shop", + schema: ShopInsufficientFundsErrorSchema, //potentially change + }, + }, + }), + async (req, res) => { + const body = req.body; + const items = body.items; + const quantity = body.quantity + + + //const body = ShopItemGenerateOrderSchema.parse(req.body) + //const { items, quantity } = body; + + for(let i = 0; i < items.length; i++) { + //items[i] is the _id of the items + const item = await Models.ShopItem.findOne({ itemId: items[i] }); + + if (!item) { + return res.status(StatusCode.ClientErrorNotFound).send(ShopItemNotFoundError); + } + + const q = quantity?.[i] as number | undefined; + if(q == undefined || item.quantity < q) { + // send which item there isn't enough of ? + return res.status(StatusCode.ClientErrorNotFound).send(ShopInsufficientFundsError); + } + } + + //have availability of all item so can generate qr code with order number + const RAND_NUM = 10; + const order = Math.floor(Math.random() * RAND_NUM); + const qrCodeUrl = `hackillinois://ordernum?orderNum=${order}`; + + const shopOrder: ShopOrder = { + orderNum: order, + items: items, + quantity: quantity, + }; + + await Models.ShopOrder.create(shopOrder); + + return res.status(StatusCode.SuccessOK).send({ qrInfo: qrCodeUrl }); + }, +); + +shopRouter.post( + "/item/fulfillorder", + specification({ + method: "post", + path: "/shop/item/fulfillorder/", + tag: Tag.SHOP, + role: null, + summary: "Purchases the order item", + body: ShopItemFulfillOrderSchema, + responses: { + [StatusCode.SuccessOK]: { + description: "The successfully purchased order", + schema: SuccessSchema, + }, + [StatusCode.ClientErrorNotFound]: { + description: "Order doesn't exist", + schema: ShopItemNotFoundErrorSchema, + }, + }, + }), + async (req, res) => { + // when qr code is scanned, will call this so body needs to have order num and then i use that + // to get the order and then for each item in the order, subtract the quantity and then return success + const body = req.body; + const num = body.orderNum; + + const order = await Models.ShopOrder.findOne({ orderNum: num }); + + if(!order) { + return res.status(StatusCode.ClientErrorNotFound).send(ShopItemNotFoundError); + } + + for(let i = 0; i < order.items.length; i++) { + const item = await Models.ShopItem.findOne({ itemId: order.items[i] }); + + if(!item) { + return res.status(StatusCode.ClientErrorNotFound).send(ShopItemNotFoundError); + } + + const q = order.quantity?.[i] as number | 0; + + const updatedItem = await Models.ShopItem.findOneAndUpdate({ itemId: order.items[i] }, body, { + quantity: item.quantity - q, + }); + + if (!updatedItem) { + return res.status(StatusCode.ClientErrorNotFound).send(ShopItemNotFoundError); + } + + //item.quantity = item.quantity - q; + //await item.save(); + } + + return res.status(StatusCode.SuccessOK).json({ message: "success" }); + }, +); + + shopRouter.post( "/item/buy", specification({ diff --git a/src/services/shop/shop-schemas.ts b/src/services/shop/shop-schemas.ts index 2303d43..2ed9740 100644 --- a/src/services/shop/shop-schemas.ts +++ b/src/services/shop/shop-schemas.ts @@ -46,7 +46,33 @@ export class ShopItem { } } +export class ShopOrder { + @prop({ required: true }) + public orderNum: number + @prop({ required: true }) + public items: Array + + @prop({ required: true }) + public quantity: Array + + constructor( + orderNum: number, + items: Array, + quantity: Array, + ) { + this.orderNum = orderNum; + this.items = items; + this.quantity = quantity; + } +} + export const ShopItemIdSchema = z.string().openapi("ShopItemId", { example: "item1234" }); +// export const ShopOrderArraySchema = z +// .tuple([ +// z.array(z.string()), +// z.array(z.number()), +// ]) +// .openapi("ShopOrderArray", { example: [["item1234", "item5678"], [1, 2]] }); export const ShopItemSchema = z .object({ @@ -112,6 +138,30 @@ export const ShopItemBuyRequestSchema = z.object({ instance: z.string().openapi({ example: "1x3" }), }); +// needs to have list of items and quantity +export const ShopItemGenerateOrderSchema = z.object({ + items: z.array(z.string()), + quantity: z.array(z.number()), +}); + +export const ShopItemFulfillOrderSchema = z.object({ + orderNum: z.number(), +}); + +export const OrderQRCodeSchema = z.string().openapi("OrderQRCode", { + example: "hackillinois://ordernum?orderNum=10", +}); + +export const OrderQRCodesSchema = z + .object({ + qrInfo: z.string(OrderQRCodeSchema), + }) + .openapi("OrderQRCodes"); + +export const SuccessSchema = z.object({ + message: z.string(), +}).openapi("Success"); + export const [ShopItemAlreadyExistsError, ShopItemAlreadyExistsErrorSchema] = CreateErrorAndSchema({ error: "AlreadyExists", message: "An item with that id already exists, did you mean to update it instead?",