From 03ff8b402f01b6a7eb53aa3b049386bd38640ecc Mon Sep 17 00:00:00 2001 From: TobiZog Date: Tue, 24 Sep 2024 15:40:16 +0200 Subject: [PATCH] Implement ordering process --- software/backend/routes/order.routes.ts | 22 +++--- software/backend/scripts/databaseHelper.ts | 10 ++- software/backend/server.ts | 6 ++ software/src/data/api/mainApi.ts | 12 +++ software/src/data/api/orderApi.ts | 15 +++- software/src/data/models/basketItemModel.ts | 5 ++ ...{orderedItemModel.ts => orderItemModel.ts} | 6 +- software/src/data/models/orderModel.ts | 5 +- software/src/data/stores/accountStore.ts | 27 +++++-- software/src/data/stores/basketStore.ts | 52 +++++++++---- software/src/locales/de.json | 6 +- software/src/locales/en.json | 6 +- software/src/pages/basketPage/index.vue | 39 +++++----- .../src/pages/basketPage/orderingDialog.vue | 35 ++++++++- .../src/pages/basketPage/productsTable.vue | 30 +++++--- software/src/pages/ordersPage/ordersCard.vue | 77 ++++++++++--------- .../src/pages/preferencesPage/systemSetup.vue | 43 +++++++++-- .../productsPage/productDetailsDialog.vue | 6 +- software/src/scripts/productScripts.ts | 32 -------- 19 files changed, 274 insertions(+), 160 deletions(-) create mode 100644 software/src/data/api/mainApi.ts rename software/src/data/models/{orderedItemModel.ts => orderItemModel.ts} (67%) diff --git a/software/backend/routes/order.routes.ts b/software/backend/routes/order.routes.ts index 5ac82cc..852379a 100644 --- a/software/backend/routes/order.routes.ts +++ b/software/backend/routes/order.routes.ts @@ -4,6 +4,7 @@ import { Product } from "../models/product.model"; import { OrderItem } from "../models/orderItem.model"; import { Brand } from "../models/brand.model"; import { Category } from "../models/category.model"; +import { Sequelize } from "sequelize-typescript"; export const order = Router() @@ -36,25 +37,20 @@ order.get("/:id", (req: Request, res: Response) => { // Place a new order order.post("/", (req: Request, res: Response) => { - let totalPrice = 0 - Order.create(req.body) .then(async order => { for (let orderItem of req.body.orderItems) { OrderItem.create({ - "orderId": order.id, - "quantity": orderItem.quantity, - "orderPrice": orderItem.orderPrice, - "productId": orderItem.productId + orderId: order.id, + quantity: orderItem.quantity, + orderPrice: orderItem.orderPrice, + productId: orderItem.productId }) - totalPrice += orderItem.quantity * orderItem.orderPrice - - Order.update({ - totalPrice: totalPrice - }, { - where: { id: order.id } - }) + Product.decrement( + "inStock", + { where: { id: orderItem.productId } } + ) } // Created diff --git a/software/backend/scripts/databaseHelper.ts b/software/backend/scripts/databaseHelper.ts index bb1fac2..07e73d8 100644 --- a/software/backend/scripts/databaseHelper.ts +++ b/software/backend/scripts/databaseHelper.ts @@ -20,11 +20,17 @@ import brands from "./../data/brands.json" * Delete all datasets in every database table */ export function deleteAllTables() { - Category.destroy({ truncate: true }) - Order.destroy({ truncate: true }) OrderItem.destroy({truncate: true }) + Order.destroy({ truncate: true }) + Product.destroy({ truncate: true }) + Brand.destroy({ truncate: true }) + Category.destroy({ truncate: true }) + + Address.destroy({ truncate: true }) + Payment.destroy({ truncate: true }) Account.destroy({ truncate: true }) + AccountRole.destroy({ truncate: true}) } /** diff --git a/software/backend/server.ts b/software/backend/server.ts index e258bd4..e012081 100644 --- a/software/backend/server.ts +++ b/software/backend/server.ts @@ -20,6 +20,12 @@ app.use(bodyParser.json()) // Create database and tables startDatabase() +// Add delay for more realistic response times +app.use((req, res, next) => { + console.log(123) + setTimeout(next, Math.floor((Math.random () * 4000) + 100)) +}) + // Routes app.use("/api", api) app.use("/categories", category) diff --git a/software/src/data/api/mainApi.ts b/software/src/data/api/mainApi.ts new file mode 100644 index 0000000..b4413a9 --- /dev/null +++ b/software/src/data/api/mainApi.ts @@ -0,0 +1,12 @@ +import axios from "axios" + +const BASE_URL = "http://localhost:3000/api" + +export function getServerState() { + return axios.get(BASE_URL) + +} + +export function resetDatabase() { + return axios.get(BASE_URL + "/resetdatabase") +} \ No newline at end of file diff --git a/software/src/data/api/orderApi.ts b/software/src/data/api/orderApi.ts index eb01f33..c2ca64a 100644 --- a/software/src/data/api/orderApi.ts +++ b/software/src/data/api/orderApi.ts @@ -1,6 +1,7 @@ import axios from "axios" import { OrderModel } from "../models/orderModel" import { BasketItemModel } from "../models/basketItemModel" +import { calcPrice } from "@/scripts/productScripts" const BASE_URL = "http://localhost:3000/orders" @@ -9,8 +10,18 @@ export async function getUserOrders(userId: number) { } export async function addOrder(accountId: number, basketItems: Array) { + let orderItems = [] + + for (let basketItem of basketItems) { + orderItems.push({ + productId: basketItem.product.id, + quantity: basketItem.quantity, + orderPrice: calcPrice(basketItem.product.price, basketItem.product.discount) + }) + } + return axios.post(BASE_URL, { - "accountId": accountId, - "orderItem": basketItems + accountId: accountId, + orderItems: orderItems }) } \ No newline at end of file diff --git a/software/src/data/models/basketItemModel.ts b/software/src/data/models/basketItemModel.ts index cb41176..b9bd578 100644 --- a/software/src/data/models/basketItemModel.ts +++ b/software/src/data/models/basketItemModel.ts @@ -4,4 +4,9 @@ export class BasketItemModel { id: number = -1 quantity: number = 1 product: ProductModel = new ProductModel() + + constructor(quantity: number, product: ProductModel) { + this.quantity = quantity + this.product = product + } } \ No newline at end of file diff --git a/software/src/data/models/orderedItemModel.ts b/software/src/data/models/orderItemModel.ts similarity index 67% rename from software/src/data/models/orderedItemModel.ts rename to software/src/data/models/orderItemModel.ts index 2983f12..bdf081e 100644 --- a/software/src/data/models/orderedItemModel.ts +++ b/software/src/data/models/orderItemModel.ts @@ -1,8 +1,8 @@ import { ProductModel } from "./productModel" -export class OrderedItemModel { +export class OrderItemModel { orderId: number = -1 - product: ProductModel quantity: number = 1 - totalPrice: number = 0 + orderPrice: number = 0 + product: ProductModel } \ No newline at end of file diff --git a/software/src/data/models/orderModel.ts b/software/src/data/models/orderModel.ts index 29666df..d4b0dac 100644 --- a/software/src/data/models/orderModel.ts +++ b/software/src/data/models/orderModel.ts @@ -1,8 +1,9 @@ -import { OrderedItemModel } from "./orderedItemModel" +import { OrderItemModel } from "./orderItemModel" export class OrderModel { + id: number accountId: number shippingProgress: number - orderItems: Array + orderItems: Array orderedAt: string } \ No newline at end of file diff --git a/software/src/data/stores/accountStore.ts b/software/src/data/stores/accountStore.ts index d0cf73e..47a7fe4 100644 --- a/software/src/data/stores/accountStore.ts +++ b/software/src/data/stores/accountStore.ts @@ -6,6 +6,7 @@ import { useFeedbackStore } from "./feedbackStore"; import { loginAccount, registerAccount, updateAccount } from "../api/accountApi"; import { getUserOrders } from "../api/orderApi"; import { BannerStateEnum } from "../enums/bannerStateEnum"; +import { calcPrice } from "@/scripts/productScripts"; export const useAccountStore = defineStore("accountStore", { state: () => ({ @@ -20,18 +21,12 @@ export const useAccountStore = defineStore("accountStore", { await loginAccount(username, password) .then(async result => { this.userAccount = result.data - this.loggedIn = true feedbackStore.changeBanner(BannerStateEnum.ACCOUNTLOGINSUCCESSFUL) - await getUserOrders(result.data.id) - .then(result => { - this.orders = result.data - }) + this.refreshOrders() }) .catch(error => { - this.loggedIn = false - if (error.status == 400) { feedbackStore.changeBanner(BannerStateEnum.ACCOUNTLOGINERROR) } else if (error.status == 401) { @@ -76,6 +71,24 @@ export const useAccountStore = defineStore("accountStore", { this.loggedIn = false feedbackStore.changeBanner(BannerStateEnum.ACCOUNTLOGOUTSUCCESSFUL) + }, + + async refreshOrders() { + await getUserOrders(this.userAccount.id) + .then(result => { + this.orders = result.data + }) + }, + + getOrderTotalPrice(orderId: number) { + let totalPrice = 0 + let order: OrderModel = this.orders.find((order: OrderModel) => order.id == orderId) + + for (let item of order.orderItems) { + totalPrice += calcPrice(item.orderPrice, 0, item.quantity) + } + + return Math.round(totalPrice * 100) / 100 } } }) \ No newline at end of file diff --git a/software/src/data/stores/basketStore.ts b/software/src/data/stores/basketStore.ts index 004ca85..b3a32ab 100644 --- a/software/src/data/stores/basketStore.ts +++ b/software/src/data/stores/basketStore.ts @@ -1,11 +1,13 @@ import { defineStore } from "pinia"; import { useLocalStorage } from "@vueuse/core"; -import { calcProductPrice } from "@/scripts/productScripts"; +import { calcPrice } from "@/scripts/productScripts"; import { BasketItemModel } from "../models/basketItemModel"; import { useFeedbackStore } from "./feedbackStore"; import { BannerStateEnum } from "../enums/bannerStateEnum"; import { addOrder } from "../api/orderApi"; import { useAccountStore } from "./accountStore"; +import { ProductModel } from "../models/productModel"; +import { useProductStore } from "./productStore"; export const useBasketStore = defineStore('basketStore', { state: () => ({ @@ -13,11 +15,16 @@ export const useBasketStore = defineStore('basketStore', { }), getters: { + /** + * Calculate price of all items in the basket with discount + * + * @returns Total price of basket + */ getTotalPrice() { let result = 0 for (let item of this.itemsInBasket) { - result += calcProductPrice(item, item.quantity) + result += calcPrice(item.product.price, item.product.discount, item.quantity) } return Math.round(result * 100) / 100 @@ -25,6 +32,11 @@ export const useBasketStore = defineStore('basketStore', { }, actions: { + /** + * Remove an item from the basket + * + * @param item Item to remove + */ removeItemFromBasket(item: BasketItemModel) { const feedbackStore = useFeedbackStore() feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTREMOVED) @@ -34,29 +46,39 @@ export const useBasketStore = defineStore('basketStore', { ) }, - addItemToBasket(item: BasketItemModel) { + /** + * Add an item to the basket. If the product is already in the basket, the quantity will increase + * + * @param product Product to add + * @param quantity Quantity of the product + */ + addItemToBasket(product: ProductModel, quantity: number) { const feedbackStore = useFeedbackStore() feedbackStore.changeBanner(BannerStateEnum.BASKETPRODUCTADDED) // Product is already in the basket, increase number of items - if (this.itemsInBasket.find((basketItem) => basketItem.productId == item.product.id)) { - this.itemsInBasket.find((basketItem) => - basketItem.productId == item.product.id).quantity += item.quantity + if (this.itemsInBasket.find((basketItem: BasketItemModel) => + basketItem.product.id == product.id)) + { + this.itemsInBasket.find((basketItem: BasketItemModel) => + basketItem.product.id == product.id).quantity += quantity } else { - this.itemsInBasket.push(item) + this.itemsInBasket.push(new BasketItemModel(quantity, product)) } }, - takeOrder() { + /** + * Take an order to the server. Sends all articles in the basket and creates an order entry in the backend database + */ + async takeOrder() { const accountStore = useAccountStore() -// - // const order = new OrderModel() - // order.accountId = userStore.userAccount.id - // order.orderItem = this.itemsInBasket -// - // console.log(order) + const productStore = useProductStore() - addOrder(accountStore.userAccount.id, this.itemsInBasket) + await addOrder(accountStore.userAccount.id, this.itemsInBasket) + this.itemsInBasket = [] + + await accountStore.refreshOrders() + await productStore.fetchAllProducts() } } }) diff --git a/software/src/locales/de.json b/software/src/locales/de.json index 1d0cf12..33f31f6 100644 --- a/software/src/locales/de.json +++ b/software/src/locales/de.json @@ -22,8 +22,7 @@ "resetConfirm": "Soll die Datenbank wirklich zurückgesetzt werden?" }, "product": { - "product": "Produkt", - "products": "Produkte", + "product": "Produkt | Produkte", "productName": "Produkt Name", "brand": "Marke", "productPrice": "Einzelpreis", @@ -115,5 +114,6 @@ "exerciseGroup2": "Aufgabengruppe 2: Broken Access Control", "exerciseGroup3": "Aufgabengruppe 3: Cross-Site Scripting (XSS)", "exercise": "Aufgabe {0}" - } + }, + "serverState": "Server Status" } diff --git a/software/src/locales/en.json b/software/src/locales/en.json index 6e6a46b..e56d37a 100644 --- a/software/src/locales/en.json +++ b/software/src/locales/en.json @@ -22,8 +22,7 @@ "resetConfirm": "Really reset the database?" }, "product": { - "product": "Product", - "products": "Products", + "product": "Product | Products", "productName": "Product name", "brand": "Brand", "productPrice": "Unit price", @@ -115,5 +114,6 @@ "exerciseGroup2": "Exercise Group 2: Broken Access Control", "exerciseGroup3": "Exercise Group 3: Cross-Site Scripting (XSS)", "exercise": "Exercise {0}" - } + }, + "serverState": "Server State:" } diff --git a/software/src/pages/basketPage/index.vue b/software/src/pages/basketPage/index.vue index 5d4e92f..7a7b6c4 100644 --- a/software/src/pages/basketPage/index.vue +++ b/software/src/pages/basketPage/index.vue @@ -22,27 +22,28 @@ const showOrderingDialog = ref() - - -
- {{ basketStore.itemsInBasket.length }} {{ $t('product.product') }} -
-
- {{ basketStore.itemsInBasket.length }} {{ $t('product.products') }} -
-
+ +