From f67a23bab9c67a88de76114c83700c745af5375c Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 1 May 2020 12:19:52 +0200 Subject: [PATCH 001/203] ShopIndexController initial implementation --- .../hso/ecommerce/app/RequestController.java | 46 +++++------ .../controller/shop/ShopIndexController.java | 77 ++++++++++++++++++- .../main/resources/templates/shop/index.html | 53 ++----------- 3 files changed, 105 insertions(+), 71 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 3602236..defeb1c 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -22,10 +22,10 @@ public class RequestController { static int notSoRandom = 0; - @GetMapping("/") - public String home() { - return "redirect:/shop/"; - } +// @GetMapping("/") +// public String home() { +// return "redirect:/shop/"; +// } @GetMapping("/login") public String login() { @@ -43,7 +43,7 @@ public class RequestController { String gto = (String) session.getAttribute("afterLogin"); Optional user = userRepository.findByEmail(username); - if (user.isEmpty()) { + if (!user.isPresent()) { request.setAttribute("error", "Email Adresse falsch."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "login"; @@ -88,10 +88,10 @@ public class RequestController { return "redirect:/"; } - @GetMapping("/shop/") - public String shop() { - return "shop/index"; - } +// @GetMapping("/shop/") +// public String shop() { +// return "shop/index"; +// } @GetMapping("/shop/search") public String shopSearch() { @@ -137,20 +137,20 @@ public class RequestController { } } - @GetMapping("/about") - public String about() { - return "about"; - } - - @GetMapping("/terms") - public String terms() { - return "terms"; - } - - @GetMapping("/privacy") - public String privacy() { - return "privacy"; - } +// @GetMapping("/about") +// public String about() { +// return "about"; +// } +// +// @GetMapping("/terms") +// public String terms() { +// return "terms"; +// } +// +// @GetMapping("/privacy") +// public String privacy() { +// return "privacy"; +// } @GetMapping("/intern/") diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 0f423db..5d442d6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -1,8 +1,83 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.entities.shop.Article; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; @Controller -//@RequestMapping("...") +@RequestMapping("/") public class ShopIndexController { + + @GetMapping("/") + public String home() { + return "redirect:/shop/"; + } + + @GetMapping("/shop/") + public String shop(Model model) { + + ArrayList
dummyArticles = getArticles(); + + model.addAttribute("articles", dummyArticles); + return "shop/index"; + } + + @GetMapping("/about") + public String about() { + return "about"; + } + + @GetMapping("/terms") + public String terms() { + return "terms"; + } + + @GetMapping("/privacy") + public String privacy() { + return "privacy"; + } + + + + public ArrayList
getArticles(){ + ArrayList
dummyArticles = new ArrayList
(); + + Article d1 = new Article(); + d1.description = "this is dummy1"; + d1.title = "dummy1"; + d1.shopPricePerUnitNetCent = 1500; + d1.id = 1234; + dummyArticles.add(d1); + + Article d2 = new Article(); + d2.description = "this is dummy2"; + d2.title = "dummy2"; + d2.shopPricePerUnitNetCent = 2000; + d2.id = 2345; + dummyArticles.add(d2); + + Article d3 = new Article(); + d3.description = "this is dummy3"; + d3.title = "dummy3"; + d3.shopPricePerUnitNetCent = 2500; + d3.id = 3456; + dummyArticles.add(d3); + + Article d4 = new Article(); + d4.description = "this is dummy4"; + d4.title = "dummy4"; + d4.shopPricePerUnitNetCent = 3000; + d4.id = 4567; + dummyArticles.add(d4); + + return dummyArticles; + } + + } diff --git a/prototype/src/main/resources/templates/shop/index.html b/prototype/src/main/resources/templates/shop/index.html index 68a4a1f..a019c8a 100644 --- a/prototype/src/main/resources/templates/shop/index.html +++ b/prototype/src/main/resources/templates/shop/index.html @@ -18,54 +18,13 @@
-
+
+ - -

Lorem Ipsum

-

25.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

10.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

25.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

10.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

44.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

+ +

+

+

From 8f081823ca24b5f97ee4ca4d86ef80c0af29b38a Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 1 May 2020 14:40:43 +0200 Subject: [PATCH 002/203] ShopIndexController without databasestuff --- .../controller/shop/ShopIndexController.java | 31 ++++++++++++++++--- .../main/resources/templates/shop/index.html | 26 ++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 5d442d6..0be1a50 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -6,6 +6,7 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpSession; import javax.swing.*; import java.awt.*; import java.util.ArrayList; @@ -20,11 +21,32 @@ public class ShopIndexController { } @GetMapping("/shop/") - public String shop(Model model) { + public String shop(Model model, HttpSession session) { - ArrayList
dummyArticles = getArticles(); + //TODO: get commercialised Articles + ArrayList
commercialArticles = getArticles(); + model.addAttribute("commercialArticles", commercialArticles); + + //check if logged in + boolean isLoggedIn = false; + boolean hasOrders = false; + if (session != null && session.getAttribute("id") != null) { + long userId = (long) session.getAttribute("id"); + isLoggedIn = true; + if (false) { + hasOrders = true; //TODO: Find out whether user has orders! + } + } + model.addAttribute("isLoggedIn", isLoggedIn); + model.addAttribute("hasOrders", hasOrders); + + + if (hasOrders) { + //TODO: get up to last 4 Orders + ArrayList
suggestedArticles = getArticles(); + model.addAttribute("suggestedArticles", suggestedArticles); + } - model.addAttribute("articles", dummyArticles); return "shop/index"; } @@ -44,8 +66,7 @@ public class ShopIndexController { } - - public ArrayList
getArticles(){ + public ArrayList
getArticles() { ArrayList
dummyArticles = new ArrayList
(); Article d1 = new Article(); diff --git a/prototype/src/main/resources/templates/shop/index.html b/prototype/src/main/resources/templates/shop/index.html index a019c8a..c3c7535 100644 --- a/prototype/src/main/resources/templates/shop/index.html +++ b/prototype/src/main/resources/templates/shop/index.html @@ -18,7 +18,7 @@
-
+
@@ -38,7 +38,9 @@
From f48f5c40f0486059f319ab7b55a34b75caef2f4b Mon Sep 17 00:00:00 2001 From: Hannes Date: Sat, 2 May 2020 14:38:40 +0200 Subject: [PATCH 003/203] ShopArticleController initial --- .../hso/ecommerce/app/RequestController.java | 46 ++++++------- .../shop/ShopArticleController.java | 69 ++++++++++++++++++- .../resources/templates/shop/articles/id.html | 43 ++++-------- 3 files changed, 105 insertions(+), 53 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 3602236..48c4f18 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -43,7 +43,7 @@ public class RequestController { String gto = (String) session.getAttribute("afterLogin"); Optional user = userRepository.findByEmail(username); - if (user.isEmpty()) { + if (!user.isPresent()) { request.setAttribute("error", "Email Adresse falsch."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "login"; @@ -114,28 +114,28 @@ public class RequestController { return "shop/checkoutFinish"; } - @GetMapping("/shop/articles/{id}") - public String shopArticlesById() { - return "shop/articles/id"; - } - - @PostMapping("/shop/articles/{id}") - public String shopArticlesByIdBuy(HttpSession session, - @RequestAttribute(value = "user", required = false) User customer, - @PathVariable("id") Integer id, - @RequestParam("fastcheckout") Boolean fastcheckout - ) { - if (customer != null) { - if (!fastcheckout) { - return "shop/articles/post_add"; - } else { - return "shop/checkout"; - } - } else { - session.setAttribute("afterLogin", "/shop/articles/" + id); - return "redirect:/login"; - } - } +// @GetMapping("/shop/articles/{id}") +// public String shopArticlesById() { +// return "shop/articles/id"; +// } +// +// @PostMapping("/shop/articles/{id}") +// public String shopArticlesByIdBuy(HttpSession session, +// @RequestAttribute(value = "user", required = false) User customer, +// @PathVariable("id") Integer id, +// @RequestParam("fastcheckout") Boolean fastcheckout +// ) { +// if (customer != null) { +// if (!fastcheckout) { +// return "shop/articles/post_add"; +// } else { +// return "shop/checkout"; +// } +// } else { +// session.setAttribute("afterLogin", "/shop/articles/" + id); +// return "redirect:/login"; +// } +// } @GetMapping("/about") public String about() { diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 27ccf63..653286e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -1,8 +1,75 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.user.User; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpSession; +import java.util.ArrayList; +import java.util.List; @Controller -//@RequestMapping("...") +@RequestMapping("/shop/articles") public class ShopArticleController { + + @GetMapping("/{id}") + public String shopArticlesById(Model model, @PathVariable("id") Integer Id) { + + //TODO: Get Article by Id instead of this dummy shit + Article d1 = new Article(); + d1.description = "this is dummy1"; + d1.title = "dummy1"; + d1.shopPricePerUnitNetCent = 1500; + d1.id = 1234; + + model.addAttribute("article", d1); + + //TODO: Check if in Stock + if(false){ + model.addAttribute("inStock", true); + }else{ + model.addAttribute("inStock", false); + } + + //TODO: Get 2 Commercialised Articles + List
commercialArticles = new ArrayList
(); + Article d2 = new Article(); + d2.description = "this is dummy2"; + d2.title = "dummy2"; + d2.shopPricePerUnitNetCent = 2000; + d2.id = 2345; + Article d3 = new Article(); + d3.description = "this is dummy3"; + d3.title = "dummy3"; + d3.shopPricePerUnitNetCent = 2500; + d3.id = 3456; + commercialArticles.add(d2); + commercialArticles.add(d3); + model.addAttribute("commercialArticles", commercialArticles); + + return "shop/articles/id"; + } + + @PostMapping("/{id}") + public String shopArticlesByIdBuy(HttpSession session, + @RequestAttribute(value = "user", required = false) User customer, +// @RequestAttribute(value = "shoppingCart", required = true) ShoppingCart shoppingCart, + @PathVariable("id") Integer id, + @RequestParam("fastcheckout") Boolean fastcheckout + ) { + if (customer != null) { + //TODO: Add Article to Shopping Cart + + if (!fastcheckout) { + return "shop/articles/post_add"; + } else { + return "shop/checkout"; + } + } else { + session.setAttribute("afterLogin", "/shop/articles/" + id); + return "redirect:/login"; + } + } } diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index c99ed1d..2a7df6d 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -19,17 +19,12 @@
-

Tolle Kamera

+

-

25.14 EUR

-

- Eine TOLLE Kamera
- Jaja du denkst jetzt bestimmt: "Bei dem Preis kann sie gar nich sooo TOLL sein".
- Aber glaub mir, sie is echt echt TOLL!
- Indianerehrenwort! -

+

+

@@ -37,14 +32,17 @@
-

50.28 EUR

+

+
-

Auf Lager

+

@@ -56,26 +54,13 @@ - Warenkorb + Warenkorb ()
diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index 2a7df6d..46b7c93 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -34,10 +34,9 @@

- - From d681f240e1f5adc128d43769bc3fce74db66567e Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 18:59:15 +0200 Subject: [PATCH 006/203] implement checkout for non logged in users. --- .../shop/ShopArticleController.java | 9 +- .../shop/ShopCheckoutController.java | 52 ++++++- .../hso/ecommerce/entities/shop/Article.java | 8 + .../ecommerce/entities/shop/ShoppingCart.java | 29 +++- prototype/src/main/resources/db/customers.sql | 7 - .../resources/templates/shop/checkout.html | 141 ++++++------------ 6 files changed, 136 insertions(+), 110 deletions(-) delete mode 100644 prototype/src/main/resources/db/customers.sql diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index bd544b3..5670f49 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -63,6 +63,7 @@ public class ShopArticleController { @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, @PathVariable("id") Long id, @RequestParam("quantity") Integer quantity, + @RequestParam(value = "set_amount", required = false) Boolean setAmount, @RequestParam("fastcheckout") Boolean fastcheckout ) { @@ -71,12 +72,16 @@ public class ShopArticleController { throw new RuntimeException("Article not found!"); } - shoppingCart.addArticle(article.get(), quantity); + if (setAmount != null && setAmount) { + shoppingCart.setArticleCount(article.get(), quantity); + } else { + shoppingCart.addArticle(article.get(), quantity); + } if (!fastcheckout) { return "shop/articles/post_add"; } else { - return "shop/checkout"; + return "redirect:/shop/checkout"; } } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index 1573b1b..960a104 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -1,23 +1,73 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.util.ArrayList; +import java.util.TreeMap; @Controller @RequestMapping("/shop/") public class ShopCheckoutController { + @Autowired + private final ArticleRepository articleRepository = null; + @GetMapping("/checkout") - public String shopCheckout(HttpSession session, HttpServletRequest request) { + public String shopCheckout(HttpSession session, + HttpServletRequest request, + @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart) { session.setAttribute("afterLogin", request.getRequestURI()); + + CheckoutListTotals totals = new CheckoutListTotals(); + ArrayList items = new ArrayList<>(); + for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) { + Article article = articleRepository.findById(item.getArticleId()).get(); + + totals.addItem(article, item.getAmount()); + items.add(new CheckoutListItem(item.getAmount(), article)); + } + + request.setAttribute("checkoutItems", items); + request.setAttribute("checkoutTotals", totals); + return "shop/checkout"; } + public static class CheckoutListTotals { + public int net; + public TreeMap vatAmounts = new TreeMap<>(); + public int total; + + void addItem(Article article, int amount) { + net += article.shopPricePerUnitNetCent * amount; + Integer vatPos = vatAmounts.getOrDefault(article.related.vatPercent, 0) + article.getVat() * amount; + vatAmounts.put(article.related.vatPercent, vatPos); + total += article.getPriceGross() * amount; + } + } + + public static class CheckoutListItem { + public int amount; + public Article article; + public int total; + + public CheckoutListItem(int amount, Article article) { + this.amount = amount; + this.article = article; + this.total = amount * article.getPriceGross(); + } + } + @PostMapping("/checkoutFinish") public String shopCheckoutFinish() { return "shop/checkoutFinish"; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java index 6cd198e..2e066cd 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java @@ -37,4 +37,12 @@ public class Article { @ManyToMany @JoinTable(name = "article_categories_bindings") public Set categories = new HashSet<>(); + + public int getVat() { + return (shopPricePerUnitNetCent * related.vatPercent) / 100; + } + + public int getPriceGross() { + return shopPricePerUnitNetCent + getVat(); + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java index f7955ec..aee94c2 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.entities.shop; import java.util.ArrayList; +import java.util.List; // Not a db entity. Just for session storage public class ShoppingCart { @@ -13,6 +14,10 @@ public class ShoppingCart { items = new ArrayList<>(); } + public List getItems() { + return items; + } + public int getItemCount() { int count = 0; @@ -40,7 +45,25 @@ public class ShoppingCart { items.add(new ShoppingCartItem(quantity, article)); } - private static class ShoppingCartItem { + public void setArticleCount(Article article, Integer quantity) { + this.revision++; + + boolean found = false; + for (ShoppingCartItem i : items) { + if (i.getArticleId() == article.id) { + i.setAmount(quantity); + found = true; + break; + } + } + if (!found) { + items.add(new ShoppingCartItem(quantity, article)); + } + + items.removeIf(i -> i.getAmount() == 0); + } + + public static class ShoppingCartItem { private int amount; private final long articleId; @@ -60,5 +83,9 @@ public class ShoppingCart { public long getArticleId() { return articleId; } + + public void setAmount(Integer amount) { + this.amount = amount; + } } } diff --git a/prototype/src/main/resources/db/customers.sql b/prototype/src/main/resources/db/customers.sql deleted file mode 100644 index b1a108f..0000000 --- a/prototype/src/main/resources/db/customers.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE "customers" ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "lastname" TEXT, - "firstname" TEXT, - "username" TEXT, - "password" TEXT -); diff --git a/prototype/src/main/resources/templates/shop/checkout.html b/prototype/src/main/resources/templates/shop/checkout.html index e6bb15d..e017f9e 100644 --- a/prototype/src/main/resources/templates/shop/checkout.html +++ b/prototype/src/main/resources/templates/shop/checkout.html @@ -31,91 +31,31 @@ Menge - - - Kamera - 100,50 EUR - - - - - - - - - - Earbuds - 63,95 EUR - - - - - - - - - - USB-Magic Light - 11,90 EUR - - - - - - - - - - 3D Magic Stativ - 15,99 EUR - - - - - - - - - - Ersatzfernbedinung - 7,95 EUR - - - - - - - + + + + + EUR + +
+ + + + +
+ + +
@@ -151,23 +91,26 @@ Musterstraße 42 - - - - - - - - - - - - - + + + + + + + - + From b2f927e48d2da3bc39c5c6a8833d6735534f2c71 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 22:56:12 +0200 Subject: [PATCH 007/203] Implement CreateCustomerOrderAction; Not implemented: correct booking and WarehouseBooking fetching... Puhh, that was a struggle to get working. Damn JPA Shit --- prototype/scripts/addusers.sql | 12 +- .../action/booking/CreateBookingAction.java | 30 +++ .../action/shop/CreateOrderAction.java | 177 ++++++++++++++++++ .../action/shop/EnableTrackingAction.java | 11 ++ .../action/somepackage/DemoAction.java | 7 - .../shop/ShopCheckoutController.java | 73 +++++++- .../ecommerce/entities/booking/Booking.java | 6 +- .../entities/booking/BookingAccountEntry.java | 40 +++- .../entities/booking/BookingReason.java | 15 +- .../entities/booking/PaymentMethod.java | 7 + .../hso/ecommerce/entities/shop/Address.java | 21 ++- .../entities/shop/CustomerOrder.java | 2 +- .../ecommerce/entities/shop/ShoppingCart.java | 16 +- .../org/hso/ecommerce/entities/user/User.java | 4 +- .../entities/warehouse/WarehouseBooking.java | 7 +- .../warehouse/WarehouseBookingPosition.java | 8 +- .../WarehouseBookingPositionSlotEntry.java | 19 +- .../warehouse/WarehouseBookingReason.java | 7 + .../BookingAccountEntryRepository.java | 26 +++ .../repos/booking/BookingRepository.java | 12 ++ .../repos/shop/CustomerOderRepository.java | 11 ++ ...useBookingPositionSlotEntryRepository.java | 18 ++ .../warehouse/WarehouseBookingRepository.java | 11 ++ .../resources/templates/shop/checkout.html | 39 ++-- 24 files changed, 526 insertions(+), 53 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java delete mode 100644 prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java diff --git a/prototype/scripts/addusers.sql b/prototype/scripts/addusers.sql index 86b6d43..db3d6ed 100644 --- a/prototype/scripts/addusers.sql +++ b/prototype/scripts/addusers.sql @@ -1,9 +1,9 @@ /* password is 123 */ -INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") -VALUES (datetime('now','localtime') ||'.0', "emp@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "0", "1", "1", "0"); +INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee") +VALUES (datetime('now','localtime') ||'.0', "emp@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "1"); -INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") -VALUES (datetime('now','localtime') ||'.0', "user@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "1", "0", "0"); +INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee") +VALUES (datetime('now','localtime') ||'.0', "user@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "0"); -INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") -VALUES (datetime('now','localtime') ||'.0', "blocked@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "0", "0", "0"); \ No newline at end of file +INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee") +VALUES (datetime('now','localtime') ||'.0', "blocked@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "0", "0"); \ No newline at end of file diff --git a/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java b/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java new file mode 100644 index 0000000..f01e6a3 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java @@ -0,0 +1,30 @@ +package org.hso.ecommerce.action.booking; + +import org.hso.ecommerce.entities.booking.Booking; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.BookingReason; + +public class CreateBookingAction { + + private Booking booking; + + public CreateBookingAction(BookingAccountEntry source, BookingAccountEntry destination, BookingReason reason, int amountCent) { + booking = new Booking(); + booking.reason = reason; + booking.amountCent = amountCent; + + assert source != null || destination != null; + + if (source != null) { + booking.source = source.copyAddAmount(-amountCent); + } + if (destination != null) { + booking.destination = destination.copyAddAmount(amountCent); + } + + } + + public Booking finish() { + return booking; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java new file mode 100644 index 0000000..ed1066c --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java @@ -0,0 +1,177 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.action.booking.CreateBookingAction; +import org.hso.ecommerce.entities.booking.*; +import org.hso.ecommerce.entities.shop.Address; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.hso.ecommerce.entities.shop.CustomerOrderPosition; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class CreateOrderAction { + + private User user; + Address destination; + + private int expectedTotalGrossCent; + + private int totalNetCent; + private int totalVatCent; + private PaymentMethod method; + + private BookingAccountEntry latestUserBooking; + private BookingAccountEntry latestVatBooking; + private BookingAccountEntry latestMainBooking; + + private List orderItems = new ArrayList<>(); + + public CreateOrderAction( + User user, + int expectedTotalGrossCent, + Address destination, + PaymentMethod method, + BookingAccountEntry latestUserBooking, + BookingAccountEntry latestVatBooking, + BookingAccountEntry latestMainBooking + ) { + this.user = user; + this.expectedTotalGrossCent = expectedTotalGrossCent; + this.destination = destination; + this.method = method; + + this.latestUserBooking = latestUserBooking; + assert latestUserBooking.userAccount.id == user.id; + + this.latestVatBooking = latestVatBooking; + assert latestVatBooking.isVATAccount; + + this.latestMainBooking = latestMainBooking; + assert latestMainBooking.isMainAccount; + } + + public void addArticle(Article article, int quantity, List availableSlots) { + for (WarehouseBookingPositionSlotEntry slot : availableSlots) { + assert slot.article.id == article.id; + } + + orderItems.add(new OrderItem(article, availableSlots, quantity)); + + totalNetCent += article.shopPricePerUnitNetCent * quantity; + totalVatCent += article.getVat() * quantity; + } + + public Result finish() { + CustomerOrder order = createOrder(); + CustomerPayment payment = createPayment(); + + List bookingList = new ArrayList<>(); + bookingList.add(new CreateBookingAction(latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish()); + bookingList.add(new CreateBookingAction(null, latestUserBooking, new BookingReason(payment), order.totalGrossCent).finish()); + bookingList.add(new CreateBookingAction(latestMainBooking, latestVatBooking, new BookingReason(order), order.totalVatCent).finish()); + + WarehouseBooking warehouseBooking = createWarehouseBooking(order); + + return new Result( + order, + warehouseBooking, + bookingList + ); + } + + private WarehouseBooking createWarehouseBooking(CustomerOrder order) { + WarehouseBooking booking = new WarehouseBooking(); + booking.created = new Timestamp(new Date().getTime()); + booking.reason = new BookingReason(order); + + for (OrderItem item : orderItems) { + int needed = item.quantity; + + for (WarehouseBookingPositionSlotEntry slot : item.availableSlots) { + int remove = Math.min(slot.newSumSlot, needed); + needed -= remove; + + WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); + + bookingPosition.article = item.article; + bookingPosition.amount = -remove; + bookingPosition.slotEntry = slot.copyAddAmount(-remove); + bookingPosition.booking = booking; + + booking.positions.add(bookingPosition); + + if (needed == 0) { + break; + } + } + } + + return booking; + } + + private CustomerPayment createPayment() { + CustomerPayment payment = new CustomerPayment(); + payment.amountCent = totalNetCent + totalVatCent; + payment.payment = method; + return payment; + } + + + private CustomerOrder createOrder() { + assert totalNetCent + totalVatCent == expectedTotalGrossCent; + + CustomerOrder customerOrder = new CustomerOrder(); + customerOrder.customer = user; + customerOrder.destination = destination; + + for (OrderItem item : orderItems) { + CustomerOrderPosition position = new CustomerOrderPosition(); + position.article = item.article; + position.pricePerUnit = item.article.shopPricePerUnitNetCent; + position.quantity = item.quantity; + + position.order = customerOrder; + + customerOrder.positions.add(position); + } + + customerOrder.created = new Timestamp(new Date().getTime()); + + customerOrder.totalNetCent = totalNetCent; + customerOrder.totalVatCent = totalVatCent; + customerOrder.totalGrossCent = totalNetCent + totalVatCent; + + return customerOrder; + } + + public static class Result { + public final CustomerOrder customerOrder; + public final WarehouseBooking warehouseBooking; + public final List bookings; + + Result(CustomerOrder customerOrder, WarehouseBooking warehouseBooking, List bookings) { + this.customerOrder = customerOrder; + this.warehouseBooking = warehouseBooking; + this.bookings = bookings; + } + } + + private static class OrderItem { + List availableSlots; + int quantity; + Article article; + + public OrderItem(Article article, List availableSlots, int quantity) { + this.article = article; + this.availableSlots = availableSlots; + this.quantity = quantity; + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java new file mode 100644 index 0000000..d3e2dc7 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.entities.shop.CustomerOrder; + +public class EnableTrackingAction { + + public static void addTrackingInfo(CustomerOrder customerOrder) { + // TODO: + customerOrder.trackingId = "555-NASE"; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java b/prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java deleted file mode 100644 index 22f8946..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.hso.ecommerce.action.somepackage; - -public class DemoAction { - // TODO: remove me. - // mksubpackage - -} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index 960a104..66ced97 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -1,14 +1,23 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.action.shop.CreateOrderAction; +import org.hso.ecommerce.action.shop.EnableTrackingAction; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.PaymentMethod; +import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; +import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.shop.CustomerOderRepository; +import org.hso.ecommerce.repos.user.UserRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestAttribute; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -19,9 +28,27 @@ import java.util.TreeMap; @RequestMapping("/shop/") public class ShopCheckoutController { + @Autowired + private final UserRepository userRepository = null; + @Autowired private final ArticleRepository articleRepository = null; + @Autowired + private final BookingAccountEntryRepository bookingEntryRepository = null; + + @Autowired + private final BookingRepository bookingRepository = null; + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @Autowired + private final CustomerOderRepository customerOderRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository wbeseRepo = null; + @GetMapping("/checkout") public String shopCheckout(HttpSession session, HttpServletRequest request, @@ -69,7 +96,43 @@ public class ShopCheckoutController { } @PostMapping("/checkoutFinish") - public String shopCheckoutFinish() { + public String shopCheckoutFinish( + @RequestAttribute(value = "user") User user, + @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, + @RequestParam("address") String address, + @RequestParam("cardnumber") String cardnumber, + @RequestParam("shopping_cart_revision") Integer cartRevision, + @RequestParam("expected_total") Integer expectedPrice + ) { + + // Must be refetched for persitence. + user = userRepository.findById(user.id).get(); + + CreateOrderAction action = new CreateOrderAction( + user, + expectedPrice, + Address.fromString(address), + PaymentMethod.fromCreditCarNumber(cardnumber), + bookingEntryRepository.getByUser(user).orElse(BookingAccountEntry.newUser(user)), + bookingEntryRepository.getByVat().orElse(BookingAccountEntry.newVat()), + bookingEntryRepository.getByMain().orElse(BookingAccountEntry.newMain()) + ); + + for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) { + Article article = articleRepository.findById(item.getArticleId()).get(); + action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article)); + } + + CreateOrderAction.Result result = action.finish(); + + EnableTrackingAction.addTrackingInfo(result.customerOrder); + + customerOderRepository.save(result.customerOrder); + bookingRepository.saveAll(result.bookings); + warehouseBookingRepository.save(result.warehouseBooking); + + shoppingCart.clear(); + return "shop/checkoutFinish"; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java index 997f578..e61ea07 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java @@ -14,12 +14,12 @@ public class Booking { // always >= 0 public int amountCent; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public BookingAccountEntry source; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public BookingAccountEntry destination; - @OneToOne(optional = false) + @OneToOne(optional = false, cascade = CascadeType.ALL) public BookingReason reason; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java index 92e282f..04b3b8a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java @@ -16,13 +16,49 @@ public class BookingAccountEntry { public int newSumCent; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public User userAccount; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public Supplier supplierAccount; public boolean isMainAccount; public boolean isVATAccount; + public BookingAccountEntry copyAddAmount(int amountCent) { + BookingAccountEntry e = new BookingAccountEntry(); + + e.userAccount = userAccount; + e.supplierAccount = supplierAccount; + e.isMainAccount = isMainAccount; + e.isVATAccount = isVATAccount; + + e.newSumCent = newSumCent + amountCent; + + return e; + } + + public static BookingAccountEntry newUser(User user) { + BookingAccountEntry e = new BookingAccountEntry(); + e.userAccount = user; + e.newSumCent = 0; + + return e; + } + + public static BookingAccountEntry newMain() { + BookingAccountEntry e = new BookingAccountEntry(); + e.isMainAccount = true; + e.newSumCent = 0; + + return e; + } + + public static BookingAccountEntry newVat() { + BookingAccountEntry e = new BookingAccountEntry(); + e.isVATAccount = true; + e.newSumCent = 0; + + return e; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java index b2bc6cc..f973d26 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java @@ -22,9 +22,22 @@ public class BookingReason { @ManyToOne(optional = true) public CustomerOrder customerOrder; - @ManyToOne(optional = true) + @OneToOne(optional = true, cascade = CascadeType.ALL) public CustomerPayment customerPayment; @ManyToOne(optional = true) public SupplierOrder supplierOrder; + + public BookingReason() { + } + + ; + + public BookingReason(CustomerOrder order) { + this.customerOrder = order; + } + + public BookingReason(CustomerPayment customerPayment) { + this.customerPayment = customerPayment; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java index 6cb5307..4f81062 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java @@ -7,4 +7,11 @@ import javax.validation.constraints.NotNull; public class PaymentMethod { @NotNull public String creditCardNumber; + + public static PaymentMethod fromCreditCarNumber(String cardnumber) { + PaymentMethod m = new PaymentMethod(); + m.creditCardNumber = cardnumber; + + return m; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java index 32d057b..2f76083 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java @@ -4,7 +4,24 @@ import javax.persistence.Embeddable; @Embeddable public class Address { - public String name; - public String addressString; + public String name = ""; + public String addressString = ""; public String country = "DE"; + + @Override + public String toString() { + return name + "\n" + addressString; + } + + public static Address fromString(String addr) { + Address a = new Address(); + + String[] arr = addr.split("\n", 2); + a.name = arr[0]; + if (arr.length > 1) { + a.addressString = arr[1]; + } + + return a; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java index dd7d4f8..1b20bb2 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java @@ -24,7 +24,7 @@ public class CustomerOrder { @OneToMany( targetEntity = CustomerOrderPosition.class, - mappedBy = "order" + mappedBy = "order", cascade = CascadeType.ALL ) public List positions = new ArrayList<>(); diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java index aee94c2..d059f15 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java @@ -6,12 +6,18 @@ import java.util.List; // Not a db entity. Just for session storage public class ShoppingCart { + private final static int MAX_ITEMS = 10; + private int revision; private ArrayList items; public ShoppingCart() { - revision = (int) Math.round(Math.random() * 0xFFFF); + clear(); + } + + public void clear() { items = new ArrayList<>(); + revision = (int) Math.round(Math.random() * 0xFFFF); } public List getItems() { @@ -60,7 +66,7 @@ public class ShoppingCart { items.add(new ShoppingCartItem(quantity, article)); } - items.removeIf(i -> i.getAmount() == 0); + items.removeIf(i -> i.getAmount() <= 0); } public static class ShoppingCartItem { @@ -78,6 +84,9 @@ public class ShoppingCart { public void addAmount(int amount) { this.amount += amount; + if (this.amount > MAX_ITEMS) { + this.amount = MAX_ITEMS; + } } public long getArticleId() { @@ -86,6 +95,9 @@ public class ShoppingCart { public void setAmount(Integer amount) { this.amount = amount; + if (this.amount > MAX_ITEMS) { + this.amount = MAX_ITEMS; + } } } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index 9b07996..784d89a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -30,10 +30,10 @@ public class User { public boolean isEmployee; @Embedded - private Address defaultDeliveryAddress; + public Address defaultDeliveryAddress; @Embedded - private PaymentMethod defaultPayment; + public PaymentMethod defaultPayment; public long getId() { return id; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java index 97a2805..cf60734 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java @@ -1,5 +1,7 @@ package org.hso.ecommerce.entities.warehouse; +import org.hso.ecommerce.entities.booking.BookingReason; + import javax.persistence.*; import javax.validation.constraints.NotNull; import java.util.ArrayList; @@ -21,7 +23,10 @@ public class WarehouseBooking { public boolean isDone; @OneToMany( - mappedBy = "booking" + mappedBy = "booking", cascade = CascadeType.ALL ) public List positions = new ArrayList<>(); + + @OneToOne(optional = false, cascade = CascadeType.ALL) + public BookingReason reason; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java index 88ceee3..e3a147a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java @@ -23,10 +23,6 @@ public class WarehouseBookingPosition { public int amount; // positive or negative - @ManyToOne(optional = true) - public WarehouseBookingPositionSlotEntry source; - - @ManyToOne(optional = true) - public WarehouseBookingPositionSlotEntry destination; - + @ManyToOne(optional = true, cascade = CascadeType.ALL) + public WarehouseBookingPositionSlotEntry slotEntry; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java index 7529e4f..d9ca4c6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java @@ -16,8 +16,23 @@ public class WarehouseBookingPositionSlotEntry { @ManyToOne public Article article; - public int newSumArticles; - public int newSumWarehousePosition; + // Can;t do, does not work when created in action. + //public int newSumArticles; + public int newSumSlot; public int slot; + + public WarehouseBookingPositionSlotEntry copyAddAmount(int amount) { + WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry(); + + e.article = article; + e.slot = slot; + + e.newSumSlot = newSumSlot + amount; + + assert e.article.warehouseUnitsPerSlot >= e.newSumSlot; + assert e.newSumSlot >= 0; + + return e; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java index ec60c4d..08ee80a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java @@ -23,4 +23,11 @@ public class WarehouseBookingReason { public CustomerOrder customerOrder; public boolean isManuel; + + public WarehouseBookingReason() { + } + + public WarehouseBookingReason(CustomerOrder order) { + this.customerOrder = order; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java new file mode 100644 index 0000000..657e34d --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java @@ -0,0 +1,26 @@ +package org.hso.ecommerce.repos.booking; + +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.user.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface BookingAccountEntryRepository extends JpaRepository { + + @Query("SELECT e FROM BookingAccountEntry e WHERE e.userAccount = :user ORDER BY e.id DESC") + Optional getByUser(User user); + + @Query("SELECT e FROM BookingAccountEntry e WHERE e.isMainAccount = 1 ORDER BY e.id DESC") + Optional getByMain(); + + @Query("SELECT e FROM BookingAccountEntry e WHERE e.isVATAccount = 1 ORDER BY e.id DESC") + Optional getByVat(); + + +} + + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java new file mode 100644 index 0000000..454b3f3 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java @@ -0,0 +1,12 @@ +package org.hso.ecommerce.repos.booking; + +import org.hso.ecommerce.entities.booking.Booking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BookingRepository extends JpaRepository { + +} + + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java new file mode 100644 index 0000000..407f45c --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CustomerOderRepository extends JpaRepository { + +} + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java new file mode 100644 index 0000000..839a017 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -0,0 +1,18 @@ +package org.hso.ecommerce.repos.warehouse; + +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository { + + // TODO this is wrong. revisit. + @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e WHERE e.article = :article ORDER BY e.id DESC") + List getByArticle(Article article); +} + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java new file mode 100644 index 0000000..8d01092 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.repos.warehouse; + +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface WarehouseBookingRepository extends JpaRepository { + +} + diff --git a/prototype/src/main/resources/templates/shop/checkout.html b/prototype/src/main/resources/templates/shop/checkout.html index e017f9e..cf02b5c 100644 --- a/prototype/src/main/resources/templates/shop/checkout.html +++ b/prototype/src/main/resources/templates/shop/checkout.html @@ -21,7 +21,17 @@ -
+
+
+

Noch keine Artikel im Warenkorb.

+

+ +

+ Weiter shoppen +
+ +
+
Artikel (Netto)200,29 EUR
Bonuspunkte-5,00 EUR
Umsatzsteuer (19%)35,00 EUR
Umsatzsteuer (7%)2,50 EUR + EUR +
Umsatzsteuer (%) + EUR +
Gesamt:240,79 EUR + EUR +
@@ -37,7 +47,9 @@ th:src="@{/shop/articles/${item.article.id}/image.jpg}" class="s"/> - + + th:src="@{/shop/articles/{id}/image.jpg(id=${item.article.id})}" class="s"/> + - + From 731f4761fab17d3e8df39c43a99f6285e7b4b4b0 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 10 May 2020 14:29:08 +0200 Subject: [PATCH 019/203] Show error if articles are not avail during checkout. --- .../action/shop/CreateOrderAction.java | 22 +++++++++++++++++-- .../shop/ShopCheckoutController.java | 20 ++++++++++++----- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java index ed1066c..8b35be8 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java @@ -68,7 +68,8 @@ public class CreateOrderAction { totalVatCent += article.getVat() * quantity; } - public Result finish() { + + public Result finish() throws ArticleNotInStockException { CustomerOrder order = createOrder(); CustomerPayment payment = createPayment(); @@ -86,7 +87,7 @@ public class CreateOrderAction { ); } - private WarehouseBooking createWarehouseBooking(CustomerOrder order) { + private WarehouseBooking createWarehouseBooking(CustomerOrder order) throws ArticleNotInStockException { WarehouseBooking booking = new WarehouseBooking(); booking.created = new Timestamp(new Date().getTime()); booking.reason = new BookingReason(order); @@ -111,6 +112,10 @@ public class CreateOrderAction { break; } } + + if (needed > 0) { + throw new ArticleNotInStockException(item.article); + } } return booking; @@ -174,4 +179,17 @@ public class CreateOrderAction { this.quantity = quantity; } } + + public static class ArticleNotInStockException extends Exception { + private Article article; + + public ArticleNotInStockException(Article article) { + super("The quantity of article '" + article.title + "' is not in stock."); + this.article = article; + } + + public Article getArticle() { + return article; + } + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index ab4827f..c2c1c3a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -98,6 +98,7 @@ public class ShopCheckoutController { @PostMapping("/checkoutFinish") public String shopCheckoutFinish( + HttpSession session, HttpServletRequest request, HttpServletResponse response, @RequestAttribute(value = "user") User user, @@ -132,15 +133,22 @@ public class ShopCheckoutController { action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article.id)); } - CreateOrderAction.Result result = action.finish(); + CreateOrderAction.Result result = null; + try { + result = action.finish(); + EnableTrackingAction.addTrackingInfo(result.customerOrder); - EnableTrackingAction.addTrackingInfo(result.customerOrder); + customerOderRepository.save(result.customerOrder); + bookingRepository.saveAll(result.bookings); + warehouseBookingRepository.save(result.warehouseBooking); - customerOderRepository.save(result.customerOrder); - bookingRepository.saveAll(result.bookings); - warehouseBookingRepository.save(result.warehouseBooking); + shoppingCart.clear(); + + } catch (CreateOrderAction.ArticleNotInStockException e) { + request.setAttribute("error", "Der Artikel '" + e.getArticle().title + "' ist leider nicht mehr in ausreichender Menge verfügbar. Bitte passen Sie die Artikelmenge an."); + return shopCheckout(session, request, shoppingCart); + } - shoppingCart.clear(); return "shop/checkoutFinish"; } From d1301f69887829139044a059721b58797c7cf4e6 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 10 May 2020 14:30:00 +0200 Subject: [PATCH 020/203] Fix naming-convension-error in ArticleOffer --- .../org/hso/ecommerce/entities/supplier/ArticleOffer.java | 2 +- .../hso/ecommerce/entities/warehouse/WarehouseBooking.java | 1 + .../java/org/hso/ecommerce/repos/shop/ArticleRepository.java | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java index ce02d13..ae86659 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java @@ -20,5 +20,5 @@ public class ArticleOffer { public int vatPercent; - public boolean should_be_advertised; + public boolean shouldBeAdvertised; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java index cf60734..08eaa40 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java @@ -27,6 +27,7 @@ public class WarehouseBooking { ) public List positions = new ArrayList<>(); + // TODO FIX ME @OneToOne(optional = false, cascade = CascadeType.ALL) public BookingReason reason; } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 2a88b7e..90b6d11 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -1,14 +1,12 @@ package org.hso.ecommerce.repos.shop; import org.hso.ecommerce.entities.shop.Article; -import org.hso.ecommerce.entities.user.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; -import java.util.Optional; @Repository public interface ArticleRepository extends JpaRepository { @@ -17,7 +15,7 @@ public interface ArticleRepository extends JpaRepository { Article findArticleById(@Param("articleId") long articleId); - @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.should_be_advertised = true") + @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.shouldBeAdvertised = true") List
getAdvertisedArticles(); From d4d1426438de504f09201ac75c914df086e9f57f Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 10 May 2020 14:34:20 +0200 Subject: [PATCH 021/203] Fix decimals in checkout page --- prototype/src/main/resources/templates/shop/checkout.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prototype/src/main/resources/templates/shop/checkout.html b/prototype/src/main/resources/templates/shop/checkout.html index 9962649..786885f 100644 --- a/prototype/src/main/resources/templates/shop/checkout.html +++ b/prototype/src/main/resources/templates/shop/checkout.html @@ -49,7 +49,7 @@
@@ -114,7 +114,7 @@ Musterstraße 4 @@ -122,7 +122,7 @@ Musterstraße 4 From 95e52cc79dc4037806e823b8758d8bef0f67be3b Mon Sep 17 00:00:00 2001 From: Hannes Date: Sun, 10 May 2020 14:57:02 +0200 Subject: [PATCH 022/203] fix non-formatted and non-vatted price --- .../src/main/resources/templates/shop/articles/id.html | 6 +++--- prototype/src/main/resources/templates/shop/index.html | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index 73de367..588b5be 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -23,7 +23,7 @@
-

+

EUR

@@ -32,7 +32,7 @@
-

+

EUR

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
EUR + EUR x @@ -45,11 +57,9 @@ @@ -64,11 +74,16 @@

Checkout

+ + +

Lieferadresse:

@@ -78,12 +93,10 @@ Musterstraße 42
-
- - -

Bestellübersicht

From f5a99700a076faf758ec1a5ca65a95cf4f0a0894 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 23:34:17 +0200 Subject: [PATCH 008/203] Renice error handling --- .../controller/shop/ShopArticleController.java | 10 ++++++++-- .../controller/shop/ShopCheckoutController.java | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 5670f49..3cb860f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -8,6 +8,8 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.List; @@ -59,7 +61,9 @@ public class ShopArticleController { } @PostMapping("/{id}") - public String shopArticlesByIdBuy(HttpSession session, + public String shopArticlesByIdBuy(HttpServletRequest request, + HttpServletResponse response, + HttpSession session, @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, @PathVariable("id") Long id, @RequestParam("quantity") Integer quantity, @@ -69,7 +73,9 @@ public class ShopArticleController { Optional
article = articleRepository.findById(id); if (!article.isPresent()) { - throw new RuntimeException("Article not found!"); + request.setAttribute("error", "Der Artikel wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; } if (setAmount != null && setAmount) { diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index 66ced97..ffdd9f3 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -20,6 +20,7 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.TreeMap; @@ -97,6 +98,8 @@ public class ShopCheckoutController { @PostMapping("/checkoutFinish") public String shopCheckoutFinish( + HttpServletRequest request, + HttpServletResponse response, @RequestAttribute(value = "user") User user, @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, @RequestParam("address") String address, @@ -105,6 +108,12 @@ public class ShopCheckoutController { @RequestParam("expected_total") Integer expectedPrice ) { + if (shoppingCart.getRevision() != cartRevision) { + request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher die Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut."); + response.setStatus(HttpServletResponse.SC_CONFLICT); + return "shop/checkout"; + } + // Must be refetched for persitence. user = userRepository.findById(user.id).get(); From cdce22968d560aff156d32a97503ae8cf7eb25c4 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Wed, 6 May 2020 14:10:37 +0200 Subject: [PATCH 009/203] Implement proper querying of warehouse slot sums and booking accounts. --- .../ecommerce/components/SlotInitializer.java | 32 +++++++++++++++++++ .../shop/ShopCheckoutController.java | 4 +-- .../ecommerce/entities/warehouse/Slot.java | 17 ++++++++++ .../WarehouseBookingPositionSlotEntry.java | 6 +++- .../BookingAccountEntryRepository.java | 9 +++--- .../repos/warehouse/SlotRepository.java | 17 ++++++++++ ...useBookingPositionSlotEntryRepository.java | 6 ++-- 7 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java diff --git a/prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java b/prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java new file mode 100644 index 0000000..ce8c39b --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java @@ -0,0 +1,32 @@ +package org.hso.ecommerce.components; + +import org.hso.ecommerce.entities.warehouse.Slot; +import org.hso.ecommerce.repos.warehouse.SlotRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +@Component +public class SlotInitializer { + + @Autowired + private final SlotRepository slotRepository = null; + + // TODO: use values form cfg. + private final int NUM_SLOTS = 50; + + @PostConstruct + public void init() { + for (int i = 1; i <= NUM_SLOTS; i++) { + if (!slotRepository.findBySlotNum(i).isPresent()) { + Slot slotAdded = new Slot(); + slotAdded.slotNum = i; + slotRepository.save(slotAdded); + + System.out.println("Added Slot " + i + " to DB"); + } + } + } + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index ffdd9f3..ab4827f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -122,14 +122,14 @@ public class ShopCheckoutController { expectedPrice, Address.fromString(address), PaymentMethod.fromCreditCarNumber(cardnumber), - bookingEntryRepository.getByUser(user).orElse(BookingAccountEntry.newUser(user)), + bookingEntryRepository.getByUser(user.id).orElse(BookingAccountEntry.newUser(user)), bookingEntryRepository.getByVat().orElse(BookingAccountEntry.newVat()), bookingEntryRepository.getByMain().orElse(BookingAccountEntry.newMain()) ); for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) { Article article = articleRepository.findById(item.getArticleId()).get(); - action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article)); + action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article.id)); } CreateOrderAction.Result result = action.finish(); diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java new file mode 100644 index 0000000..6a56541 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java @@ -0,0 +1,17 @@ +package org.hso.ecommerce.entities.warehouse; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Entity +@Table(name = "warehouse_slots") +public class Slot { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Basic + public long id; + + @NotNull + @Column(unique = true) + public int slotNum; +} diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java index d9ca4c6..766398f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java @@ -3,6 +3,7 @@ package org.hso.ecommerce.entities.warehouse; import org.hso.ecommerce.entities.shop.Article; import javax.persistence.*; +import javax.validation.constraints.NotNull; @Entity @Table(name = "warehouse_booking_position_entries") @@ -13,6 +14,7 @@ public class WarehouseBookingPositionSlotEntry { @Basic public long id; + @NotNull @ManyToOne public Article article; @@ -20,7 +22,9 @@ public class WarehouseBookingPositionSlotEntry { //public int newSumArticles; public int newSumSlot; - public int slot; + @NotNull + @ManyToOne + public Slot slot; public WarehouseBookingPositionSlotEntry copyAddAmount(int amount) { WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry(); diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java index 657e34d..d842a3a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java @@ -1,7 +1,6 @@ package org.hso.ecommerce.repos.booking; import org.hso.ecommerce.entities.booking.BookingAccountEntry; -import org.hso.ecommerce.entities.user.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -11,13 +10,13 @@ import java.util.Optional; @Repository public interface BookingAccountEntryRepository extends JpaRepository { - @Query("SELECT e FROM BookingAccountEntry e WHERE e.userAccount = :user ORDER BY e.id DESC") - Optional getByUser(User user); + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.user_account_id = :user ORDER BY e.id DESC LIMIT 1", nativeQuery = true) + Optional getByUser(Long user); - @Query("SELECT e FROM BookingAccountEntry e WHERE e.isMainAccount = 1 ORDER BY e.id DESC") + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.is_main_account = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true) Optional getByMain(); - @Query("SELECT e FROM BookingAccountEntry e WHERE e.isVATAccount = 1 ORDER BY e.id DESC") + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.isvataccount = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true) Optional getByVat(); diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java new file mode 100644 index 0000000..d0f00c0 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java @@ -0,0 +1,17 @@ +package org.hso.ecommerce.repos.warehouse; + +import org.hso.ecommerce.entities.warehouse.Slot; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface SlotRepository extends JpaRepository { + + @Query("SELECT s FROM Slot s WHERE s.slotNum = :slotNum") + Optional findBySlotNum(int slotNum); + + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java index 839a017..962231a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -1,6 +1,5 @@ package org.hso.ecommerce.repos.warehouse; -import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -12,7 +11,8 @@ import java.util.List; public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository { // TODO this is wrong. revisit. - @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e WHERE e.article = :article ORDER BY e.id DESC") - List getByArticle(Article article); + // @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e, Slot s WHERE e.slot = s AND e.article = :article GROUP BY e.slot.slotNum HAVING max(e.id)") + @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) + List getByArticle(long article); } From beb0131b60714f9c17c1fc4d9d969a0a67e71aba Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Wed, 6 May 2020 17:00:28 +0200 Subject: [PATCH 010/203] Unify Style of error messages to match rest of ui. --- .../static/img/error_404_illustatus.svg | 377 +++++------------- .../static/img/error_generic_illustatus.svg | 352 ++++------------ .../main/resources/templates/error/404.html | 4 +- .../main/resources/templates/error/500.html | 4 +- 4 files changed, 177 insertions(+), 560 deletions(-) diff --git a/prototype/src/main/resources/static/img/error_404_illustatus.svg b/prototype/src/main/resources/static/img/error_404_illustatus.svg index 452b2d5..d58cd65 100644 --- a/prototype/src/main/resources/static/img/error_404_illustatus.svg +++ b/prototype/src/main/resources/static/img/error_404_illustatus.svg @@ -1,277 +1,100 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Oops, Page not found - - - - - \ No newline at end of file + + page not found + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/prototype/src/main/resources/static/img/error_generic_illustatus.svg b/prototype/src/main/resources/static/img/error_generic_illustatus.svg index f94a8d4..20143d7 100644 --- a/prototype/src/main/resources/static/img/error_generic_illustatus.svg +++ b/prototype/src/main/resources/static/img/error_generic_illustatus.svg @@ -1,277 +1,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Oops, something went wrong - - - - - \ No newline at end of file + + server down + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/prototype/src/main/resources/templates/error/404.html b/prototype/src/main/resources/templates/error/404.html index 699b4f5..a58016a 100644 --- a/prototype/src/main/resources/templates/error/404.html +++ b/prototype/src/main/resources/templates/error/404.html @@ -9,10 +9,8 @@
-

Error 404

+

Ein Fehler ist aufgetreten. Die gewünschte Ressource konnte nicht gefunden werden

-

Ein Fehler ist aufgetreten. Die gewünschte Ressource konnte nicht gefunden werden.

-
diff --git a/prototype/src/main/resources/templates/error/500.html b/prototype/src/main/resources/templates/error/500.html index f14f141..4f9747f 100644 --- a/prototype/src/main/resources/templates/error/500.html +++ b/prototype/src/main/resources/templates/error/500.html @@ -9,10 +9,8 @@
-

Error 500

+

Ein Fehler ist aufgetreten. Bitte versuche es später nocheinmal.

-

Ein Fehler ist aufgetreten. Bitte versuche es später nocheinmal.

-
From 4fd80bb3c76639c46640550498309da340afb447 Mon Sep 17 00:00:00 2001 From: Hannes Date: Thu, 7 May 2020 15:48:00 +0200 Subject: [PATCH 011/203] implement imageserving and first part of ArticleRepository --- .gitignore | 2 + prototype/.gitignore | 2 +- .../shop/ShopArticleController.java | 84 +++++++++++-------- .../controller/shop/ShopIndexController.java | 64 ++++---------- .../hso/ecommerce/entities/shop/Article.java | 1 + .../hso/ecommerce/entities/shop/Image.java | 5 +- .../repos/shop/ArticleRepository.java | 20 +++++ .../resources/templates/shop/articles/id.html | 4 +- .../main/resources/templates/shop/index.html | 8 +- 9 files changed, 98 insertions(+), 92 deletions(-) diff --git a/.gitignore b/.gitignore index 3b718f8..3912d2e 100644 --- a/.gitignore +++ b/.gitignore @@ -89,3 +89,5 @@ local.properties # SQLite prototype/*.db + +prototype/images diff --git a/prototype/.gitignore b/prototype/.gitignore index 59529a7..4fac9a9 100644 --- a/prototype/.gitignore +++ b/prototype/.gitignore @@ -1,4 +1,4 @@ -./test.db +./e-commerce.db ./build ./gradle ./out diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 3cb860f..a6159d9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -1,9 +1,11 @@ package org.hso.ecommerce.controller.shop; +import org.apache.tomcat.util.http.fileupload.IOUtils; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @@ -11,7 +13,10 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.util.ArrayList; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.Optional; @@ -23,40 +28,35 @@ public class ShopArticleController { private final ArticleRepository articleRepository = null; @GetMapping("/{id}") - public String shopArticlesById(Model model, @PathVariable("id") Integer Id) { + public String shopArticlesById(Model model, + @PathVariable("id") Long id, + HttpServletRequest request, + HttpServletResponse response + ) { - //TODO: Get Article by Id instead of this dummy shit - Article d1 = new Article(); - d1.description = "this is dummy1"; - d1.title = "dummy1"; - d1.shopPricePerUnitNetCent = 1500; - d1.id = 1234; - model.addAttribute("article", d1); + Article article = articleRepository.findArticleById(id); + + + if(article == null) { + request.setAttribute("error", "Der Artikel wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; + } + + model.addAttribute("article", article); //TODO: Check if in Stock - if(false){ + if (false) { model.addAttribute("inStock", true); - }else{ + } else { model.addAttribute("inStock", false); } - //TODO: Get 2 Commercialised Articles - List
commercialArticles = new ArrayList
(); - Article d2 = new Article(); - d2.description = "this is dummy2"; - d2.title = "dummy2"; - d2.shopPricePerUnitNetCent = 2000; - d2.id = 2345; - Article d3 = new Article(); - d3.description = "this is dummy3"; - d3.title = "dummy3"; - d3.shopPricePerUnitNetCent = 2500; - d3.id = 3456; - commercialArticles.add(d2); - commercialArticles.add(d3); + List
commercialArticles = articleRepository.getCommercialisedArticles("2"); model.addAttribute("commercialArticles", commercialArticles); + return "shop/articles/id"; } @@ -71,17 +71,19 @@ public class ShopArticleController { @RequestParam("fastcheckout") Boolean fastcheckout ) { - Optional
article = articleRepository.findById(id); - if (!article.isPresent()) { - request.setAttribute("error", "Der Artikel wurde nicht gefunden."); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - return "error/404"; - } + + Article article = articleRepository.findArticleById(id); + +// if (!article.isPresent()) { +// request.setAttribute("error", "Der Artikel wurde nicht gefunden."); +// response.setStatus(HttpServletResponse.SC_NOT_FOUND); +// return "error/404"; +// } if (setAmount != null && setAmount) { - shoppingCart.setArticleCount(article.get(), quantity); + shoppingCart.setArticleCount(article, quantity); } else { - shoppingCart.addArticle(article.get(), quantity); + shoppingCart.addArticle(article, quantity); } if (!fastcheckout) { @@ -90,4 +92,20 @@ public class ShopArticleController { return "redirect:/shop/checkout"; } } + + @GetMapping("/{id}/image.jpg") + public void getImageAsByteArray(HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id + ) throws IOException { + + Article article = articleRepository.findArticleById(id); + + InputStream in = new FileInputStream(article.image.path); + response.setContentType(MediaType.IMAGE_JPEG_VALUE); + IOUtils.copy(in, response.getOutputStream()); + } + + } + diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 0be1a50..69dce5d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -1,6 +1,8 @@ package org.hso.ecommerce.controller.shop; import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -8,13 +10,15 @@ import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; +import java.util.List; @Controller @RequestMapping("/") public class ShopIndexController { + @Autowired + private final ArticleRepository articleRepository = null; + @GetMapping("/") public String home() { return "redirect:/shop/"; @@ -23,8 +27,7 @@ public class ShopIndexController { @GetMapping("/shop/") public String shop(Model model, HttpSession session) { - //TODO: get commercialised Articles - ArrayList
commercialArticles = getArticles(); + List
commercialArticles = articleRepository.getCommercialisedArticles("4"); model.addAttribute("commercialArticles", commercialArticles); //check if logged in @@ -33,20 +36,19 @@ public class ShopIndexController { if (session != null && session.getAttribute("id") != null) { long userId = (long) session.getAttribute("id"); isLoggedIn = true; - if (false) { - hasOrders = true; //TODO: Find out whether user has orders! + } + + if (isLoggedIn) { + List
suggestedArticles = articleRepository.getLastOrderedArticles("4"); + if(suggestedArticles.size() > 0) { + model.addAttribute("suggestedArticles", suggestedArticles); + hasOrders = true; } } + model.addAttribute("isLoggedIn", isLoggedIn); model.addAttribute("hasOrders", hasOrders); - - if (hasOrders) { - //TODO: get up to last 4 Orders - ArrayList
suggestedArticles = getArticles(); - model.addAttribute("suggestedArticles", suggestedArticles); - } - return "shop/index"; } @@ -65,40 +67,4 @@ public class ShopIndexController { return "privacy"; } - - public ArrayList
getArticles() { - ArrayList
dummyArticles = new ArrayList
(); - - Article d1 = new Article(); - d1.description = "this is dummy1"; - d1.title = "dummy1"; - d1.shopPricePerUnitNetCent = 1500; - d1.id = 1234; - dummyArticles.add(d1); - - Article d2 = new Article(); - d2.description = "this is dummy2"; - d2.title = "dummy2"; - d2.shopPricePerUnitNetCent = 2000; - d2.id = 2345; - dummyArticles.add(d2); - - Article d3 = new Article(); - d3.description = "this is dummy3"; - d3.title = "dummy3"; - d3.shopPricePerUnitNetCent = 2500; - d3.id = 3456; - dummyArticles.add(d3); - - Article d4 = new Article(); - d4.description = "this is dummy4"; - d4.title = "dummy4"; - d4.shopPricePerUnitNetCent = 3000; - d4.id = 4567; - dummyArticles.add(d4); - - return dummyArticles; - } - - } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java index 2e066cd..a26cb39 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java @@ -32,6 +32,7 @@ public class Article { public String description; @OneToOne(optional = true) + @Basic(fetch = FetchType.LAZY) public Image image; @ManyToMany diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java index 33eebdb..d27d415 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java @@ -11,7 +11,6 @@ public class Image { @Basic public long id; - @Lob - @Column(name = "data", columnDefinition = "BLOB", nullable = false) - private byte[] data; + public String path; } + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 0ac4bd2..42ef5ee 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -1,11 +1,31 @@ package org.hso.ecommerce.repos.shop; import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.user.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + @Repository public interface ArticleRepository extends JpaRepository { + @Query(nativeQuery = true, value = "Select * FROM articles where articles.id = :articleId") + Article findArticleById(@Param("articleId") long articleId); + + + //TODO: getCommercialisedArticles(int quantity) + @Query("SELECT c FROM User c WHERE c.email = :quantity") + List
getCommercialisedArticles(@Param("quantity") String quantity); + + + //TODO: getLastOrderedArticles(int quantity) + @Query("SELECT c FROM User c WHERE c.email = :quantity") + List
getLastOrderedArticles(@Param("quantity") String quantity); + + } diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index 46b7c93..c82aedb 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -27,7 +27,7 @@

- +
@@ -56,7 +56,7 @@

Weitere Schnäppchen

-
+

Jetzt Shoppen und Empfehlungen erhalten!

-
+
- +

From c097e58bf36b0dc5ddce228353612abd9e915bbf Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 8 May 2020 09:58:29 +0200 Subject: [PATCH 012/203] change Databasename and add Advertisementflag --- prototype/.gitignore | 2 +- .../ecommerce/controller/shop/ShopArticleController.java | 4 +--- .../ecommerce/controller/shop/ShopIndexController.java | 3 +-- .../org/hso/ecommerce/entities/supplier/ArticleOffer.java | 2 ++ .../org/hso/ecommerce/repos/shop/ArticleRepository.java | 8 +++----- prototype/src/main/resources/application.properties | 2 +- prototype/src/main/resources/templates/shop/checkout.html | 3 ++- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/prototype/.gitignore b/prototype/.gitignore index 4fac9a9..52b353d 100644 --- a/prototype/.gitignore +++ b/prototype/.gitignore @@ -1,4 +1,4 @@ -./e-commerce.db +e-commerce.db ./build ./gradle ./out diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index a6159d9..6d8cea9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -13,12 +13,10 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; -import java.util.Optional; @Controller @RequestMapping("/shop/articles") @@ -53,7 +51,7 @@ public class ShopArticleController { model.addAttribute("inStock", false); } - List

commercialArticles = articleRepository.getCommercialisedArticles("2"); + List
commercialArticles = articleRepository.getAdvertisedArticles(); model.addAttribute("commercialArticles", commercialArticles); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 69dce5d..73c3241 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; -import javax.swing.*; import java.util.List; @Controller @@ -27,7 +26,7 @@ public class ShopIndexController { @GetMapping("/shop/") public String shop(Model model, HttpSession session) { - List
commercialArticles = articleRepository.getCommercialisedArticles("4"); + List
commercialArticles = articleRepository.getAdvertisedArticles(); model.addAttribute("commercialArticles", commercialArticles); //check if logged in diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java index 005b0b9..ce02d13 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java @@ -19,4 +19,6 @@ public class ArticleOffer { public String articleNumber; public int vatPercent; + + public boolean should_be_advertised; } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 42ef5ee..a22f191 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -13,16 +13,14 @@ import java.util.Optional; @Repository public interface ArticleRepository extends JpaRepository { - @Query(nativeQuery = true, value = "Select * FROM articles where articles.id = :articleId") + @Query("Select a FROM Article a where a.id = :articleId") Article findArticleById(@Param("articleId") long articleId); - //TODO: getCommercialisedArticles(int quantity) - @Query("SELECT c FROM User c WHERE c.email = :quantity") - List
getCommercialisedArticles(@Param("quantity") String quantity); + @Query("SELECT a from Article a join a.related ao where ao.should_be_advertised = true") + List
getAdvertisedArticles(); - //TODO: getLastOrderedArticles(int quantity) @Query("SELECT c FROM User c WHERE c.email = :quantity") List
getLastOrderedArticles(@Param("quantity") String quantity); diff --git a/prototype/src/main/resources/application.properties b/prototype/src/main/resources/application.properties index b62e80f..18ec88c 100644 --- a/prototype/src/main/resources/application.properties +++ b/prototype/src/main/resources/application.properties @@ -3,7 +3,7 @@ spring.resources.cache.cachecontrol.maxAge=P0D # LOGGING logging.level.org.springframework.web=WARN # DATABASE -spring.datasource.url=jdbc:sqlite:./test.db +spring.datasource.url=jdbc:sqlite:./e-commerce.db spring.datasource.driverClassName=org.sqlite.JDBC spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLiteDialect spring.jpa.hibernate.ddl-auto=update diff --git a/prototype/src/main/resources/templates/shop/checkout.html b/prototype/src/main/resources/templates/shop/checkout.html index cf02b5c..9962649 100644 --- a/prototype/src/main/resources/templates/shop/checkout.html +++ b/prototype/src/main/resources/templates/shop/checkout.html @@ -44,7 +44,8 @@

Date: Fri, 8 May 2020 10:37:42 +0200 Subject: [PATCH 013/203] Advertised Articles now shown correctly --- .../action/shop/GetRandomArticlesAction.java | 20 +++++++++++++++++++ .../shop/ShopArticleController.java | 6 ++++-- .../controller/shop/ShopIndexController.java | 6 ++++-- .../resources/templates/shop/articles/id.html | 4 ++-- .../main/resources/templates/shop/index.html | 2 +- 5 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java new file mode 100644 index 0000000..2c6fd55 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.entities.shop.Article; + +import java.util.ArrayList; +import java.util.List; + +public class GetRandomArticlesAction { + + public static List
getRandomArticles(int quantity, List
advertisedArticles) { + List
randomisedArticles = new ArrayList
(); + for (int i = 0; i < quantity; i++) { + int index = (int) (Math.random() * advertisedArticles.size()); + randomisedArticles.add(advertisedArticles.remove(index)); + if(advertisedArticles.size() == 0) + break; + } + return randomisedArticles; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 6d8cea9..eb13de9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.controller.shop; import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; import org.hso.ecommerce.repos.shop.ArticleRepository; @@ -16,6 +17,7 @@ import javax.servlet.http.HttpSession; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; @Controller @@ -36,7 +38,7 @@ public class ShopArticleController { Article article = articleRepository.findArticleById(id); - if(article == null) { + if (article == null) { request.setAttribute("error", "Der Artikel wurde nicht gefunden."); response.setStatus(HttpServletResponse.SC_NOT_FOUND); return "error/404"; @@ -51,7 +53,7 @@ public class ShopArticleController { model.addAttribute("inStock", false); } - List
commercialArticles = articleRepository.getAdvertisedArticles(); + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(4, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 73c3241..8844c8b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -1,5 +1,6 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -9,6 +10,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; +import java.util.ArrayList; import java.util.List; @Controller @@ -26,7 +28,7 @@ public class ShopIndexController { @GetMapping("/shop/") public String shop(Model model, HttpSession session) { - List
commercialArticles = articleRepository.getAdvertisedArticles(); + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); //check if logged in @@ -39,7 +41,7 @@ public class ShopIndexController { if (isLoggedIn) { List
suggestedArticles = articleRepository.getLastOrderedArticles("4"); - if(suggestedArticles.size() > 0) { + if (suggestedArticles.size() > 0) { model.addAttribute("suggestedArticles", suggestedArticles); hasOrders = true; } diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index c82aedb..73de367 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -27,7 +27,7 @@

- +
@@ -56,7 +56,7 @@

Weitere Schnäppchen

- +

diff --git a/prototype/src/main/resources/templates/shop/index.html b/prototype/src/main/resources/templates/shop/index.html index bb6e4cd..f38b012 100644 --- a/prototype/src/main/resources/templates/shop/index.html +++ b/prototype/src/main/resources/templates/shop/index.html @@ -21,7 +21,7 @@
- +

From 1bd267a26dd55173090605998a289074838f1784 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 8 May 2020 11:44:30 +0200 Subject: [PATCH 014/203] implement personalized suggestions --- .../controller/shop/ShopArticleController.java | 10 +++++----- .../ecommerce/controller/shop/ShopIndexController.java | 6 ++++-- .../hso/ecommerce/repos/shop/ArticleRepository.java | 8 ++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index eb13de9..3b906d0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -74,11 +74,11 @@ public class ShopArticleController { Article article = articleRepository.findArticleById(id); -// if (!article.isPresent()) { -// request.setAttribute("error", "Der Artikel wurde nicht gefunden."); -// response.setStatus(HttpServletResponse.SC_NOT_FOUND); -// return "error/404"; -// } + if (article == null) { + request.setAttribute("error", "Der Artikel wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; + } if (setAmount != null && setAmount) { shoppingCart.setArticleCount(article, quantity); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 8844c8b..9fa8c37 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -2,6 +2,7 @@ package org.hso.ecommerce.controller.shop; import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -31,7 +32,7 @@ public class ShopIndexController { List

commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); - //check if logged in + boolean isLoggedIn = false; boolean hasOrders = false; if (session != null && session.getAttribute("id") != null) { @@ -40,7 +41,8 @@ public class ShopIndexController { } if (isLoggedIn) { - List
suggestedArticles = articleRepository.getLastOrderedArticles("4"); + List
suggestedArticles = articleRepository.getLastOrderedArticles(); + suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,3) : suggestedArticles; //only latest 4 articles if (suggestedArticles.size() > 0) { model.addAttribute("suggestedArticles", suggestedArticles); hasOrders = true; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index a22f191..933959f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -13,16 +13,16 @@ import java.util.Optional; @Repository public interface ArticleRepository extends JpaRepository { - @Query("Select a FROM Article a where a.id = :articleId") + @Query("SELECT a FROM Article a WHERE a.id = :articleId") Article findArticleById(@Param("articleId") long articleId); - @Query("SELECT a from Article a join a.related ao where ao.should_be_advertised = true") + @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.should_be_advertised = true") List
getAdvertisedArticles(); - @Query("SELECT c FROM User c WHERE c.email = :quantity") - List
getLastOrderedArticles(@Param("quantity") String quantity); + @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") + List
getLastOrderedArticles(); } From c0ce1e23c3ac3f5e5cd6a79c8998f988d53c3487 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 8 May 2020 13:01:29 +0200 Subject: [PATCH 015/203] fix better quantities for UI --- .../action/shop/GetRandomArticlesAction.java | 5 ++--- .../controller/shop/ShopArticleController.java | 12 +++++++++--- .../controller/shop/ShopIndexController.java | 5 ++--- .../hso/ecommerce/repos/shop/ArticleRepository.java | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java index 2c6fd55..c299b92 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java @@ -9,11 +9,10 @@ public class GetRandomArticlesAction { public static List
getRandomArticles(int quantity, List
advertisedArticles) { List
randomisedArticles = new ArrayList
(); - for (int i = 0; i < quantity; i++) { + int loopcount = quantity > advertisedArticles.size() ? advertisedArticles.size() : quantity; + for (int i = 0; i < loopcount; i++) { int index = (int) (Math.random() * advertisedArticles.size()); randomisedArticles.add(advertisedArticles.remove(index)); - if(advertisedArticles.size() == 0) - break; } return randomisedArticles; } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 3b906d0..03da5b7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -4,7 +4,9 @@ import org.apache.tomcat.util.http.fileupload.IOUtils; import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; @@ -27,6 +29,9 @@ public class ShopArticleController { @Autowired private final ArticleRepository articleRepository = null; + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + @GetMapping("/{id}") public String shopArticlesById(Model model, @PathVariable("id") Long id, @@ -46,14 +51,15 @@ public class ShopArticleController { model.addAttribute("article", article); - //TODO: Check if in Stock - if (false) { + + //if (warehouseBookingPositionSlotEntryRepository.getByArticle(id).get(0).newSumSlot > 0) { //TODO: use this as soon as warehouse works + if (true) { model.addAttribute("inStock", true); } else { model.addAttribute("inStock", false); } - List
commercialArticles = GetRandomArticlesAction.getRandomArticles(4, articleRepository.getAdvertisedArticles()); + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(3, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 9fa8c37..e660eb0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -32,7 +32,6 @@ public class ShopIndexController { List
commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); - boolean isLoggedIn = false; boolean hasOrders = false; if (session != null && session.getAttribute("id") != null) { @@ -41,8 +40,8 @@ public class ShopIndexController { } if (isLoggedIn) { - List
suggestedArticles = articleRepository.getLastOrderedArticles(); - suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,3) : suggestedArticles; //only latest 4 articles + List
suggestedArticles = articleRepository.getOrderedArticles(); + suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,4) : suggestedArticles; //only latest 4 articles if (suggestedArticles.size() > 0) { model.addAttribute("suggestedArticles", suggestedArticles); hasOrders = true; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 933959f..2a88b7e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -22,7 +22,7 @@ public interface ArticleRepository extends JpaRepository { @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") - List
getLastOrderedArticles(); + List
getOrderedArticles(); } From 8dd4cc4459152ff0d9306d9858f508850ed30729 Mon Sep 17 00:00:00 2001 From: Seil0 Date: Sat, 9 May 2020 19:31:57 +0200 Subject: [PATCH 016/203] use application.properties when run via bootRun --- prototype/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/build.gradle b/prototype/build.gradle index 2769783..ad8b14c 100644 --- a/prototype/build.gradle +++ b/prototype/build.gradle @@ -35,5 +35,5 @@ group 'org.hso' version '0.1.0' bootRun { - args = ["--spring.profiles.active=dev"] + args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties\""] } From 69b5b33af80d96d2d42ef991e6241a5f8a68af45 Mon Sep 17 00:00:00 2001 From: Seil0 Date: Sat, 9 May 2020 19:58:31 +0200 Subject: [PATCH 017/203] fix typo in 8dd4cc4459 --- prototype/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/build.gradle b/prototype/build.gradle index ad8b14c..c7defd9 100644 --- a/prototype/build.gradle +++ b/prototype/build.gradle @@ -35,5 +35,5 @@ group 'org.hso' version '0.1.0' bootRun { - args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties\""] + args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties"] } From d66b3d1385877d9df7c0483e38ab1b80660fd659 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 9 May 2020 23:01:25 +0200 Subject: [PATCH 018/203] changed listedArticles Request to own Controller --- .../hso/ecommerce/app/RequestController.java | 2 + .../intern/InternArticleController.java | 51 ++++++++++++++++++- .../repos/shop/ArticleRepository.java | 20 ++++---- .../intern/listedArticles/index.html | 2 +- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 6ef7624..3a1259e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -157,6 +157,7 @@ public class RequestController { public String intern() { return "intern/index"; } + /* @GetMapping("/intern/listedArticles/") public String internListedArticles() { @@ -168,6 +169,7 @@ public class RequestController { return "intern/listedArticles/id"; } +*/ @GetMapping("/intern/articles/") public String internArticles() { diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index d078f54..29fe361 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -1,8 +1,57 @@ package org.hso.ecommerce.controller.intern; + + +import java.util.List; + +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + + + @Controller -//@RequestMapping("...") +@RequestMapping("intern/listedArticles") public class InternArticleController { + + @Autowired + private final ArticleRepository articleRepository = null; + + /* + @Autowired + public InternArticleController(ArticleRepository articleRepository) + { + this.articleRepository = articleRepository; + } + */ + + @GetMapping("/") + public String internListedArticles(Model model) { + + + List
articles = articleRepository.findAll(); + + System.out.println(articles.size()); + + + + // model.addAttribute("ListedArticles", bookService.findAll()); + + + + return "intern/listedArticles/index"; + } + + + + @GetMapping("/{id}") + public String internListedArticlesId() { + return "intern/listedArticles/id"; + } + + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 2a88b7e..27223db 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -11,19 +11,19 @@ import java.util.List; import java.util.Optional; @Repository -public interface ArticleRepository extends JpaRepository { +public interface ArticleRepository extends JpaRepository +{ - @Query("SELECT a FROM Article a WHERE a.id = :articleId") - Article findArticleById(@Param("articleId") long articleId); + @Query("SELECT a FROM Article a WHERE a.id = :articleId") + Article findArticleById(@Param("articleId") long articleId); + @Query("SELECT a FROM Article a") + List
findAll(); - @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.should_be_advertised = true") - List
getAdvertisedArticles(); - - - @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") - List
getOrderedArticles(); + @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.should_be_advertised = true") + List
getAdvertisedArticles(); + @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") + List
getOrderedArticles(); } - diff --git a/prototype/src/main/resources/templates/intern/listedArticles/index.html b/prototype/src/main/resources/templates/intern/listedArticles/index.html index ea62db9..adce5ef 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/index.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/index.html @@ -51,7 +51,7 @@

KameraKameraÖ 100,50 EUR (84.45 EUR) Úberwachung, Elektronik + th:text="${#numbers.formatDecimal(item.article.getPriceGross() * 0.01, 1, 'POINT', 2, 'COMMA')}"> EUR x @@ -106,7 +106,7 @@ Musterstraße 4
Artikel (Netto) + th:text="${#numbers.formatDecimal(checkoutTotals.net * 0.01, 1, 'POINT', 2, 'COMMA')}"> EUR
Umsatzsteuer (%) + th:text="${#numbers.formatDecimal(item.getValue() * 0.01, 1, 'POINT', 2, 'COMMA')}"> EUR
Gesamt: + th:text="${#numbers.formatDecimal(checkoutTotals.total * 0.01, 1, 'POINT', 2, 'COMMA')}"> EUR
Bild Name Preis (Netto) KategorienLagerbestand (Aktiv)ArtikelId (bearbeiten)
KameraÖ100,50 EUR (84.45 EUR)Úberwachung, Elektronik301 5051890
Earbuds63,95 EUR(53,73 EUR)Kopfhörer, Elektronik12 84013850
USB-Magic Light11,90 EUR (10,00 EUR)Sonstiges, Elektronik384015784
3D Magic Stativ15,99 EUR (13.44 EUR)Úberwachung, Elektronik421354564
Ersatzfernbedinung7,95 EUR (6.68 EUR)Úberwachung, Elektronik045654566LagerbestandAngebotID

From a62e3e8a63ca3809f093e1b813ab8a7ef43a468d Mon Sep 17 00:00:00 2001 From: Hannes Date: Sun, 10 May 2020 15:28:39 +0200 Subject: [PATCH 025/203] fix only show ordered Articles from specific Customer --- .../hso/ecommerce/controller/shop/ShopIndexController.java | 7 +++---- .../org/hso/ecommerce/repos/shop/ArticleRepository.java | 4 ++-- prototype/src/main/resources/templates/shop/index.html | 5 ++++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 9d1e47a..4897445 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -34,14 +34,13 @@ public class ShopIndexController { boolean isLoggedIn = false; boolean hasOrders = false; + if (session != null && session.getAttribute("userId") != null) { long userId = (long) session.getAttribute("userId"); isLoggedIn = true; - } - if (isLoggedIn) { - List
suggestedArticles = articleRepository.getOrderedArticles(); - suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,4) : suggestedArticles; //only latest 4 articles + List
suggestedArticles = articleRepository.getOrderedArticles(userId); + suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0, 4) : suggestedArticles; //only latest 4 articles if (suggestedArticles.size() > 0) { model.addAttribute("suggestedArticles", suggestedArticles); hasOrders = true; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 90b6d11..0b108f1 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -19,8 +19,8 @@ public interface ArticleRepository extends JpaRepository { List
getAdvertisedArticles(); - @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") - List
getOrderedArticles(); + @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a WHERE c.id = :customerId ORDER BY co.id DESC") + List
getOrderedArticles(long customerId); } diff --git a/prototype/src/main/resources/templates/shop/index.html b/prototype/src/main/resources/templates/shop/index.html index 300471a..54985ad 100644 --- a/prototype/src/main/resources/templates/shop/index.html +++ b/prototype/src/main/resources/templates/shop/index.html @@ -17,7 +17,10 @@

Angebote

-
+
+

Momentan gibt es keine Angebote

+
+
From 7a42122a9a1bd340773b478e8b9833bb0f694e4d Mon Sep 17 00:00:00 2001 From: Seil0 Date: Sun, 10 May 2020 15:31:20 +0200 Subject: [PATCH 026/203] fix typo --- prototype/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/build.gradle b/prototype/build.gradle index ad8b14c..c7defd9 100644 --- a/prototype/build.gradle +++ b/prototype/build.gradle @@ -35,5 +35,5 @@ group 'org.hso' version '0.1.0' bootRun { - args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties\""] + args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties"] } From 777786f5b6faa1def5cf562983511fc24de7ef93 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 10 May 2020 15:39:24 +0200 Subject: [PATCH 027/203] Remove old missleading comment --- .../warehouse/WarehouseBookingPositionSlotEntryRepository.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java index 962231a..c3eab4b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -10,9 +10,8 @@ import java.util.List; @Repository public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository { - // TODO this is wrong. revisit. - // @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e, Slot s WHERE e.slot = s AND e.article = :article GROUP BY e.slot.slotNum HAVING max(e.id)") @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) List getByArticle(long article); + } From e406918ca28d7a41f7e6b7ac2bca10898b3ad2e7 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 10 May 2020 16:57:33 +0200 Subject: [PATCH 028/203] Remove commend out code --- .../hso/ecommerce/app/RequestController.java | 71 ++----------------- 1 file changed, 4 insertions(+), 67 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 6ef7624..3adf74e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -1,10 +1,12 @@ package org.hso.ecommerce.app; -import org.hso.ecommerce.repos.user.UserRepository; import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -22,11 +24,6 @@ public class RequestController { static int notSoRandom = 0; -// @GetMapping("/") -// public String home() { -// return "redirect:/shop/"; -// } - @GetMapping("/login") public String login() { return "login"; @@ -88,71 +85,11 @@ public class RequestController { return "redirect:/"; } -// @GetMapping("/shop/") -// public String shop() { -// return "shop/index"; -// } - @GetMapping("/shop/search") public String shopSearch() { return "shop/search"; } -/* @GetMapping("/shop/checkout") - public String shopCheckout(HttpSession session, HttpServletRequest request) { - session.setAttribute("afterLogin", request.getRequestURI()); - return "shop/checkout"; - } - - @PostMapping("/shop/checkoutFinish") - public String shopCheckoutFinish() { - return "shop/checkoutFinish"; - } - - @GetMapping("/shop/checkoutFinish") - public String shopCheckoutFinishGET() { - return "shop/checkoutFinish"; - }*/ - -// @GetMapping("/shop/articles/{id}") -// public String shopArticlesById() { -// return "shop/articles/id"; -// } -// -// @PostMapping("/shop/articles/{id}") -// public String shopArticlesByIdBuy(HttpSession session, -// @RequestAttribute(value = "user", required = false) User customer, -// @PathVariable("id") Integer id, -// @RequestParam("fastcheckout") Boolean fastcheckout -// ) { -// if (customer != null) { -// if (!fastcheckout) { -// return "shop/articles/post_add"; -// } else { -// return "shop/checkout"; -// } -// } else { -// session.setAttribute("afterLogin", "/shop/articles/" + id); -// return "redirect:/login"; -// } -// } - -// @GetMapping("/about") -// public String about() { -// return "about"; -// } -// -// @GetMapping("/terms") -// public String terms() { -// return "terms"; -// } -// -// @GetMapping("/privacy") -// public String privacy() { -// return "privacy"; -// } - - @GetMapping("/intern/") public String intern() { return "intern/index"; From 713c8ebe86faffcde94f076956bdc5d02fe3ddaa Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 10 May 2020 17:03:40 +0200 Subject: [PATCH 029/203] Comment empty constructors --- .../java/org/hso/ecommerce/entities/booking/BookingReason.java | 3 +-- .../ecommerce/entities/warehouse/WarehouseBookingReason.java | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java index f973d26..78e8ab5 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java @@ -28,11 +28,10 @@ public class BookingReason { @ManyToOne(optional = true) public SupplierOrder supplierOrder; + // Default Constructor is needed for construction by ORM public BookingReason() { } - ; - public BookingReason(CustomerOrder order) { this.customerOrder = order; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java index 08ee80a..e287141 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java @@ -24,6 +24,7 @@ public class WarehouseBookingReason { public boolean isManuel; + // Default Constructor is needed for construction by ORM public WarehouseBookingReason() { } From bd2aeb63f6ea5a3c0d2f78cde2ff8f5691061f30 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 10 May 2020 17:05:19 +0200 Subject: [PATCH 030/203] Remove unneeded comment --- .../entities/warehouse/WarehouseBookingPositionSlotEntry.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java index 766398f..444b921 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java @@ -18,8 +18,7 @@ public class WarehouseBookingPositionSlotEntry { @ManyToOne public Article article; - // Can;t do, does not work when created in action. - //public int newSumArticles; + @NotNull public int newSumSlot; @NotNull From 8f24aee350a302d7f638a566e6b116af58ad6271 Mon Sep 17 00:00:00 2001 From: Hannes Date: Sun, 10 May 2020 17:16:17 +0200 Subject: [PATCH 031/203] fix add should_be_advertised to script --- prototype/scripts/addarticles.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/scripts/addarticles.sql b/prototype/scripts/addarticles.sql index 8f976a7..f7e5f5b 100644 --- a/prototype/scripts/addarticles.sql +++ b/prototype/scripts/addarticles.sql @@ -1,5 +1,5 @@ -INSERT INTO article_offers ("manufacturer", "article_number", "vat_percent") -VALUES ("McDonalds", "1", 7); +INSERT INTO article_offers ("manufacturer", "article_number", "vat_percent", "should_be_advertised") +VALUES ("McDonalds", "1", 7, 1); INSERT INTO articles ("related_id", "shop_price_per_unit_net_cent", "warehouse_units_per_slot", "should_reorder", "reorder_max_price", "title", "description", "image_id") VALUES (1, 19.99, 10, 1, 15, "Huge Hamburger", "This huge Hamburger is awesome!", NULL); From 530034a40ed6f9e4d8fc7edb295c43c68f896eb7 Mon Sep 17 00:00:00 2001 From: Hannes Date: Sun, 10 May 2020 17:19:18 +0200 Subject: [PATCH 032/203] fix unused imports and unneeded empty lines --- .../controller/shop/ShopArticleController.java | 14 -------------- .../controller/shop/ShopIndexController.java | 2 -- .../org/hso/ecommerce/entities/shop/Image.java | 3 +-- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 03da5b7..2fc0aa2 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -4,7 +4,6 @@ import org.apache.tomcat.util.http.fileupload.IOUtils; import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; -import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -19,7 +18,6 @@ import javax.servlet.http.HttpSession; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; import java.util.List; @Controller @@ -38,20 +36,15 @@ public class ShopArticleController { HttpServletRequest request, HttpServletResponse response ) { - - Article article = articleRepository.findArticleById(id); - if (article == null) { request.setAttribute("error", "Der Artikel wurde nicht gefunden."); response.setStatus(HttpServletResponse.SC_NOT_FOUND); return "error/404"; } - model.addAttribute("article", article); - //if (warehouseBookingPositionSlotEntryRepository.getByArticle(id).get(0).newSumSlot > 0) { //TODO: use this as soon as warehouse works if (true) { model.addAttribute("inStock", true); @@ -62,7 +55,6 @@ public class ShopArticleController { List
commercialArticles = GetRandomArticlesAction.getRandomArticles(3, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); - return "shop/articles/id"; } @@ -76,8 +68,6 @@ public class ShopArticleController { @RequestParam(value = "set_amount", required = false) Boolean setAmount, @RequestParam("fastcheckout") Boolean fastcheckout ) { - - Article article = articleRepository.findArticleById(id); if (article == null) { @@ -104,14 +94,10 @@ public class ShopArticleController { HttpServletResponse response, @PathVariable("id") Long id ) throws IOException { - Article article = articleRepository.findArticleById(id); - InputStream in = new FileInputStream(article.image.path); response.setContentType(MediaType.IMAGE_JPEG_VALUE); IOUtils.copy(in, response.getOutputStream()); } - - } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 4897445..15b6067 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -2,7 +2,6 @@ package org.hso.ecommerce.controller.shop; import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; -import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -11,7 +10,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; -import java.util.ArrayList; import java.util.List; @Controller diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java index d27d415..6609853 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java @@ -12,5 +12,4 @@ public class Image { public long id; public String path; -} - +} \ No newline at end of file From edcdb2e9cfd04262ab48c125768861d169b277fe Mon Sep 17 00:00:00 2001 From: Hannes Date: Sun, 10 May 2020 17:25:48 +0200 Subject: [PATCH 033/203] fix use Math.min for loopcount --- .../org/hso/ecommerce/action/shop/GetRandomArticlesAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java index c299b92..62c70c7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java @@ -9,7 +9,7 @@ public class GetRandomArticlesAction { public static List
getRandomArticles(int quantity, List
advertisedArticles) { List
randomisedArticles = new ArrayList
(); - int loopcount = quantity > advertisedArticles.size() ? advertisedArticles.size() : quantity; + int loopcount = Math.min(quantity, advertisedArticles.size()); for (int i = 0; i < loopcount; i++) { int index = (int) (Math.random() * advertisedArticles.size()); randomisedArticles.add(advertisedArticles.remove(index)); From 8a28aabaf3bc619d26efcc3ff45ae4b1f502d9b8 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 1 May 2020 12:19:52 +0200 Subject: [PATCH 034/203] ShopIndexController initial implementation --- .../hso/ecommerce/app/RequestController.java | 44 +++++------ .../controller/shop/ShopIndexController.java | 77 ++++++++++++++++++- .../main/resources/templates/shop/index.html | 53 ++----------- 3 files changed, 104 insertions(+), 70 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 48c4f18..1e463fc 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -22,10 +22,10 @@ public class RequestController { static int notSoRandom = 0; - @GetMapping("/") - public String home() { - return "redirect:/shop/"; - } +// @GetMapping("/") +// public String home() { +// return "redirect:/shop/"; +// } @GetMapping("/login") public String login() { @@ -88,10 +88,10 @@ public class RequestController { return "redirect:/"; } - @GetMapping("/shop/") - public String shop() { - return "shop/index"; - } +// @GetMapping("/shop/") +// public String shop() { +// return "shop/index"; +// } @GetMapping("/shop/search") public String shopSearch() { @@ -137,20 +137,20 @@ public class RequestController { // } // } - @GetMapping("/about") - public String about() { - return "about"; - } - - @GetMapping("/terms") - public String terms() { - return "terms"; - } - - @GetMapping("/privacy") - public String privacy() { - return "privacy"; - } +// @GetMapping("/about") +// public String about() { +// return "about"; +// } +// +// @GetMapping("/terms") +// public String terms() { +// return "terms"; +// } +// +// @GetMapping("/privacy") +// public String privacy() { +// return "privacy"; +// } @GetMapping("/intern/") diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 0f423db..5d442d6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -1,8 +1,83 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.entities.shop.Article; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; @Controller -//@RequestMapping("...") +@RequestMapping("/") public class ShopIndexController { + + @GetMapping("/") + public String home() { + return "redirect:/shop/"; + } + + @GetMapping("/shop/") + public String shop(Model model) { + + ArrayList
dummyArticles = getArticles(); + + model.addAttribute("articles", dummyArticles); + return "shop/index"; + } + + @GetMapping("/about") + public String about() { + return "about"; + } + + @GetMapping("/terms") + public String terms() { + return "terms"; + } + + @GetMapping("/privacy") + public String privacy() { + return "privacy"; + } + + + + public ArrayList
getArticles(){ + ArrayList
dummyArticles = new ArrayList
(); + + Article d1 = new Article(); + d1.description = "this is dummy1"; + d1.title = "dummy1"; + d1.shopPricePerUnitNetCent = 1500; + d1.id = 1234; + dummyArticles.add(d1); + + Article d2 = new Article(); + d2.description = "this is dummy2"; + d2.title = "dummy2"; + d2.shopPricePerUnitNetCent = 2000; + d2.id = 2345; + dummyArticles.add(d2); + + Article d3 = new Article(); + d3.description = "this is dummy3"; + d3.title = "dummy3"; + d3.shopPricePerUnitNetCent = 2500; + d3.id = 3456; + dummyArticles.add(d3); + + Article d4 = new Article(); + d4.description = "this is dummy4"; + d4.title = "dummy4"; + d4.shopPricePerUnitNetCent = 3000; + d4.id = 4567; + dummyArticles.add(d4); + + return dummyArticles; + } + + } diff --git a/prototype/src/main/resources/templates/shop/index.html b/prototype/src/main/resources/templates/shop/index.html index 68a4a1f..a019c8a 100644 --- a/prototype/src/main/resources/templates/shop/index.html +++ b/prototype/src/main/resources/templates/shop/index.html @@ -18,54 +18,13 @@
-
+
+ - -

Lorem Ipsum

-

25.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

10.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

25.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

10.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

-
-
-
- - -

Lorem Ipsum

-

44.14 EUR

-

- Als Gregor Samsa eines Morgens aus unruhigen Träumen erwachte. -

+ +

+

+

From e9e072b26b4b3d2b746704d18e489a2e604aa21f Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 1 May 2020 14:40:43 +0200 Subject: [PATCH 035/203] ShopIndexController without databasestuff --- .../controller/shop/ShopIndexController.java | 31 ++++++++++++++++--- .../main/resources/templates/shop/index.html | 26 ++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 5d442d6..0be1a50 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -6,6 +6,7 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpSession; import javax.swing.*; import java.awt.*; import java.util.ArrayList; @@ -20,11 +21,32 @@ public class ShopIndexController { } @GetMapping("/shop/") - public String shop(Model model) { + public String shop(Model model, HttpSession session) { - ArrayList
dummyArticles = getArticles(); + //TODO: get commercialised Articles + ArrayList
commercialArticles = getArticles(); + model.addAttribute("commercialArticles", commercialArticles); + + //check if logged in + boolean isLoggedIn = false; + boolean hasOrders = false; + if (session != null && session.getAttribute("id") != null) { + long userId = (long) session.getAttribute("id"); + isLoggedIn = true; + if (false) { + hasOrders = true; //TODO: Find out whether user has orders! + } + } + model.addAttribute("isLoggedIn", isLoggedIn); + model.addAttribute("hasOrders", hasOrders); + + + if (hasOrders) { + //TODO: get up to last 4 Orders + ArrayList
suggestedArticles = getArticles(); + model.addAttribute("suggestedArticles", suggestedArticles); + } - model.addAttribute("articles", dummyArticles); return "shop/index"; } @@ -44,8 +66,7 @@ public class ShopIndexController { } - - public ArrayList
getArticles(){ + public ArrayList
getArticles() { ArrayList
dummyArticles = new ArrayList
(); Article d1 = new Article(); diff --git a/prototype/src/main/resources/templates/shop/index.html b/prototype/src/main/resources/templates/shop/index.html index a019c8a..c3c7535 100644 --- a/prototype/src/main/resources/templates/shop/index.html +++ b/prototype/src/main/resources/templates/shop/index.html @@ -18,7 +18,7 @@
-
+
@@ -38,7 +38,9 @@
From 4338f188f0e33aa0359d473908be7700b14b6caf Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 17:22:35 +0200 Subject: [PATCH 036/203] fix local merge conflicts in req ctrl --- .../main/java/org/hso/ecommerce/app/RequestController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 1e463fc..6ef7624 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -98,7 +98,7 @@ public class RequestController { return "shop/search"; } - @GetMapping("/shop/checkout") +/* @GetMapping("/shop/checkout") public String shopCheckout(HttpSession session, HttpServletRequest request) { session.setAttribute("afterLogin", request.getRequestURI()); return "shop/checkout"; @@ -112,7 +112,7 @@ public class RequestController { @GetMapping("/shop/checkoutFinish") public String shopCheckoutFinishGET() { return "shop/checkoutFinish"; - } + }*/ // @GetMapping("/shop/articles/{id}") // public String shopArticlesById() { From 7d83d723c184d686be28dbf195f18fc7ab24c8d0 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 17:24:25 +0200 Subject: [PATCH 037/203] Implement add Items to ShoppingCart --- prototype/scripts/addarticles.sql | 6 ++ .../java/org/hso/ecommerce/app/Config.java | 3 + .../components/ShoppingCartInterceptor.java | 43 +++++++++++++ .../shop/ShopArticleController.java | 35 ++++++---- .../shop/ShopCheckoutController.java | 24 ++++++- .../ecommerce/entities/shop/ShoppingCart.java | 64 +++++++++++++++++++ .../repos/shop/ArticleRepository.java | 11 ++++ .../resources/templates/fragments/header.html | 2 +- .../resources/templates/shop/articles/id.html | 5 +- 9 files changed, 174 insertions(+), 19 deletions(-) create mode 100644 prototype/scripts/addarticles.sql create mode 100644 prototype/src/main/java/org/hso/ecommerce/components/ShoppingCartInterceptor.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java diff --git a/prototype/scripts/addarticles.sql b/prototype/scripts/addarticles.sql new file mode 100644 index 0000000..8f976a7 --- /dev/null +++ b/prototype/scripts/addarticles.sql @@ -0,0 +1,6 @@ +INSERT INTO article_offers ("manufacturer", "article_number", "vat_percent") +VALUES ("McDonalds", "1", 7); + +INSERT INTO articles ("related_id", "shop_price_per_unit_net_cent", "warehouse_units_per_slot", "should_reorder", "reorder_max_price", "title", "description", "image_id") +VALUES (1, 19.99, 10, 1, 15, "Huge Hamburger", "This huge Hamburger is awesome!", NULL); + diff --git a/prototype/src/main/java/org/hso/ecommerce/app/Config.java b/prototype/src/main/java/org/hso/ecommerce/app/Config.java index 7eda415..961f7ba 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/Config.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/Config.java @@ -3,6 +3,7 @@ package org.hso.ecommerce.app; import org.hso.ecommerce.components.ErrorDemoInterceptor; import org.hso.ecommerce.components.InfoDemoInterceptor; import org.hso.ecommerce.components.LoginIntercepter; +import org.hso.ecommerce.components.ShoppingCartInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; @@ -23,5 +24,7 @@ public class Config implements WebMvcConfigurer { registry.addInterceptor(buildLoginIntercepter()); registry.addInterceptor(new ErrorDemoInterceptor()); registry.addInterceptor(new InfoDemoInterceptor()); + registry.addInterceptor(new ShoppingCartInterceptor()); + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/components/ShoppingCartInterceptor.java b/prototype/src/main/java/org/hso/ecommerce/components/ShoppingCartInterceptor.java new file mode 100644 index 0000000..3a67897 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/components/ShoppingCartInterceptor.java @@ -0,0 +1,43 @@ +package org.hso.ecommerce.components; + +import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +public class ShoppingCartInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle( + HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + + HttpSession session = request.getSession(); + Object shoppingCart = session.getAttribute("shoppingCart"); + + if (shoppingCart == null) { + shoppingCart = new ShoppingCart(); + } + + request.setAttribute("shoppingCart", shoppingCart); + + return true; + } + + @Override + public void postHandle( + HttpServletRequest request, HttpServletResponse response, Object handler, + ModelAndView modelAndView) throws Exception { + + HttpSession session = request.getSession(); + Object shoppingCart = request.getAttribute("shoppingCart"); + session.setAttribute("shoppingCart", shoppingCart); + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, + Object handler, Exception exception) throws Exception { + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 653286e..bd544b3 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -1,7 +1,9 @@ package org.hso.ecommerce.controller.shop; import org.hso.ecommerce.entities.shop.Article; -import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @@ -9,11 +11,15 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Controller @RequestMapping("/shop/articles") public class ShopArticleController { + @Autowired + private final ArticleRepository articleRepository = null; + @GetMapping("/{id}") public String shopArticlesById(Model model, @PathVariable("id") Integer Id) { @@ -54,22 +60,23 @@ public class ShopArticleController { @PostMapping("/{id}") public String shopArticlesByIdBuy(HttpSession session, - @RequestAttribute(value = "user", required = false) User customer, -// @RequestAttribute(value = "shoppingCart", required = true) ShoppingCart shoppingCart, - @PathVariable("id") Integer id, + @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, + @PathVariable("id") Long id, + @RequestParam("quantity") Integer quantity, @RequestParam("fastcheckout") Boolean fastcheckout ) { - if (customer != null) { - //TODO: Add Article to Shopping Cart - - if (!fastcheckout) { - return "shop/articles/post_add"; - } else { - return "shop/checkout"; - } + + Optional
article = articleRepository.findById(id); + if (!article.isPresent()) { + throw new RuntimeException("Article not found!"); + } + + shoppingCart.addArticle(article.get(), quantity); + + if (!fastcheckout) { + return "shop/articles/post_add"; } else { - session.setAttribute("afterLogin", "/shop/articles/" + id); - return "redirect:/login"; + return "shop/checkout"; } } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index dd127da..1573b1b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -1,8 +1,30 @@ package org.hso.ecommerce.controller.shop; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; @Controller -//@RequestMapping("...") +@RequestMapping("/shop/") public class ShopCheckoutController { + + @GetMapping("/checkout") + public String shopCheckout(HttpSession session, HttpServletRequest request) { + session.setAttribute("afterLogin", request.getRequestURI()); + return "shop/checkout"; + } + + @PostMapping("/checkoutFinish") + public String shopCheckoutFinish() { + return "shop/checkoutFinish"; + } + + @GetMapping("/checkoutFinish") + public String shopCheckoutFinishGET() { + return "shop/checkoutFinish"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java new file mode 100644 index 0000000..f7955ec --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java @@ -0,0 +1,64 @@ +package org.hso.ecommerce.entities.shop; + +import java.util.ArrayList; + +// Not a db entity. Just for session storage +public class ShoppingCart { + + private int revision; + private ArrayList items; + + public ShoppingCart() { + revision = (int) Math.round(Math.random() * 0xFFFF); + items = new ArrayList<>(); + } + + public int getItemCount() { + int count = 0; + + for (ShoppingCartItem i : items) { + count += i.getAmount(); + } + + return count; + } + + public int getRevision() { + return revision; + } + + public void addArticle(Article article, int quantity) { + this.revision++; + + for (ShoppingCartItem i : items) { + if (i.getArticleId() == article.id) { + i.addAmount(quantity); + return; + } + } + + items.add(new ShoppingCartItem(quantity, article)); + } + + private static class ShoppingCartItem { + private int amount; + private final long articleId; + + public ShoppingCartItem(int amount, Article article) { + this.amount = amount; + this.articleId = article.id; + } + + public int getAmount() { + return amount; + } + + public void addAmount(int amount) { + this.amount += amount; + } + + public long getArticleId() { + return articleId; + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java new file mode 100644 index 0000000..0ac4bd2 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.Article; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ArticleRepository extends JpaRepository { + +} + diff --git a/prototype/src/main/resources/templates/fragments/header.html b/prototype/src/main/resources/templates/fragments/header.html index b0b52bb..7b6ec7c 100644 --- a/prototype/src/main/resources/templates/fragments/header.html +++ b/prototype/src/main/resources/templates/fragments/header.html @@ -26,7 +26,7 @@
- Warenkorb + Warenkorb ()
diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index 2a7df6d..46b7c93 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -34,10 +34,9 @@

- - From d2bf6e8c520a084fb0780c9cfe30b2652a1b49e0 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 18:59:15 +0200 Subject: [PATCH 038/203] implement checkout for non logged in users. --- .../shop/ShopArticleController.java | 9 +- .../shop/ShopCheckoutController.java | 52 ++++++- .../hso/ecommerce/entities/shop/Article.java | 8 + .../ecommerce/entities/shop/ShoppingCart.java | 29 +++- prototype/src/main/resources/db/customers.sql | 7 - .../resources/templates/shop/checkout.html | 141 ++++++------------ 6 files changed, 136 insertions(+), 110 deletions(-) delete mode 100644 prototype/src/main/resources/db/customers.sql diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index bd544b3..5670f49 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -63,6 +63,7 @@ public class ShopArticleController { @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, @PathVariable("id") Long id, @RequestParam("quantity") Integer quantity, + @RequestParam(value = "set_amount", required = false) Boolean setAmount, @RequestParam("fastcheckout") Boolean fastcheckout ) { @@ -71,12 +72,16 @@ public class ShopArticleController { throw new RuntimeException("Article not found!"); } - shoppingCart.addArticle(article.get(), quantity); + if (setAmount != null && setAmount) { + shoppingCart.setArticleCount(article.get(), quantity); + } else { + shoppingCart.addArticle(article.get(), quantity); + } if (!fastcheckout) { return "shop/articles/post_add"; } else { - return "shop/checkout"; + return "redirect:/shop/checkout"; } } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index 1573b1b..960a104 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -1,23 +1,73 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.util.ArrayList; +import java.util.TreeMap; @Controller @RequestMapping("/shop/") public class ShopCheckoutController { + @Autowired + private final ArticleRepository articleRepository = null; + @GetMapping("/checkout") - public String shopCheckout(HttpSession session, HttpServletRequest request) { + public String shopCheckout(HttpSession session, + HttpServletRequest request, + @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart) { session.setAttribute("afterLogin", request.getRequestURI()); + + CheckoutListTotals totals = new CheckoutListTotals(); + ArrayList items = new ArrayList<>(); + for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) { + Article article = articleRepository.findById(item.getArticleId()).get(); + + totals.addItem(article, item.getAmount()); + items.add(new CheckoutListItem(item.getAmount(), article)); + } + + request.setAttribute("checkoutItems", items); + request.setAttribute("checkoutTotals", totals); + return "shop/checkout"; } + public static class CheckoutListTotals { + public int net; + public TreeMap vatAmounts = new TreeMap<>(); + public int total; + + void addItem(Article article, int amount) { + net += article.shopPricePerUnitNetCent * amount; + Integer vatPos = vatAmounts.getOrDefault(article.related.vatPercent, 0) + article.getVat() * amount; + vatAmounts.put(article.related.vatPercent, vatPos); + total += article.getPriceGross() * amount; + } + } + + public static class CheckoutListItem { + public int amount; + public Article article; + public int total; + + public CheckoutListItem(int amount, Article article) { + this.amount = amount; + this.article = article; + this.total = amount * article.getPriceGross(); + } + } + @PostMapping("/checkoutFinish") public String shopCheckoutFinish() { return "shop/checkoutFinish"; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java index 6cd198e..2e066cd 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java @@ -37,4 +37,12 @@ public class Article { @ManyToMany @JoinTable(name = "article_categories_bindings") public Set categories = new HashSet<>(); + + public int getVat() { + return (shopPricePerUnitNetCent * related.vatPercent) / 100; + } + + public int getPriceGross() { + return shopPricePerUnitNetCent + getVat(); + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java index f7955ec..aee94c2 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.entities.shop; import java.util.ArrayList; +import java.util.List; // Not a db entity. Just for session storage public class ShoppingCart { @@ -13,6 +14,10 @@ public class ShoppingCart { items = new ArrayList<>(); } + public List getItems() { + return items; + } + public int getItemCount() { int count = 0; @@ -40,7 +45,25 @@ public class ShoppingCart { items.add(new ShoppingCartItem(quantity, article)); } - private static class ShoppingCartItem { + public void setArticleCount(Article article, Integer quantity) { + this.revision++; + + boolean found = false; + for (ShoppingCartItem i : items) { + if (i.getArticleId() == article.id) { + i.setAmount(quantity); + found = true; + break; + } + } + if (!found) { + items.add(new ShoppingCartItem(quantity, article)); + } + + items.removeIf(i -> i.getAmount() == 0); + } + + public static class ShoppingCartItem { private int amount; private final long articleId; @@ -60,5 +83,9 @@ public class ShoppingCart { public long getArticleId() { return articleId; } + + public void setAmount(Integer amount) { + this.amount = amount; + } } } diff --git a/prototype/src/main/resources/db/customers.sql b/prototype/src/main/resources/db/customers.sql deleted file mode 100644 index b1a108f..0000000 --- a/prototype/src/main/resources/db/customers.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE "customers" ( - "id" INTEGER PRIMARY KEY AUTOINCREMENT, - "lastname" TEXT, - "firstname" TEXT, - "username" TEXT, - "password" TEXT -); diff --git a/prototype/src/main/resources/templates/shop/checkout.html b/prototype/src/main/resources/templates/shop/checkout.html index e6bb15d..e017f9e 100644 --- a/prototype/src/main/resources/templates/shop/checkout.html +++ b/prototype/src/main/resources/templates/shop/checkout.html @@ -31,91 +31,31 @@ Menge - - - Kamera - 100,50 EUR - - - - - - - - - - Earbuds - 63,95 EUR - - - - - - - - - - USB-Magic Light - 11,90 EUR - - - - - - - - - - 3D Magic Stativ - 15,99 EUR - - - - - - - - - - Ersatzfernbedinung - 7,95 EUR - - - - - - - + + + + + EUR + +
+ + + + +
+ + +
@@ -151,23 +91,26 @@ Musterstraße 42 - - - - - - - - - - - - - + + + + + + + - + From 2ec3ad32eaea0f834c7f12b53e5b20c0b2b4cf85 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 22:56:12 +0200 Subject: [PATCH 039/203] Implement CreateCustomerOrderAction; Not implemented: correct booking and WarehouseBooking fetching... Puhh, that was a struggle to get working. Damn JPA Shit --- prototype/scripts/addusers.sql | 12 +- .../action/booking/CreateBookingAction.java | 30 +++ .../action/shop/CreateOrderAction.java | 177 ++++++++++++++++++ .../action/shop/EnableTrackingAction.java | 11 ++ .../action/somepackage/DemoAction.java | 7 - .../shop/ShopCheckoutController.java | 73 +++++++- .../ecommerce/entities/booking/Booking.java | 6 +- .../entities/booking/BookingAccountEntry.java | 40 +++- .../entities/booking/BookingReason.java | 15 +- .../entities/booking/PaymentMethod.java | 7 + .../hso/ecommerce/entities/shop/Address.java | 21 ++- .../entities/shop/CustomerOrder.java | 2 +- .../ecommerce/entities/shop/ShoppingCart.java | 16 +- .../org/hso/ecommerce/entities/user/User.java | 4 +- .../entities/warehouse/WarehouseBooking.java | 7 +- .../warehouse/WarehouseBookingPosition.java | 8 +- .../WarehouseBookingPositionSlotEntry.java | 19 +- .../warehouse/WarehouseBookingReason.java | 7 + .../BookingAccountEntryRepository.java | 26 +++ .../repos/booking/BookingRepository.java | 12 ++ .../repos/shop/CustomerOderRepository.java | 11 ++ ...useBookingPositionSlotEntryRepository.java | 18 ++ .../warehouse/WarehouseBookingRepository.java | 11 ++ .../resources/templates/shop/checkout.html | 39 ++-- 24 files changed, 526 insertions(+), 53 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java delete mode 100644 prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java diff --git a/prototype/scripts/addusers.sql b/prototype/scripts/addusers.sql index 86b6d43..db3d6ed 100644 --- a/prototype/scripts/addusers.sql +++ b/prototype/scripts/addusers.sql @@ -1,9 +1,9 @@ /* password is 123 */ -INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") -VALUES (datetime('now','localtime') ||'.0', "emp@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "0", "1", "1", "0"); +INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee") +VALUES (datetime('now','localtime') ||'.0', "emp@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "1"); -INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") -VALUES (datetime('now','localtime') ||'.0', "user@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "1", "0", "0"); +INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee") +VALUES (datetime('now','localtime') ||'.0', "user@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "0"); -INSERT INTO users ("created", "email", "password_hash", "gets_ads", "is_active", "is_employee", "isb2b") -VALUES (datetime('now','localtime') ||'.0', "blocked@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "1", "0", "0", "0"); \ No newline at end of file +INSERT INTO users ("created", "email", "password_hash", "is_active", "is_employee") +VALUES (datetime('now','localtime') ||'.0', "blocked@ecom", "$2a$10$zFiqcePBmXHErD86vkI.vO1dnX20ezoVSM8xjGi59nktXYQv0o.fK", "0", "0"); \ No newline at end of file diff --git a/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java b/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java new file mode 100644 index 0000000..f01e6a3 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java @@ -0,0 +1,30 @@ +package org.hso.ecommerce.action.booking; + +import org.hso.ecommerce.entities.booking.Booking; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.BookingReason; + +public class CreateBookingAction { + + private Booking booking; + + public CreateBookingAction(BookingAccountEntry source, BookingAccountEntry destination, BookingReason reason, int amountCent) { + booking = new Booking(); + booking.reason = reason; + booking.amountCent = amountCent; + + assert source != null || destination != null; + + if (source != null) { + booking.source = source.copyAddAmount(-amountCent); + } + if (destination != null) { + booking.destination = destination.copyAddAmount(amountCent); + } + + } + + public Booking finish() { + return booking; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java new file mode 100644 index 0000000..ed1066c --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java @@ -0,0 +1,177 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.action.booking.CreateBookingAction; +import org.hso.ecommerce.entities.booking.*; +import org.hso.ecommerce.entities.shop.Address; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.hso.ecommerce.entities.shop.CustomerOrderPosition; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class CreateOrderAction { + + private User user; + Address destination; + + private int expectedTotalGrossCent; + + private int totalNetCent; + private int totalVatCent; + private PaymentMethod method; + + private BookingAccountEntry latestUserBooking; + private BookingAccountEntry latestVatBooking; + private BookingAccountEntry latestMainBooking; + + private List orderItems = new ArrayList<>(); + + public CreateOrderAction( + User user, + int expectedTotalGrossCent, + Address destination, + PaymentMethod method, + BookingAccountEntry latestUserBooking, + BookingAccountEntry latestVatBooking, + BookingAccountEntry latestMainBooking + ) { + this.user = user; + this.expectedTotalGrossCent = expectedTotalGrossCent; + this.destination = destination; + this.method = method; + + this.latestUserBooking = latestUserBooking; + assert latestUserBooking.userAccount.id == user.id; + + this.latestVatBooking = latestVatBooking; + assert latestVatBooking.isVATAccount; + + this.latestMainBooking = latestMainBooking; + assert latestMainBooking.isMainAccount; + } + + public void addArticle(Article article, int quantity, List availableSlots) { + for (WarehouseBookingPositionSlotEntry slot : availableSlots) { + assert slot.article.id == article.id; + } + + orderItems.add(new OrderItem(article, availableSlots, quantity)); + + totalNetCent += article.shopPricePerUnitNetCent * quantity; + totalVatCent += article.getVat() * quantity; + } + + public Result finish() { + CustomerOrder order = createOrder(); + CustomerPayment payment = createPayment(); + + List bookingList = new ArrayList<>(); + bookingList.add(new CreateBookingAction(latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish()); + bookingList.add(new CreateBookingAction(null, latestUserBooking, new BookingReason(payment), order.totalGrossCent).finish()); + bookingList.add(new CreateBookingAction(latestMainBooking, latestVatBooking, new BookingReason(order), order.totalVatCent).finish()); + + WarehouseBooking warehouseBooking = createWarehouseBooking(order); + + return new Result( + order, + warehouseBooking, + bookingList + ); + } + + private WarehouseBooking createWarehouseBooking(CustomerOrder order) { + WarehouseBooking booking = new WarehouseBooking(); + booking.created = new Timestamp(new Date().getTime()); + booking.reason = new BookingReason(order); + + for (OrderItem item : orderItems) { + int needed = item.quantity; + + for (WarehouseBookingPositionSlotEntry slot : item.availableSlots) { + int remove = Math.min(slot.newSumSlot, needed); + needed -= remove; + + WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); + + bookingPosition.article = item.article; + bookingPosition.amount = -remove; + bookingPosition.slotEntry = slot.copyAddAmount(-remove); + bookingPosition.booking = booking; + + booking.positions.add(bookingPosition); + + if (needed == 0) { + break; + } + } + } + + return booking; + } + + private CustomerPayment createPayment() { + CustomerPayment payment = new CustomerPayment(); + payment.amountCent = totalNetCent + totalVatCent; + payment.payment = method; + return payment; + } + + + private CustomerOrder createOrder() { + assert totalNetCent + totalVatCent == expectedTotalGrossCent; + + CustomerOrder customerOrder = new CustomerOrder(); + customerOrder.customer = user; + customerOrder.destination = destination; + + for (OrderItem item : orderItems) { + CustomerOrderPosition position = new CustomerOrderPosition(); + position.article = item.article; + position.pricePerUnit = item.article.shopPricePerUnitNetCent; + position.quantity = item.quantity; + + position.order = customerOrder; + + customerOrder.positions.add(position); + } + + customerOrder.created = new Timestamp(new Date().getTime()); + + customerOrder.totalNetCent = totalNetCent; + customerOrder.totalVatCent = totalVatCent; + customerOrder.totalGrossCent = totalNetCent + totalVatCent; + + return customerOrder; + } + + public static class Result { + public final CustomerOrder customerOrder; + public final WarehouseBooking warehouseBooking; + public final List bookings; + + Result(CustomerOrder customerOrder, WarehouseBooking warehouseBooking, List bookings) { + this.customerOrder = customerOrder; + this.warehouseBooking = warehouseBooking; + this.bookings = bookings; + } + } + + private static class OrderItem { + List availableSlots; + int quantity; + Article article; + + public OrderItem(Article article, List availableSlots, int quantity) { + this.article = article; + this.availableSlots = availableSlots; + this.quantity = quantity; + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java new file mode 100644 index 0000000..d3e2dc7 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/EnableTrackingAction.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.entities.shop.CustomerOrder; + +public class EnableTrackingAction { + + public static void addTrackingInfo(CustomerOrder customerOrder) { + // TODO: + customerOrder.trackingId = "555-NASE"; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java b/prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java deleted file mode 100644 index 22f8946..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/action/somepackage/DemoAction.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.hso.ecommerce.action.somepackage; - -public class DemoAction { - // TODO: remove me. - // mksubpackage - -} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index 960a104..66ced97 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -1,14 +1,23 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.action.shop.CreateOrderAction; +import org.hso.ecommerce.action.shop.EnableTrackingAction; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.PaymentMethod; +import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; +import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.shop.CustomerOderRepository; +import org.hso.ecommerce.repos.user.UserRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestAttribute; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -19,9 +28,27 @@ import java.util.TreeMap; @RequestMapping("/shop/") public class ShopCheckoutController { + @Autowired + private final UserRepository userRepository = null; + @Autowired private final ArticleRepository articleRepository = null; + @Autowired + private final BookingAccountEntryRepository bookingEntryRepository = null; + + @Autowired + private final BookingRepository bookingRepository = null; + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @Autowired + private final CustomerOderRepository customerOderRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository wbeseRepo = null; + @GetMapping("/checkout") public String shopCheckout(HttpSession session, HttpServletRequest request, @@ -69,7 +96,43 @@ public class ShopCheckoutController { } @PostMapping("/checkoutFinish") - public String shopCheckoutFinish() { + public String shopCheckoutFinish( + @RequestAttribute(value = "user") User user, + @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, + @RequestParam("address") String address, + @RequestParam("cardnumber") String cardnumber, + @RequestParam("shopping_cart_revision") Integer cartRevision, + @RequestParam("expected_total") Integer expectedPrice + ) { + + // Must be refetched for persitence. + user = userRepository.findById(user.id).get(); + + CreateOrderAction action = new CreateOrderAction( + user, + expectedPrice, + Address.fromString(address), + PaymentMethod.fromCreditCarNumber(cardnumber), + bookingEntryRepository.getByUser(user).orElse(BookingAccountEntry.newUser(user)), + bookingEntryRepository.getByVat().orElse(BookingAccountEntry.newVat()), + bookingEntryRepository.getByMain().orElse(BookingAccountEntry.newMain()) + ); + + for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) { + Article article = articleRepository.findById(item.getArticleId()).get(); + action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article)); + } + + CreateOrderAction.Result result = action.finish(); + + EnableTrackingAction.addTrackingInfo(result.customerOrder); + + customerOderRepository.save(result.customerOrder); + bookingRepository.saveAll(result.bookings); + warehouseBookingRepository.save(result.warehouseBooking); + + shoppingCart.clear(); + return "shop/checkoutFinish"; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java index 997f578..e61ea07 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/Booking.java @@ -14,12 +14,12 @@ public class Booking { // always >= 0 public int amountCent; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public BookingAccountEntry source; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public BookingAccountEntry destination; - @OneToOne(optional = false) + @OneToOne(optional = false, cascade = CascadeType.ALL) public BookingReason reason; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java index 92e282f..04b3b8a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java @@ -16,13 +16,49 @@ public class BookingAccountEntry { public int newSumCent; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public User userAccount; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.ALL) public Supplier supplierAccount; public boolean isMainAccount; public boolean isVATAccount; + public BookingAccountEntry copyAddAmount(int amountCent) { + BookingAccountEntry e = new BookingAccountEntry(); + + e.userAccount = userAccount; + e.supplierAccount = supplierAccount; + e.isMainAccount = isMainAccount; + e.isVATAccount = isVATAccount; + + e.newSumCent = newSumCent + amountCent; + + return e; + } + + public static BookingAccountEntry newUser(User user) { + BookingAccountEntry e = new BookingAccountEntry(); + e.userAccount = user; + e.newSumCent = 0; + + return e; + } + + public static BookingAccountEntry newMain() { + BookingAccountEntry e = new BookingAccountEntry(); + e.isMainAccount = true; + e.newSumCent = 0; + + return e; + } + + public static BookingAccountEntry newVat() { + BookingAccountEntry e = new BookingAccountEntry(); + e.isVATAccount = true; + e.newSumCent = 0; + + return e; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java index b2bc6cc..f973d26 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java @@ -22,9 +22,22 @@ public class BookingReason { @ManyToOne(optional = true) public CustomerOrder customerOrder; - @ManyToOne(optional = true) + @OneToOne(optional = true, cascade = CascadeType.ALL) public CustomerPayment customerPayment; @ManyToOne(optional = true) public SupplierOrder supplierOrder; + + public BookingReason() { + } + + ; + + public BookingReason(CustomerOrder order) { + this.customerOrder = order; + } + + public BookingReason(CustomerPayment customerPayment) { + this.customerPayment = customerPayment; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java index 6cb5307..4f81062 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java @@ -7,4 +7,11 @@ import javax.validation.constraints.NotNull; public class PaymentMethod { @NotNull public String creditCardNumber; + + public static PaymentMethod fromCreditCarNumber(String cardnumber) { + PaymentMethod m = new PaymentMethod(); + m.creditCardNumber = cardnumber; + + return m; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java index 32d057b..2f76083 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Address.java @@ -4,7 +4,24 @@ import javax.persistence.Embeddable; @Embeddable public class Address { - public String name; - public String addressString; + public String name = ""; + public String addressString = ""; public String country = "DE"; + + @Override + public String toString() { + return name + "\n" + addressString; + } + + public static Address fromString(String addr) { + Address a = new Address(); + + String[] arr = addr.split("\n", 2); + a.name = arr[0]; + if (arr.length > 1) { + a.addressString = arr[1]; + } + + return a; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java index dd7d4f8..1b20bb2 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java @@ -24,7 +24,7 @@ public class CustomerOrder { @OneToMany( targetEntity = CustomerOrderPosition.class, - mappedBy = "order" + mappedBy = "order", cascade = CascadeType.ALL ) public List positions = new ArrayList<>(); diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java index aee94c2..d059f15 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java @@ -6,12 +6,18 @@ import java.util.List; // Not a db entity. Just for session storage public class ShoppingCart { + private final static int MAX_ITEMS = 10; + private int revision; private ArrayList items; public ShoppingCart() { - revision = (int) Math.round(Math.random() * 0xFFFF); + clear(); + } + + public void clear() { items = new ArrayList<>(); + revision = (int) Math.round(Math.random() * 0xFFFF); } public List getItems() { @@ -60,7 +66,7 @@ public class ShoppingCart { items.add(new ShoppingCartItem(quantity, article)); } - items.removeIf(i -> i.getAmount() == 0); + items.removeIf(i -> i.getAmount() <= 0); } public static class ShoppingCartItem { @@ -78,6 +84,9 @@ public class ShoppingCart { public void addAmount(int amount) { this.amount += amount; + if (this.amount > MAX_ITEMS) { + this.amount = MAX_ITEMS; + } } public long getArticleId() { @@ -86,6 +95,9 @@ public class ShoppingCart { public void setAmount(Integer amount) { this.amount = amount; + if (this.amount > MAX_ITEMS) { + this.amount = MAX_ITEMS; + } } } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index 9b07996..784d89a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -30,10 +30,10 @@ public class User { public boolean isEmployee; @Embedded - private Address defaultDeliveryAddress; + public Address defaultDeliveryAddress; @Embedded - private PaymentMethod defaultPayment; + public PaymentMethod defaultPayment; public long getId() { return id; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java index 97a2805..cf60734 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java @@ -1,5 +1,7 @@ package org.hso.ecommerce.entities.warehouse; +import org.hso.ecommerce.entities.booking.BookingReason; + import javax.persistence.*; import javax.validation.constraints.NotNull; import java.util.ArrayList; @@ -21,7 +23,10 @@ public class WarehouseBooking { public boolean isDone; @OneToMany( - mappedBy = "booking" + mappedBy = "booking", cascade = CascadeType.ALL ) public List positions = new ArrayList<>(); + + @OneToOne(optional = false, cascade = CascadeType.ALL) + public BookingReason reason; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java index 88ceee3..e3a147a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPosition.java @@ -23,10 +23,6 @@ public class WarehouseBookingPosition { public int amount; // positive or negative - @ManyToOne(optional = true) - public WarehouseBookingPositionSlotEntry source; - - @ManyToOne(optional = true) - public WarehouseBookingPositionSlotEntry destination; - + @ManyToOne(optional = true, cascade = CascadeType.ALL) + public WarehouseBookingPositionSlotEntry slotEntry; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java index 7529e4f..d9ca4c6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java @@ -16,8 +16,23 @@ public class WarehouseBookingPositionSlotEntry { @ManyToOne public Article article; - public int newSumArticles; - public int newSumWarehousePosition; + // Can;t do, does not work when created in action. + //public int newSumArticles; + public int newSumSlot; public int slot; + + public WarehouseBookingPositionSlotEntry copyAddAmount(int amount) { + WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry(); + + e.article = article; + e.slot = slot; + + e.newSumSlot = newSumSlot + amount; + + assert e.article.warehouseUnitsPerSlot >= e.newSumSlot; + assert e.newSumSlot >= 0; + + return e; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java index ec60c4d..08ee80a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java @@ -23,4 +23,11 @@ public class WarehouseBookingReason { public CustomerOrder customerOrder; public boolean isManuel; + + public WarehouseBookingReason() { + } + + public WarehouseBookingReason(CustomerOrder order) { + this.customerOrder = order; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java new file mode 100644 index 0000000..657e34d --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java @@ -0,0 +1,26 @@ +package org.hso.ecommerce.repos.booking; + +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.user.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface BookingAccountEntryRepository extends JpaRepository { + + @Query("SELECT e FROM BookingAccountEntry e WHERE e.userAccount = :user ORDER BY e.id DESC") + Optional getByUser(User user); + + @Query("SELECT e FROM BookingAccountEntry e WHERE e.isMainAccount = 1 ORDER BY e.id DESC") + Optional getByMain(); + + @Query("SELECT e FROM BookingAccountEntry e WHERE e.isVATAccount = 1 ORDER BY e.id DESC") + Optional getByVat(); + + +} + + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java new file mode 100644 index 0000000..454b3f3 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java @@ -0,0 +1,12 @@ +package org.hso.ecommerce.repos.booking; + +import org.hso.ecommerce.entities.booking.Booking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BookingRepository extends JpaRepository { + +} + + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java new file mode 100644 index 0000000..407f45c --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CustomerOderRepository extends JpaRepository { + +} + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java new file mode 100644 index 0000000..839a017 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -0,0 +1,18 @@ +package org.hso.ecommerce.repos.warehouse; + +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository { + + // TODO this is wrong. revisit. + @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e WHERE e.article = :article ORDER BY e.id DESC") + List getByArticle(Article article); +} + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java new file mode 100644 index 0000000..8d01092 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.repos.warehouse; + +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface WarehouseBookingRepository extends JpaRepository { + +} + diff --git a/prototype/src/main/resources/templates/shop/checkout.html b/prototype/src/main/resources/templates/shop/checkout.html index e017f9e..cf02b5c 100644 --- a/prototype/src/main/resources/templates/shop/checkout.html +++ b/prototype/src/main/resources/templates/shop/checkout.html @@ -21,7 +21,17 @@ -
+
+
+

Noch keine Artikel im Warenkorb.

+

+ +

+ Weiter shoppen +
+ +
+
Artikel (Netto)200,29 EUR
Bonuspunkte-5,00 EUR
Umsatzsteuer (19%)35,00 EUR
Umsatzsteuer (7%)2,50 EUR + EUR +
Umsatzsteuer (%) + EUR +
Gesamt:240,79 EUR + EUR +
@@ -37,7 +47,9 @@ th:src="@{/shop/articles/${item.article.id}/image.jpg}" class="s"/> - + + th:src="@{/shop/articles/{id}/image.jpg(id=${item.article.id})}" class="s"/> + - + From a2f20938cd012106aacd784a759f2479bb098f44 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 10 May 2020 15:26:50 +0200 Subject: [PATCH 049/203] show listedArticles --- prototype/build.gradle | 2 +- .../intern/InternArticleController.java | 100 +++++++++++------- .../hso/ecommerce/entities/shop/Article.java | 68 +++++++----- .../hso/ecommerce/entities/shop/Category.java | 2 - .../repos/shop/ArticleRepository.java | 2 - ...useBookingPositionSlotEntryRepository.java | 6 ++ .../intern/listedArticles/index.html | 71 +++---------- 7 files changed, 127 insertions(+), 124 deletions(-) diff --git a/prototype/build.gradle b/prototype/build.gradle index 2769783..ad8b14c 100644 --- a/prototype/build.gradle +++ b/prototype/build.gradle @@ -35,5 +35,5 @@ group 'org.hso' version '0.1.0' bootRun { - args = ["--spring.profiles.active=dev"] + args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties\""] } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index 29fe361..c79cc6d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -1,57 +1,81 @@ package org.hso.ecommerce.controller.intern; - - +import java.util.ArrayList; import java.util.List; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; - - - @Controller @RequestMapping("intern/listedArticles") -public class InternArticleController { - - @Autowired - private final ArticleRepository articleRepository = null; - - /* - @Autowired - public InternArticleController(ArticleRepository articleRepository) - { - this.articleRepository = articleRepository; - } - */ - - @GetMapping("/") - public String internListedArticles(Model model) { - +public class InternArticleController +{ + @Autowired + private final ArticleRepository articleRepository = null; - List
articles = articleRepository.findAll(); - - System.out.println(articles.size()); + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseEntryRepository = null; + @GetMapping("/") + public String internListedArticles(Model model) + { - - // model.addAttribute("ListedArticles", bookService.findAll()); + List totals = new ArrayList(); - - - return "intern/listedArticles/index"; - } - - - - @GetMapping("/{id}") - public String internListedArticlesId() { - return "intern/listedArticles/id"; - } + for (Article article : articleRepository.findAll()) { + ListedArticlesListTotals tmp = new ListedArticlesListTotals(); + tmp.addListedArticle(article, + warehouseEntryRepository.getArticleStock(article.id).orElse(0)); + totals.add(tmp); + } + + model.addAttribute("ListedArticles", totals); + return "intern/listedArticles/index"; + } + + @GetMapping("/{id}") + public String internListedArticlesId() + { + return "intern/listedArticles/id"; + } + + public static class ListedArticlesListTotals + { + + public String imgPath; + + public String title; + + public String price; + + public String price_netto; + + public String categorie; + + public int stock; + + public long offer_id; + + public long id; + + void addListedArticle(Article article, int stock) + { + this.imgPath = article.image.path; + this.title = article.title; + this.price_netto = String.format("%.2f", + ((float) article.shopPricePerUnitNetCent / 100)); + this.price = String.format("%.2f", + ((float) article.getPriceGross() / 100)); + this.categorie = article.getCategories(); + this.stock = stock; + this.offer_id = article.related.id; + this.id = article.id; + } + } - } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java index a26cb39..ed351ea 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java @@ -9,41 +9,55 @@ import java.util.Set; @Entity @Table(name = "articles") -public class Article { +public class Article +{ - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Basic - public long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Basic + public long id; - @ManyToOne(optional = false) - public ArticleOffer related; + @ManyToOne(optional = false) + public ArticleOffer related; - public int shopPricePerUnitNetCent; - public int warehouseUnitsPerSlot; + public int shopPricePerUnitNetCent; - public boolean shouldReorder; - public int reorderMaxPrice; + public int warehouseUnitsPerSlot; - @NotNull - public String title; + public boolean shouldReorder; - @NotNull - public String description; + public int reorderMaxPrice; - @OneToOne(optional = true) - @Basic(fetch = FetchType.LAZY) - public Image image; + @NotNull + public String title; - @ManyToMany - @JoinTable(name = "article_categories_bindings") - public Set categories = new HashSet<>(); + @NotNull + public String description; - public int getVat() { - return (shopPricePerUnitNetCent * related.vatPercent) / 100; - } + @OneToOne(optional = true) + @Basic(fetch = FetchType.LAZY) + public Image image; - public int getPriceGross() { - return shopPricePerUnitNetCent + getVat(); - } + @ManyToMany + @JoinTable(name = "article_categories_bindings") + public Set categories = new HashSet<>(); + + public String getCategories() + { + StringBuilder result = new StringBuilder(); + for (Category temp : categories) { + result.append(temp.name); + } + return result.toString(); + } + + public int getVat() + { + return (shopPricePerUnitNetCent * related.vatPercent) / 100; + } + + public int getPriceGross() + { + return shopPricePerUnitNetCent + getVat(); + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Category.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Category.java index 13b0b54..242826d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Category.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Category.java @@ -1,7 +1,5 @@ package org.hso.ecommerce.entities.shop; -import org.hso.ecommerce.entities.shop.Article; - import javax.persistence.*; import javax.validation.constraints.NotNull; import java.util.HashSet; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 27223db..93803cc 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -1,14 +1,12 @@ package org.hso.ecommerce.repos.shop; import org.hso.ecommerce.entities.shop.Article; -import org.hso.ecommerce.entities.user.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; -import java.util.Optional; @Repository public interface ArticleRepository extends JpaRepository diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java index 962231a..e1327ce 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository { @@ -14,5 +15,10 @@ public interface WarehouseBookingPositionSlotEntryRepository extends JpaReposito // @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e, Slot s WHERE e.slot = s AND e.article = :article GROUP BY e.slot.slotNum HAVING max(e.id)") @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) List getByArticle(long article); + + + @Query(value = "SELECT SUM(w.new_sum_articles) FROM warehouse_booking_position_entries as w WHERE w.article_id = :articleid", nativeQuery = true) + Optional getArticleStock(long articleid); + } diff --git a/prototype/src/main/resources/templates/intern/listedArticles/index.html b/prototype/src/main/resources/templates/intern/listedArticles/index.html index adce5ef..321835b 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/index.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/index.html @@ -38,67 +38,30 @@ data-target-id="main-table">
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
EUR + EUR x @@ -45,11 +57,9 @@ @@ -64,11 +74,16 @@

Checkout

+ + +

Lieferadresse:

@@ -78,12 +93,10 @@ Musterstraße 42
-
- - -

Bestellübersicht

From 5f071939d371c0be602a197b8e6b627c5ccd56a4 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Tue, 5 May 2020 23:34:17 +0200 Subject: [PATCH 040/203] Renice error handling --- .../controller/shop/ShopArticleController.java | 10 ++++++++-- .../controller/shop/ShopCheckoutController.java | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 5670f49..3cb860f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -8,6 +8,8 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.List; @@ -59,7 +61,9 @@ public class ShopArticleController { } @PostMapping("/{id}") - public String shopArticlesByIdBuy(HttpSession session, + public String shopArticlesByIdBuy(HttpServletRequest request, + HttpServletResponse response, + HttpSession session, @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, @PathVariable("id") Long id, @RequestParam("quantity") Integer quantity, @@ -69,7 +73,9 @@ public class ShopArticleController { Optional
article = articleRepository.findById(id); if (!article.isPresent()) { - throw new RuntimeException("Article not found!"); + request.setAttribute("error", "Der Artikel wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; } if (setAmount != null && setAmount) { diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index 66ced97..ffdd9f3 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -20,6 +20,7 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.TreeMap; @@ -97,6 +98,8 @@ public class ShopCheckoutController { @PostMapping("/checkoutFinish") public String shopCheckoutFinish( + HttpServletRequest request, + HttpServletResponse response, @RequestAttribute(value = "user") User user, @RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, @RequestParam("address") String address, @@ -105,6 +108,12 @@ public class ShopCheckoutController { @RequestParam("expected_total") Integer expectedPrice ) { + if (shoppingCart.getRevision() != cartRevision) { + request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher die Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut."); + response.setStatus(HttpServletResponse.SC_CONFLICT); + return "shop/checkout"; + } + // Must be refetched for persitence. user = userRepository.findById(user.id).get(); From 572a99946721101a1a9efb23cf12bb54d5be8943 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Wed, 6 May 2020 14:10:37 +0200 Subject: [PATCH 041/203] Implement proper querying of warehouse slot sums and booking accounts. --- .../ecommerce/components/SlotInitializer.java | 32 +++++++++++++++++++ .../shop/ShopCheckoutController.java | 4 +-- .../ecommerce/entities/warehouse/Slot.java | 17 ++++++++++ .../WarehouseBookingPositionSlotEntry.java | 6 +++- .../BookingAccountEntryRepository.java | 9 +++--- .../repos/warehouse/SlotRepository.java | 17 ++++++++++ ...useBookingPositionSlotEntryRepository.java | 6 ++-- 7 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java diff --git a/prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java b/prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java new file mode 100644 index 0000000..ce8c39b --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/components/SlotInitializer.java @@ -0,0 +1,32 @@ +package org.hso.ecommerce.components; + +import org.hso.ecommerce.entities.warehouse.Slot; +import org.hso.ecommerce.repos.warehouse.SlotRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +@Component +public class SlotInitializer { + + @Autowired + private final SlotRepository slotRepository = null; + + // TODO: use values form cfg. + private final int NUM_SLOTS = 50; + + @PostConstruct + public void init() { + for (int i = 1; i <= NUM_SLOTS; i++) { + if (!slotRepository.findBySlotNum(i).isPresent()) { + Slot slotAdded = new Slot(); + slotAdded.slotNum = i; + slotRepository.save(slotAdded); + + System.out.println("Added Slot " + i + " to DB"); + } + } + } + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index ffdd9f3..ab4827f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -122,14 +122,14 @@ public class ShopCheckoutController { expectedPrice, Address.fromString(address), PaymentMethod.fromCreditCarNumber(cardnumber), - bookingEntryRepository.getByUser(user).orElse(BookingAccountEntry.newUser(user)), + bookingEntryRepository.getByUser(user.id).orElse(BookingAccountEntry.newUser(user)), bookingEntryRepository.getByVat().orElse(BookingAccountEntry.newVat()), bookingEntryRepository.getByMain().orElse(BookingAccountEntry.newMain()) ); for (ShoppingCart.ShoppingCartItem item : shoppingCart.getItems()) { Article article = articleRepository.findById(item.getArticleId()).get(); - action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article)); + action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article.id)); } CreateOrderAction.Result result = action.finish(); diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java new file mode 100644 index 0000000..6a56541 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/Slot.java @@ -0,0 +1,17 @@ +package org.hso.ecommerce.entities.warehouse; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +@Entity +@Table(name = "warehouse_slots") +public class Slot { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Basic + public long id; + + @NotNull + @Column(unique = true) + public int slotNum; +} diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java index d9ca4c6..766398f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java @@ -3,6 +3,7 @@ package org.hso.ecommerce.entities.warehouse; import org.hso.ecommerce.entities.shop.Article; import javax.persistence.*; +import javax.validation.constraints.NotNull; @Entity @Table(name = "warehouse_booking_position_entries") @@ -13,6 +14,7 @@ public class WarehouseBookingPositionSlotEntry { @Basic public long id; + @NotNull @ManyToOne public Article article; @@ -20,7 +22,9 @@ public class WarehouseBookingPositionSlotEntry { //public int newSumArticles; public int newSumSlot; - public int slot; + @NotNull + @ManyToOne + public Slot slot; public WarehouseBookingPositionSlotEntry copyAddAmount(int amount) { WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry(); diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java index 657e34d..d842a3a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java @@ -1,7 +1,6 @@ package org.hso.ecommerce.repos.booking; import org.hso.ecommerce.entities.booking.BookingAccountEntry; -import org.hso.ecommerce.entities.user.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -11,13 +10,13 @@ import java.util.Optional; @Repository public interface BookingAccountEntryRepository extends JpaRepository { - @Query("SELECT e FROM BookingAccountEntry e WHERE e.userAccount = :user ORDER BY e.id DESC") - Optional getByUser(User user); + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.user_account_id = :user ORDER BY e.id DESC LIMIT 1", nativeQuery = true) + Optional getByUser(Long user); - @Query("SELECT e FROM BookingAccountEntry e WHERE e.isMainAccount = 1 ORDER BY e.id DESC") + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.is_main_account = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true) Optional getByMain(); - @Query("SELECT e FROM BookingAccountEntry e WHERE e.isVATAccount = 1 ORDER BY e.id DESC") + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.isvataccount = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true) Optional getByVat(); diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java new file mode 100644 index 0000000..d0f00c0 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/SlotRepository.java @@ -0,0 +1,17 @@ +package org.hso.ecommerce.repos.warehouse; + +import org.hso.ecommerce.entities.warehouse.Slot; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface SlotRepository extends JpaRepository { + + @Query("SELECT s FROM Slot s WHERE s.slotNum = :slotNum") + Optional findBySlotNum(int slotNum); + + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java index 839a017..962231a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -1,6 +1,5 @@ package org.hso.ecommerce.repos.warehouse; -import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -12,7 +11,8 @@ import java.util.List; public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository { // TODO this is wrong. revisit. - @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e WHERE e.article = :article ORDER BY e.id DESC") - List getByArticle(Article article); + // @Query("SELECT e FROM WarehouseBookingPositionSlotEntry e, Slot s WHERE e.slot = s AND e.article = :article GROUP BY e.slot.slotNum HAVING max(e.id)") + @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) + List getByArticle(long article); } From 4048d6c5ad5d4a7a7d3ba00b2feddd9513692b4f Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Wed, 6 May 2020 17:00:28 +0200 Subject: [PATCH 042/203] Unify Style of error messages to match rest of ui. --- .../static/img/error_404_illustatus.svg | 377 +++++------------- .../static/img/error_generic_illustatus.svg | 352 ++++------------ .../main/resources/templates/error/404.html | 4 +- .../main/resources/templates/error/500.html | 4 +- 4 files changed, 177 insertions(+), 560 deletions(-) diff --git a/prototype/src/main/resources/static/img/error_404_illustatus.svg b/prototype/src/main/resources/static/img/error_404_illustatus.svg index 452b2d5..d58cd65 100644 --- a/prototype/src/main/resources/static/img/error_404_illustatus.svg +++ b/prototype/src/main/resources/static/img/error_404_illustatus.svg @@ -1,277 +1,100 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Oops, Page not found - - - - - \ No newline at end of file + + page not found + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/prototype/src/main/resources/static/img/error_generic_illustatus.svg b/prototype/src/main/resources/static/img/error_generic_illustatus.svg index f94a8d4..20143d7 100644 --- a/prototype/src/main/resources/static/img/error_generic_illustatus.svg +++ b/prototype/src/main/resources/static/img/error_generic_illustatus.svg @@ -1,277 +1,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Oops, something went wrong - - - - - \ No newline at end of file + + server down + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/prototype/src/main/resources/templates/error/404.html b/prototype/src/main/resources/templates/error/404.html index 699b4f5..a58016a 100644 --- a/prototype/src/main/resources/templates/error/404.html +++ b/prototype/src/main/resources/templates/error/404.html @@ -9,10 +9,8 @@
-

Error 404

+

Ein Fehler ist aufgetreten. Die gewünschte Ressource konnte nicht gefunden werden

-

Ein Fehler ist aufgetreten. Die gewünschte Ressource konnte nicht gefunden werden.

-
diff --git a/prototype/src/main/resources/templates/error/500.html b/prototype/src/main/resources/templates/error/500.html index f14f141..4f9747f 100644 --- a/prototype/src/main/resources/templates/error/500.html +++ b/prototype/src/main/resources/templates/error/500.html @@ -9,10 +9,8 @@
-

Error 500

+

Ein Fehler ist aufgetreten. Bitte versuche es später nocheinmal.

-

Ein Fehler ist aufgetreten. Bitte versuche es später nocheinmal.

-
From 1e7266e554266c483e54ad92cb2361900ee193c8 Mon Sep 17 00:00:00 2001 From: Hannes Date: Thu, 7 May 2020 15:48:00 +0200 Subject: [PATCH 043/203] implement imageserving and first part of ArticleRepository --- .gitignore | 2 + prototype/.gitignore | 2 +- .../shop/ShopArticleController.java | 84 +++++++++++-------- .../controller/shop/ShopIndexController.java | 64 ++++---------- .../hso/ecommerce/entities/shop/Article.java | 1 + .../hso/ecommerce/entities/shop/Image.java | 5 +- .../repos/shop/ArticleRepository.java | 20 +++++ .../resources/templates/shop/articles/id.html | 4 +- .../main/resources/templates/shop/index.html | 8 +- 9 files changed, 98 insertions(+), 92 deletions(-) diff --git a/.gitignore b/.gitignore index 3b718f8..3912d2e 100644 --- a/.gitignore +++ b/.gitignore @@ -89,3 +89,5 @@ local.properties # SQLite prototype/*.db + +prototype/images diff --git a/prototype/.gitignore b/prototype/.gitignore index 59529a7..4fac9a9 100644 --- a/prototype/.gitignore +++ b/prototype/.gitignore @@ -1,4 +1,4 @@ -./test.db +./e-commerce.db ./build ./gradle ./out diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 3cb860f..a6159d9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -1,9 +1,11 @@ package org.hso.ecommerce.controller.shop; +import org.apache.tomcat.util.http.fileupload.IOUtils; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @@ -11,7 +13,10 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.util.ArrayList; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.Optional; @@ -23,40 +28,35 @@ public class ShopArticleController { private final ArticleRepository articleRepository = null; @GetMapping("/{id}") - public String shopArticlesById(Model model, @PathVariable("id") Integer Id) { + public String shopArticlesById(Model model, + @PathVariable("id") Long id, + HttpServletRequest request, + HttpServletResponse response + ) { - //TODO: Get Article by Id instead of this dummy shit - Article d1 = new Article(); - d1.description = "this is dummy1"; - d1.title = "dummy1"; - d1.shopPricePerUnitNetCent = 1500; - d1.id = 1234; - model.addAttribute("article", d1); + Article article = articleRepository.findArticleById(id); + + + if(article == null) { + request.setAttribute("error", "Der Artikel wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; + } + + model.addAttribute("article", article); //TODO: Check if in Stock - if(false){ + if (false) { model.addAttribute("inStock", true); - }else{ + } else { model.addAttribute("inStock", false); } - //TODO: Get 2 Commercialised Articles - List
commercialArticles = new ArrayList
(); - Article d2 = new Article(); - d2.description = "this is dummy2"; - d2.title = "dummy2"; - d2.shopPricePerUnitNetCent = 2000; - d2.id = 2345; - Article d3 = new Article(); - d3.description = "this is dummy3"; - d3.title = "dummy3"; - d3.shopPricePerUnitNetCent = 2500; - d3.id = 3456; - commercialArticles.add(d2); - commercialArticles.add(d3); + List
commercialArticles = articleRepository.getCommercialisedArticles("2"); model.addAttribute("commercialArticles", commercialArticles); + return "shop/articles/id"; } @@ -71,17 +71,19 @@ public class ShopArticleController { @RequestParam("fastcheckout") Boolean fastcheckout ) { - Optional
article = articleRepository.findById(id); - if (!article.isPresent()) { - request.setAttribute("error", "Der Artikel wurde nicht gefunden."); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - return "error/404"; - } + + Article article = articleRepository.findArticleById(id); + +// if (!article.isPresent()) { +// request.setAttribute("error", "Der Artikel wurde nicht gefunden."); +// response.setStatus(HttpServletResponse.SC_NOT_FOUND); +// return "error/404"; +// } if (setAmount != null && setAmount) { - shoppingCart.setArticleCount(article.get(), quantity); + shoppingCart.setArticleCount(article, quantity); } else { - shoppingCart.addArticle(article.get(), quantity); + shoppingCart.addArticle(article, quantity); } if (!fastcheckout) { @@ -90,4 +92,20 @@ public class ShopArticleController { return "redirect:/shop/checkout"; } } + + @GetMapping("/{id}/image.jpg") + public void getImageAsByteArray(HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id + ) throws IOException { + + Article article = articleRepository.findArticleById(id); + + InputStream in = new FileInputStream(article.image.path); + response.setContentType(MediaType.IMAGE_JPEG_VALUE); + IOUtils.copy(in, response.getOutputStream()); + } + + } + diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 0be1a50..69dce5d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -1,6 +1,8 @@ package org.hso.ecommerce.controller.shop; import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -8,13 +10,15 @@ import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; +import java.util.List; @Controller @RequestMapping("/") public class ShopIndexController { + @Autowired + private final ArticleRepository articleRepository = null; + @GetMapping("/") public String home() { return "redirect:/shop/"; @@ -23,8 +27,7 @@ public class ShopIndexController { @GetMapping("/shop/") public String shop(Model model, HttpSession session) { - //TODO: get commercialised Articles - ArrayList
commercialArticles = getArticles(); + List
commercialArticles = articleRepository.getCommercialisedArticles("4"); model.addAttribute("commercialArticles", commercialArticles); //check if logged in @@ -33,20 +36,19 @@ public class ShopIndexController { if (session != null && session.getAttribute("id") != null) { long userId = (long) session.getAttribute("id"); isLoggedIn = true; - if (false) { - hasOrders = true; //TODO: Find out whether user has orders! + } + + if (isLoggedIn) { + List
suggestedArticles = articleRepository.getLastOrderedArticles("4"); + if(suggestedArticles.size() > 0) { + model.addAttribute("suggestedArticles", suggestedArticles); + hasOrders = true; } } + model.addAttribute("isLoggedIn", isLoggedIn); model.addAttribute("hasOrders", hasOrders); - - if (hasOrders) { - //TODO: get up to last 4 Orders - ArrayList
suggestedArticles = getArticles(); - model.addAttribute("suggestedArticles", suggestedArticles); - } - return "shop/index"; } @@ -65,40 +67,4 @@ public class ShopIndexController { return "privacy"; } - - public ArrayList
getArticles() { - ArrayList
dummyArticles = new ArrayList
(); - - Article d1 = new Article(); - d1.description = "this is dummy1"; - d1.title = "dummy1"; - d1.shopPricePerUnitNetCent = 1500; - d1.id = 1234; - dummyArticles.add(d1); - - Article d2 = new Article(); - d2.description = "this is dummy2"; - d2.title = "dummy2"; - d2.shopPricePerUnitNetCent = 2000; - d2.id = 2345; - dummyArticles.add(d2); - - Article d3 = new Article(); - d3.description = "this is dummy3"; - d3.title = "dummy3"; - d3.shopPricePerUnitNetCent = 2500; - d3.id = 3456; - dummyArticles.add(d3); - - Article d4 = new Article(); - d4.description = "this is dummy4"; - d4.title = "dummy4"; - d4.shopPricePerUnitNetCent = 3000; - d4.id = 4567; - dummyArticles.add(d4); - - return dummyArticles; - } - - } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java index 2e066cd..a26cb39 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java @@ -32,6 +32,7 @@ public class Article { public String description; @OneToOne(optional = true) + @Basic(fetch = FetchType.LAZY) public Image image; @ManyToMany diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java index 33eebdb..d27d415 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java @@ -11,7 +11,6 @@ public class Image { @Basic public long id; - @Lob - @Column(name = "data", columnDefinition = "BLOB", nullable = false) - private byte[] data; + public String path; } + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 0ac4bd2..42ef5ee 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -1,11 +1,31 @@ package org.hso.ecommerce.repos.shop; import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.user.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + @Repository public interface ArticleRepository extends JpaRepository { + @Query(nativeQuery = true, value = "Select * FROM articles where articles.id = :articleId") + Article findArticleById(@Param("articleId") long articleId); + + + //TODO: getCommercialisedArticles(int quantity) + @Query("SELECT c FROM User c WHERE c.email = :quantity") + List
getCommercialisedArticles(@Param("quantity") String quantity); + + + //TODO: getLastOrderedArticles(int quantity) + @Query("SELECT c FROM User c WHERE c.email = :quantity") + List
getLastOrderedArticles(@Param("quantity") String quantity); + + } diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index 46b7c93..c82aedb 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -27,7 +27,7 @@

- +
@@ -56,7 +56,7 @@

Weitere Schnäppchen

-
+

Jetzt Shoppen und Empfehlungen erhalten!

-
+
- +

From 262640a2b4e61d8df82f35c3dc026845506c40a9 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 8 May 2020 09:58:29 +0200 Subject: [PATCH 044/203] change Databasename and add Advertisementflag --- prototype/.gitignore | 2 +- .../ecommerce/controller/shop/ShopArticleController.java | 4 +--- .../ecommerce/controller/shop/ShopIndexController.java | 3 +-- .../org/hso/ecommerce/entities/supplier/ArticleOffer.java | 2 ++ .../org/hso/ecommerce/repos/shop/ArticleRepository.java | 8 +++----- prototype/src/main/resources/application.properties | 2 +- prototype/src/main/resources/templates/shop/checkout.html | 3 ++- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/prototype/.gitignore b/prototype/.gitignore index 4fac9a9..52b353d 100644 --- a/prototype/.gitignore +++ b/prototype/.gitignore @@ -1,4 +1,4 @@ -./e-commerce.db +e-commerce.db ./build ./gradle ./out diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index a6159d9..6d8cea9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -13,12 +13,10 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; -import java.util.Optional; @Controller @RequestMapping("/shop/articles") @@ -53,7 +51,7 @@ public class ShopArticleController { model.addAttribute("inStock", false); } - List

commercialArticles = articleRepository.getCommercialisedArticles("2"); + List
commercialArticles = articleRepository.getAdvertisedArticles(); model.addAttribute("commercialArticles", commercialArticles); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 69dce5d..73c3241 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; -import javax.swing.*; import java.util.List; @Controller @@ -27,7 +26,7 @@ public class ShopIndexController { @GetMapping("/shop/") public String shop(Model model, HttpSession session) { - List
commercialArticles = articleRepository.getCommercialisedArticles("4"); + List
commercialArticles = articleRepository.getAdvertisedArticles(); model.addAttribute("commercialArticles", commercialArticles); //check if logged in diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java index 005b0b9..ce02d13 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java @@ -19,4 +19,6 @@ public class ArticleOffer { public String articleNumber; public int vatPercent; + + public boolean should_be_advertised; } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 42ef5ee..a22f191 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -13,16 +13,14 @@ import java.util.Optional; @Repository public interface ArticleRepository extends JpaRepository { - @Query(nativeQuery = true, value = "Select * FROM articles where articles.id = :articleId") + @Query("Select a FROM Article a where a.id = :articleId") Article findArticleById(@Param("articleId") long articleId); - //TODO: getCommercialisedArticles(int quantity) - @Query("SELECT c FROM User c WHERE c.email = :quantity") - List
getCommercialisedArticles(@Param("quantity") String quantity); + @Query("SELECT a from Article a join a.related ao where ao.should_be_advertised = true") + List
getAdvertisedArticles(); - //TODO: getLastOrderedArticles(int quantity) @Query("SELECT c FROM User c WHERE c.email = :quantity") List
getLastOrderedArticles(@Param("quantity") String quantity); diff --git a/prototype/src/main/resources/application.properties b/prototype/src/main/resources/application.properties index b62e80f..18ec88c 100644 --- a/prototype/src/main/resources/application.properties +++ b/prototype/src/main/resources/application.properties @@ -3,7 +3,7 @@ spring.resources.cache.cachecontrol.maxAge=P0D # LOGGING logging.level.org.springframework.web=WARN # DATABASE -spring.datasource.url=jdbc:sqlite:./test.db +spring.datasource.url=jdbc:sqlite:./e-commerce.db spring.datasource.driverClassName=org.sqlite.JDBC spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLiteDialect spring.jpa.hibernate.ddl-auto=update diff --git a/prototype/src/main/resources/templates/shop/checkout.html b/prototype/src/main/resources/templates/shop/checkout.html index cf02b5c..9962649 100644 --- a/prototype/src/main/resources/templates/shop/checkout.html +++ b/prototype/src/main/resources/templates/shop/checkout.html @@ -44,7 +44,8 @@

Date: Fri, 8 May 2020 10:37:42 +0200 Subject: [PATCH 045/203] Advertised Articles now shown correctly --- .../action/shop/GetRandomArticlesAction.java | 20 +++++++++++++++++++ .../shop/ShopArticleController.java | 6 ++++-- .../controller/shop/ShopIndexController.java | 6 ++++-- .../resources/templates/shop/articles/id.html | 4 ++-- .../main/resources/templates/shop/index.html | 2 +- 5 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java new file mode 100644 index 0000000..2c6fd55 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.entities.shop.Article; + +import java.util.ArrayList; +import java.util.List; + +public class GetRandomArticlesAction { + + public static List
getRandomArticles(int quantity, List
advertisedArticles) { + List
randomisedArticles = new ArrayList
(); + for (int i = 0; i < quantity; i++) { + int index = (int) (Math.random() * advertisedArticles.size()); + randomisedArticles.add(advertisedArticles.remove(index)); + if(advertisedArticles.size() == 0) + break; + } + return randomisedArticles; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 6d8cea9..eb13de9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.controller.shop; import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; import org.hso.ecommerce.repos.shop.ArticleRepository; @@ -16,6 +17,7 @@ import javax.servlet.http.HttpSession; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; @Controller @@ -36,7 +38,7 @@ public class ShopArticleController { Article article = articleRepository.findArticleById(id); - if(article == null) { + if (article == null) { request.setAttribute("error", "Der Artikel wurde nicht gefunden."); response.setStatus(HttpServletResponse.SC_NOT_FOUND); return "error/404"; @@ -51,7 +53,7 @@ public class ShopArticleController { model.addAttribute("inStock", false); } - List
commercialArticles = articleRepository.getAdvertisedArticles(); + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(4, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 73c3241..8844c8b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -1,5 +1,6 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -9,6 +10,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; +import java.util.ArrayList; import java.util.List; @Controller @@ -26,7 +28,7 @@ public class ShopIndexController { @GetMapping("/shop/") public String shop(Model model, HttpSession session) { - List
commercialArticles = articleRepository.getAdvertisedArticles(); + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); //check if logged in @@ -39,7 +41,7 @@ public class ShopIndexController { if (isLoggedIn) { List
suggestedArticles = articleRepository.getLastOrderedArticles("4"); - if(suggestedArticles.size() > 0) { + if (suggestedArticles.size() > 0) { model.addAttribute("suggestedArticles", suggestedArticles); hasOrders = true; } diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index c82aedb..73de367 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -27,7 +27,7 @@

- +
@@ -56,7 +56,7 @@

Weitere Schnäppchen

- +

diff --git a/prototype/src/main/resources/templates/shop/index.html b/prototype/src/main/resources/templates/shop/index.html index bb6e4cd..f38b012 100644 --- a/prototype/src/main/resources/templates/shop/index.html +++ b/prototype/src/main/resources/templates/shop/index.html @@ -21,7 +21,7 @@
- +

From bb4aa79243cd3604df8e069838f9afde11951b22 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 8 May 2020 11:44:30 +0200 Subject: [PATCH 046/203] implement personalized suggestions --- .../controller/shop/ShopArticleController.java | 10 +++++----- .../ecommerce/controller/shop/ShopIndexController.java | 6 ++++-- .../hso/ecommerce/repos/shop/ArticleRepository.java | 8 ++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index eb13de9..3b906d0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -74,11 +74,11 @@ public class ShopArticleController { Article article = articleRepository.findArticleById(id); -// if (!article.isPresent()) { -// request.setAttribute("error", "Der Artikel wurde nicht gefunden."); -// response.setStatus(HttpServletResponse.SC_NOT_FOUND); -// return "error/404"; -// } + if (article == null) { + request.setAttribute("error", "Der Artikel wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; + } if (setAmount != null && setAmount) { shoppingCart.setArticleCount(article, quantity); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 8844c8b..9fa8c37 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -2,6 +2,7 @@ package org.hso.ecommerce.controller.shop; import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -31,7 +32,7 @@ public class ShopIndexController { List

commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); - //check if logged in + boolean isLoggedIn = false; boolean hasOrders = false; if (session != null && session.getAttribute("id") != null) { @@ -40,7 +41,8 @@ public class ShopIndexController { } if (isLoggedIn) { - List
suggestedArticles = articleRepository.getLastOrderedArticles("4"); + List
suggestedArticles = articleRepository.getLastOrderedArticles(); + suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,3) : suggestedArticles; //only latest 4 articles if (suggestedArticles.size() > 0) { model.addAttribute("suggestedArticles", suggestedArticles); hasOrders = true; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index a22f191..933959f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -13,16 +13,16 @@ import java.util.Optional; @Repository public interface ArticleRepository extends JpaRepository { - @Query("Select a FROM Article a where a.id = :articleId") + @Query("SELECT a FROM Article a WHERE a.id = :articleId") Article findArticleById(@Param("articleId") long articleId); - @Query("SELECT a from Article a join a.related ao where ao.should_be_advertised = true") + @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.should_be_advertised = true") List
getAdvertisedArticles(); - @Query("SELECT c FROM User c WHERE c.email = :quantity") - List
getLastOrderedArticles(@Param("quantity") String quantity); + @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") + List
getLastOrderedArticles(); } From 143a53acf5bcd8a9c5484ed4c59b8a7f6ec118a2 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 8 May 2020 13:01:29 +0200 Subject: [PATCH 047/203] fix better quantities for UI --- .../action/shop/GetRandomArticlesAction.java | 5 ++--- .../controller/shop/ShopArticleController.java | 12 +++++++++--- .../controller/shop/ShopIndexController.java | 5 ++--- .../hso/ecommerce/repos/shop/ArticleRepository.java | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java index 2c6fd55..c299b92 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java @@ -9,11 +9,10 @@ public class GetRandomArticlesAction { public static List
getRandomArticles(int quantity, List
advertisedArticles) { List
randomisedArticles = new ArrayList
(); - for (int i = 0; i < quantity; i++) { + int loopcount = quantity > advertisedArticles.size() ? advertisedArticles.size() : quantity; + for (int i = 0; i < loopcount; i++) { int index = (int) (Math.random() * advertisedArticles.size()); randomisedArticles.add(advertisedArticles.remove(index)); - if(advertisedArticles.size() == 0) - break; } return randomisedArticles; } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 3b906d0..03da5b7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -4,7 +4,9 @@ import org.apache.tomcat.util.http.fileupload.IOUtils; import org.hso.ecommerce.action.shop.GetRandomArticlesAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.ShoppingCart; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; @@ -27,6 +29,9 @@ public class ShopArticleController { @Autowired private final ArticleRepository articleRepository = null; + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + @GetMapping("/{id}") public String shopArticlesById(Model model, @PathVariable("id") Long id, @@ -46,14 +51,15 @@ public class ShopArticleController { model.addAttribute("article", article); - //TODO: Check if in Stock - if (false) { + + //if (warehouseBookingPositionSlotEntryRepository.getByArticle(id).get(0).newSumSlot > 0) { //TODO: use this as soon as warehouse works + if (true) { model.addAttribute("inStock", true); } else { model.addAttribute("inStock", false); } - List
commercialArticles = GetRandomArticlesAction.getRandomArticles(4, articleRepository.getAdvertisedArticles()); + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(3, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java index 9fa8c37..e660eb0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopIndexController.java @@ -32,7 +32,6 @@ public class ShopIndexController { List
commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); model.addAttribute("commercialArticles", commercialArticles); - boolean isLoggedIn = false; boolean hasOrders = false; if (session != null && session.getAttribute("id") != null) { @@ -41,8 +40,8 @@ public class ShopIndexController { } if (isLoggedIn) { - List
suggestedArticles = articleRepository.getLastOrderedArticles(); - suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,3) : suggestedArticles; //only latest 4 articles + List
suggestedArticles = articleRepository.getOrderedArticles(); + suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,4) : suggestedArticles; //only latest 4 articles if (suggestedArticles.size() > 0) { model.addAttribute("suggestedArticles", suggestedArticles); hasOrders = true; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 933959f..2a88b7e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -22,7 +22,7 @@ public interface ArticleRepository extends JpaRepository { @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") - List
getLastOrderedArticles(); + List
getOrderedArticles(); } From 2b3ecb23429d674ec61be9aaf7002492cdbc06f9 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 9 May 2020 23:01:25 +0200 Subject: [PATCH 048/203] changed listedArticles Request to own Controller --- .../hso/ecommerce/app/RequestController.java | 2 + .../intern/InternArticleController.java | 51 ++++++++++++++++++- .../repos/shop/ArticleRepository.java | 20 ++++---- .../intern/listedArticles/index.html | 2 +- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 6ef7624..3a1259e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -157,6 +157,7 @@ public class RequestController { public String intern() { return "intern/index"; } + /* @GetMapping("/intern/listedArticles/") public String internListedArticles() { @@ -168,6 +169,7 @@ public class RequestController { return "intern/listedArticles/id"; } +*/ @GetMapping("/intern/articles/") public String internArticles() { diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index d078f54..29fe361 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -1,8 +1,57 @@ package org.hso.ecommerce.controller.intern; + + +import java.util.List; + +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + + + @Controller -//@RequestMapping("...") +@RequestMapping("intern/listedArticles") public class InternArticleController { + + @Autowired + private final ArticleRepository articleRepository = null; + + /* + @Autowired + public InternArticleController(ArticleRepository articleRepository) + { + this.articleRepository = articleRepository; + } + */ + + @GetMapping("/") + public String internListedArticles(Model model) { + + + List
articles = articleRepository.findAll(); + + System.out.println(articles.size()); + + + + // model.addAttribute("ListedArticles", bookService.findAll()); + + + + return "intern/listedArticles/index"; + } + + + + @GetMapping("/{id}") + public String internListedArticlesId() { + return "intern/listedArticles/id"; + } + + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 2a88b7e..27223db 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -11,19 +11,19 @@ import java.util.List; import java.util.Optional; @Repository -public interface ArticleRepository extends JpaRepository { +public interface ArticleRepository extends JpaRepository +{ - @Query("SELECT a FROM Article a WHERE a.id = :articleId") - Article findArticleById(@Param("articleId") long articleId); + @Query("SELECT a FROM Article a WHERE a.id = :articleId") + Article findArticleById(@Param("articleId") long articleId); + @Query("SELECT a FROM Article a") + List
findAll(); - @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.should_be_advertised = true") - List
getAdvertisedArticles(); - - - @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") - List
getOrderedArticles(); + @Query("SELECT a FROM Article a JOIN a.related ao WHERE ao.should_be_advertised = true") + List
getAdvertisedArticles(); + @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a ORDER BY co.id DESC") + List
getOrderedArticles(); } - diff --git a/prototype/src/main/resources/templates/intern/listedArticles/index.html b/prototype/src/main/resources/templates/intern/listedArticles/index.html index ea62db9..adce5ef 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/index.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/index.html @@ -51,7 +51,7 @@

KameraKameraÖ 100,50 EUR (84.45 EUR) Úberwachung, Elektronik
Bild Name Preis (Netto) KategorienLagerbestand (Aktiv)ArtikelId (bearbeiten)
KameraÖ100,50 EUR (84.45 EUR)Úberwachung, Elektronik301 5051890
Earbuds63,95 EUR(53,73 EUR)Kopfhörer, Elektronik12 84013850
USB-Magic Light11,90 EUR (10,00 EUR)Sonstiges, Elektronik384015784
3D Magic Stativ15,99 EUR (13.44 EUR)Úberwachung, Elektronik421354564
Ersatzfernbedinung7,95 EUR (6.68 EUR)Úberwachung, Elektronik045654566LagerbestandAngebotID

From 3a3b7abad0a0bdde8fec217ae1c9f4e2ba8ff744 Mon Sep 17 00:00:00 2001 From: Seil0 Date: Sun, 10 May 2020 15:31:20 +0200 Subject: [PATCH 050/203] fix typo --- prototype/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/build.gradle b/prototype/build.gradle index ad8b14c..c7defd9 100644 --- a/prototype/build.gradle +++ b/prototype/build.gradle @@ -35,5 +35,5 @@ group 'org.hso' version '0.1.0' bootRun { - args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties\""] + args = ["--spring.profiles.active=dev --spring.config.location=classpath:/application.properties"] } From 22d2744c8c572fdc38f80bd99e684c217a15106c Mon Sep 17 00:00:00 2001 From: localhorst Date: Mon, 11 May 2020 11:58:26 +0200 Subject: [PATCH 051/203] GET article edit page --- prototype/data/img/product-1.jpg | Bin 0 -> 13288 bytes prototype/data/img/product-2.jpg | Bin 0 -> 17951 bytes prototype/data/img/product-3.jpg | Bin 0 -> 14913 bytes prototype/data/img/product-4.jpg | Bin 0 -> 5682 bytes prototype/data/img/product-5.jpg | Bin 0 -> 9289 bytes prototype/data/img/product-6.jpg | Bin 0 -> 14439 bytes prototype/data/img/product-7.jpg | Bin 0 -> 12305 bytes prototype/data/img/product-8.jpg | Bin 0 -> 8928 bytes .../intern/InternArticleController.java | 65 +++++++++++++----- .../templates/intern/listedArticles/id.html | 39 +++++------ 10 files changed, 66 insertions(+), 38 deletions(-) create mode 100644 prototype/data/img/product-1.jpg create mode 100644 prototype/data/img/product-2.jpg create mode 100644 prototype/data/img/product-3.jpg create mode 100644 prototype/data/img/product-4.jpg create mode 100644 prototype/data/img/product-5.jpg create mode 100644 prototype/data/img/product-6.jpg create mode 100644 prototype/data/img/product-7.jpg create mode 100644 prototype/data/img/product-8.jpg diff --git a/prototype/data/img/product-1.jpg b/prototype/data/img/product-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ea7598d85d80b9d7c26f17db2b13b81c3675057 GIT binary patch literal 13288 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 literal 0 HcmV?d00001 diff --git a/prototype/data/img/product-2.jpg b/prototype/data/img/product-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..427b2f0ed62eaff94147f8500356d77bc7163902 GIT binary patch literal 17951 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 literal 0 HcmV?d00001 diff --git a/prototype/data/img/product-3.jpg b/prototype/data/img/product-3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dbc1b2e20a08d8bacc98dcce28a79f3c5e00de4c GIT binary patch literal 14913 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 ZtpyiTXtx!Xl#_nxP|F#=c$@uP`(H-x3LO9d literal 0 HcmV?d00001 diff --git a/prototype/data/img/product-4.jpg b/prototype/data/img/product-4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8f2d857f98e5468e92e4718e087ce3d0f3b5846c GIT binary patch literal 5682 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 LipLRA(ue;5V?X(| literal 0 HcmV?d00001 diff --git a/prototype/data/img/product-5.jpg b/prototype/data/img/product-5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5e0a406afbbed9d563034bc001b2831175b41056 GIT binary patch literal 9289 zcmbt)g;N~c6YeawxI?fY%i>Og>tc($I|O%k65QQ_JHg$Z5S#>eCqWV{2^I+Uxc7Ho zy+7dfoT;fgHPhee>OOs{`p&QQ@UI{~3lh@vKt}%O zMn(BYP%*)1;OBvffrXBaj)g&hgN21dK#Y%vPfSKaLPkSP!^$cI`u|n%Gzj3K0vo^v zA_xK?;DHeFKu<#e^Deb>#382LR?}V^RM`=;QO`?c2~HY56y&Z4b|%MP3oq8tR*^D=!ZK zAZ5Q}-Q4D7YDfCDAOL(Q-r>>7ZFvVt;7Lvb?q?5cuBP6 zw#6NTB{hJ6e^ed0|4v!$&xwjJr8t06>cNs9sImXMoII#E2tfRL^joEg34QyZ5pLGA z2LRgaHE-iSNFR3Q&-Lv9z#Myq0M+o}2`MK4AW|NOkDpQQ-adP4_xaU-1?M>y$Uy(e ze{;_d5CS3+3i7{kfPnko^!Z&QfazyVW6HC1d48?!65KF*TK9$C4k=FWbuVk{%+ih9o?@8dh2?v(phbxVpDAtzgWo)2OYj`YP^2eI@0 zw4bHEux&h=q6Eyz9luSN8FsETK9N1Bvm$JjSjw~I#^t^$L;Q4F6%7(E2=TWxRWRA} z*cRel-IboFKhe%!+_LS-3k$Rq@w)$^stNy23(XHOl zTmQea-k+VJDo+4g`>=Gi(|KNy1lnA9T*DV8qw`6HlqN2F7D@k?=)=c)GLBoXjd<1` zc~?8t8U%w@SYv8`=lysLKeFbosa79XKPFRBS5CI|eo6g{qC28$5M;u7vf6#SGMG*7HivDxAa3$%Uw z<#sErC`(h(C*X)40O36WFSZgtJ!eJTIyJ}lGr}LNW~pA=@r;v(*FQ>i2_B&r4=R-f z<{kVj)Khh3{42M&m#(a|n8FGjlxBeLh_=!vqel4K241uTPDAc5U%4{bx?l$DY{a%` zSHvwAcL#aWA3OoE+pSA+kmS_Fiiv$PUTMvk*p3yeZxDwz_AeE4o|A+h-l4zJI&SIL zyb`e!(Cb))qk z#$L0Oe3-GHF6_1X^{N3raXZ%o*HlY$HFCUl;>j5&PfPCcb&O~_fT?TJoRK|bUeO;( zJmtwO*|T-a26U%U1P^I3f;)mn+kNiewFFQ3_0ha-M#Id(gCSVmjJ!1`-gcDsKJP_V zr~svW&`7dATAlNg4vSPWAV{HOa>?||!bOggBIip=(F8#kbsza@EKF|^GIBmX0R^iB z0PwfnDT|H*8~-}$a(T8VV~>b1U74w=p7_LRsMqZc6ROUVCyAc#!Me!fh9XEK~j0M&)U5Qgn>FR5hcf~mK%sA~O zeIP_`b%)l{QEbUb|21FxWHrjdrcR8ok7i)C%^)aQc%ZL#;q47CmZV-$w+SXwmM1}m z65`s+y0`^w^)gG9TGpL$e5}=PW-|BBzd86ZSqQNu`QBCP#h1f7+O)RG(>>p-a&z}L zziF8TGkjwxNA|kC?))%YPgAL-XDX_Oc9kV33;-7%=15bQ(I_ zPdGHxDAg05DnJOj{>=JAmjn|d`3dN(>-@43*0C~|YAoZqvvq>XhKBPJVJ4lCw;$O! zPmahEN|F~;h&6j7)yoz*Q2m=~?Z!IO_l>1+F>jisg~KIG6ail1nVh4S}*yUoV(Ges+4 zxU%x<6Klje`{z3ItHtCSgM|Q_mBpJVT;<01O$tO@T&ST^-&Z1zn<3Z~N!@aszaE?XR&ctg?sfnsyiZV^+;5(Sq+F*|jCS zNoHhNuO<{w2S)gvC1&kIt&_NABFLR+z}mjJt$GU%?TJnq$Vmzejoo}pcOs|b8X{md zXIeGsj&*kvg6=Q5>YlEdp|uP-Hw_0z@8^t1xUWakYDR^>WTwZfw-TW*uZWH&Z8P5QMy8Y~_Wsv#2r}f*sQA)) zg-#^QzI7(GX}qyo{e&@W=w!WOIqRg9&Jx$wFfy^8uR{G*R?X}r|IN~nJL-g>;J{=0 z8$~gNQS#K=5$1!oLlgDIn-sMg`ZswE0h@f7-LLWscCzi4q7g#Tvsp^tB)yHHOAt`K zW&+Uh)Y0k9qi||;q_weO5Jrdv-;odoEhxvFPHyv!LRw<*8|#NJ{NtZT)@q*s-fF!? z3opAz23=E3KQNmoNir%i(+b04RwS2Gk{Yr>!AjsmFT4Z)3HZ4nozL4BZ7ka>mt)^9 zwdynr4XcsCusPE^MD~tPz^7M(yVa1jJIx|MQY;~2v3t{5m|uQ-{JnDyRc(@@Zk#tV zDQ@=l9Y=`b6elZYlxZ|HtSj2=9pJYv9$IJ_VAF&g~O^Bmy#eg1!KeS^H6nm9><<}h? z$w;;>wrcm9ceN)qZ;I_N;AOFGp0pjZADWlSEuQGDvoDu>NEAfx#;9iZBIuU8wN$gd z`b52#BJ*9=l6nvCPmW8>L-LkY7v^hkJ!}qVf@NvzpSAJA#{9|KuQ@m3vFcGw`_sLE?ko@okN_9Ad;2=~&TxM}Kt%-T^S8uOT1lc0ZBQ0vS zxgf;eMLs(4$DnDMe(&qEs?+WO2L-rtF+Kz-!%c5{>&+9OhF#p!9N7fl4-r-8`~DgY zL8>a9MvT^Sv6Xc!gAcERF0wAp#nL9sKHY?qTri*UAjD>aTaF@z)JWHZ88${c6u2el zAESB9AVggL)#B`{y7)^JIVdt&{Z=S4DOxwA2;Ki!+u$7+k+u}KmI2$W@eLVG%gy+W*jQz8!5AR`z;Pesy;FlM}78d+e+~+ zECjUlqgP|yyLOU(ROgp(F)KLw?mr{g2$ot96D|F&{aZ)q%Q zmKFmv(hUaK={cTsf5DoVGwd;M2%dji9D`VnfX)x#3f8q7PrX{)5`tTtrmvMIScSS& zDvLgT8pkts$*`bi?-pSrU{k8nZ8JH}pO(X!Ig53V3}g{XU>7J<(#f#fY&^Nb8gyJ^ z`rbd+o8!ef$lyL3%PTa+Q9IE!t*?R@I-9)wtBaF)au2@1v6j7XjHGa3i%Qnc7(uEk zYxiepN5;YR?BkmmiwDO{!=>a1|IPtnDpi9}r&EFAUDhw{(GeCh%q}-bU_|C^5w8#! zTVT8G`rg)`(^2R47gSdL!ezNNk@W&5pW}`)w!66_6;{`wkhJ0xVF`8^r&z_ z-2E0Ui&3|Brj0#omtM)LvHA!9Ru{7w3a22|)`-K4d>oiV%&z2Yd_z0g8JT)0S-jFV zsf?3Jd6!%+Lc4B?U~*-3uoka?)NiLZd7BSy6rc=Mj1XwsuSxcH!>%dZYjp5bSLxwt zU!I!x(*E;@%r&uL=w=%A2@rg6B*b-A#m=AA`&HO}$MmRCI~h>kd9AKbv`bvxb5^jq z?;BHWF$D6WrrzUeT5IfaP&+j`I}~vo8=0j&6Dl1RKTsi6r*l-5j7?w>rp8!~9+ zf@))8ae6ylOOPsB5;6{uZJ9=#eN%BwTs42}iDg_bvk;f}ZroAiM9lM7)UFgsCEix- z4n>!xG@qM5bpt0aeF4b%Y@wMvSaVTvleh#0q}n{favxo~)MgnaIhipR$TV3NT z$0=jaKIEp@ePRm1d5`K7Pmm&ar% z_l=oT7YAXjOi;JXzxQLz5IjY;NCdk*zKwJrwv!rHI-3|WRvKuTSsLMEQYGw$P*duM z>&Y{`(aqPv0grJCNJ6 zJBZxG*Rlwjt%)ExBm}rxwbmx~rh*euB}o~w{JJk*SthQ_@I~#6l7C~}ytEZmH#egg z8o$9ZNWOuzhvk^DCeme@E|-r`!kWh!Mlit|f;Un%C8cfO&S)vQz)g{hfbElCUFcRm z8yURhldh^vKK}%`9=#5*Ofv-?7jk<|NKFffecBu(y)41-_O#SfIlzN&^?&#IzUNIC zRQ6&riG${W#EyH|T-_&RubHwmMNWWKj@2)Zijvvj;p%lx=~(|jieXytl^wSID91Za z+Obl@s2MPha5eh76jA4@`4u~(VfXD9qn@nX-QAQ>)3ikv2-Z=wLv1#9l4jxpUg`ob zPp=G{_^+kE*dXwnvPMjZ3@!@w;eOrfM0djUOJ5?)7wD+g>t$}-W2K?QJgs8y8XQwe z^*4xdI#N)XqCa4w9a*3fS|ryt?d80)shFySHf+@FS|tcKjI!b5EuuzTFRTa1z{s|m8D}^{BRww^L2MnXdNs&5+GUg1)<%wm z8J%t4X>AMWwv>USp3D^R^#F9z&Lh|_z=BpDr?Y)XPgDl}HT#zVapro#6A)vY;Gcgv z4BWzB5Y8THa$($IQQ`aL>AFG^8EZg6-)1X`g0lm~Lm{pgcGE!)%?PzDE@zq1;$#7;xj~}QVBrFPwcOQUIbpdwDVze6 zsM(A3uE!X`A5+9N=n9ROXe8Fo^U9zXe?oFB0#v>tOwT2sD|v7zm{)FzJ<=aO0mdcN zvHmANJ348nyHmeMe$S3Fqpru7CQFo-9iUpX73zrVKXeowvW z*}^IKs&<@Q6;HB$ojuVAv$QabE^o}2BcVkmv^WZ*oKo`e#v-LlQ#NXTy@&Vy`)d?< zO9NN6uWK}C=oQj;4@Prk{>yLi3TLaKr_3C`3q=9n`UoBwNef5yGkP)t)udpG89LL1 zUAb?JR)QF#uPfmg4fk`PFVn+`iyEhxGYaROdX0CPm&&o>%z-!s36bn}ozx-R9Y4Ne z_l(b+{{q9uuzay_G-^ByBLeOTExvnj%kqbwQwri&)-}++4xc%vAmSd1vo2gfD(jly zy3E9a>OPXf`|=|ttZlhDWs}aeAK)s%^HZ1=MoIE&q28E37Lj65_XH1Bm3ZvQU#rQs zEwN_2(obqfLlZ^93=YCBRXt8#2Jd*b%4hJ72K@3Vofubk@U$rfDk-y@%2=1Iu-FW^ zE>l`E@6`%6Odqs#N<0I`&aBn-JV1Kj14@C*F@YHo*srl!{>R{%6qnb+Nb)9B^^s?u zPXN9((&HyYMc3`+D$~+e_Zs7aoT82;2`*Ap9v!0Qw1nbMfM^89sCv4*$=O#&lFx3= z@5}i3P_(~mS-yXaqT(Vka-I{?t3)?5&7s?i7k|X}AL}DE(sRJg(~8Ugjqxk>lwp*1 zTD@L!4<1E*i+2}VCZP5@p3@{HKN|=p)U|^8TGPO8^Q2jAMb>%ep!FXyN_|A{ctNx` zIn;LhnMYp1Mbln+@*lf~@>znfN3-oT#bNAmOfgx&BN$@)CdzYw`htbrw-VeF_18v)!i!a!qCV{%#>-8YX=-YVs*u%% zguFQw3pi~cZhF{aUK61;s4L`?HVbO)*?I(P_xr@K5QbcjxV;1#S?VL7_zLEn4`Iap zgN@*{61=bSh!s$~!Ep3q5=at0X0xm~K0#chl{swD!|SBAGI_o?7|c+V1&qg@N*)zx zNytvSvJQ5(q9OrJ9%#&mQo)Sacdb9yxEm8Meh7AKu#sJf$zR^Q{cHe3$o#I6&`&KE zHB#94H!mOWbaT%CTq7=nAkL@P&HcBMqZcS<-#s!$lcuRZ?V)U8NRD8;!8%_%Ss@bD z4|(k4h)5Kj>& z^L>BpKUS^e`C2C{Y2zoq!oN!i67^VIu(P8Nk8XB&E-*Y3qMpmYAynB$R2^0-u-i`Zm>a0{R2AfD8&^pD;+2 zET5@x{SWz!WV_OU=_pinWOR$6QdM?o#jKl$B}ECD37W@A)+)dHYC_Yr(UPp3Ld&6a zrk4MYpGE$4ugw&i7CI$9jM=kuN$*&(&u&Xm41q!U?VISd6Q@tVa*u(q zp>O4)5P_6gx|{0Io*dfMjtso&IG~F6VML}H^FUV}#$4-}tm3wcxTTI=R^=!~(`(|! zsX|Q+9ci>kkRFjo3_@Kx?ijR4XVPi!*+}b(+DY&xl?}zL%0Pr3n^)pi^F-TkX2`-Y zccF@fNr*F^z>hce+X~FJn_H7olT68_si{S*o#?3Of}pyY&0!Ypq`|aj1)v39)OvyM zTd=-UU1`coT2fOKV)Fj+S-ShKDyR-_E+1aVZ_nSO$_ztsb_*!BydsV9bm5@6h-ul6vyr5t=KqHn6TS&%( zjun-=ClaThA8ISac#+e_QtVjIgw`f>1U8+-FMF&lgzHICwZDEzY{JdC?b(W%v=Rav zhsH=Ve*g3t*F~nBaT8JQVN&q|bY}q=VFDZ%(`DZgx z6L_cKMC@QL_cd#cE?=f`{;%l>;TAuC!*8i?&dY-y+|OsfJ1B4DFnp8=02e1@r&afz zb`GiALPR~8bA?j(4A6?D-m`y}TyIn?ZMLpwDUgkzJ3i5dU$!X)p$x@`+ez)-pkA)wed_6LbV}mA|;T0r;R^CUH za5|Y)XX;7MXKv!>R-2HHhT-4bzbK2XZ0t5Ql`;!iBAalYMftvu)qW2mKNOqVN%)fq zA}@?}I}eQ5n@zk&-fnw+K8(kz=FGX=Q8uY^=(MD;*l}0icK=49DLXV|P8B*|&dq1b zbq;?y(WewOn%T){Fhei4atnU_ff?#J3Z8ub!13%14!mb_8{zq95(M!7eDM##jRzn- zH@9HV-7PgjYECm(Tw(~fxN2~6NihM(Gw={8Kx;vEGKmU zGQel#*(gvY;c*?xd^o_LA~~aRIiOr{tIhbq_CZi0uyj~zs()ZEE%y_wK12MX@Z@b) zD$lf;QySe+`dL;Ai^hz$7MVQQmuFnZhOmXM83;XH|AInaU{Jhi*%Ho=FR$)hgF`BP zI&QU-7M+EF=_D%srY(Ngr#_}xzlS?l0DV3!-j7+(id?JdUQ$+9PF5WjLY;WlRK%bm zM)ZaDJgT_Ny9`vm%v?d+Ye594&CCw0@<&EXmNjS+t0sZI^p(HqHKZl>`c3gZ+iAg) zIc;KbBY=!WJt{K@m;ud2hpGqUBqqaJM)mI@G1a^uVxmir%-dq<6^98Y0zMAf^T?em z1{?aCjRfGBMH&Y7y1JcFt3+4`ufrLY9nOgGReA6rjVLD>pAZ&D=lbf20O%-;U}i3i z%2|n?={WL?5Tjas3`AX#j*Ai`x1yQ^zZD-Oj0*}S8!3KH@vzH}71ca#FVe+fcsj(l ega)*m=+~!5h%i@_{6Oa{wN$*Lly}lktN#biSnXv1 literal 0 HcmV?d00001 diff --git a/prototype/data/img/product-6.jpg b/prototype/data/img/product-6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..05d78c0a6aec4fae5e550121d0a0e7455258c178 GIT binary patch literal 14439 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 Y>KZBdK>RQMLHXBg{U19i`7rap0LF-%xc~qF literal 0 HcmV?d00001 diff --git a/prototype/data/img/product-7.jpg b/prototype/data/img/product-7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..220140c357067eaedd69bba904c7c08949778b4b GIT binary patch literal 12305 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 totals = new ArrayList(); for (Article article : articleRepository.findAll()) { ListedArticlesListTotals tmp = new ListedArticlesListTotals(); - tmp.addListedArticle(article, - warehouseEntryRepository.getArticleStock(article.id).orElse(0)); + tmp.addListedArticle(article, warehouseEntryRepository.getArticleStock(article.id).orElse(0)); totals.add(tmp); } @@ -39,13 +36,21 @@ public class InternArticleController } @GetMapping("/{id}") - public String internListedArticlesId() - { + public String internListedArticlesId(Model model, @PathVariable String id) { + + int articleid = Integer.parseInt(id); + + ListedArticlesListIdTotal total = new ListedArticlesListIdTotal(); + + total.addArticle(articleRepository.findArticleById(articleid), + warehouseEntryRepository.getArticleStock(articleid).orElse(0)); + + model.addAttribute("ArticleID", total); + return "intern/listedArticles/id"; } - public static class ListedArticlesListTotals - { + public static class ListedArticlesListTotals { public String imgPath; @@ -63,14 +68,11 @@ public class InternArticleController public long id; - void addListedArticle(Article article, int stock) - { + void addListedArticle(Article article, int stock) { this.imgPath = article.image.path; this.title = article.title; - this.price_netto = String.format("%.2f", - ((float) article.shopPricePerUnitNetCent / 100)); - this.price = String.format("%.2f", - ((float) article.getPriceGross() / 100)); + this.price_netto = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); + this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); this.categorie = article.getCategories(); this.stock = stock; this.offer_id = article.related.id; @@ -78,4 +80,35 @@ public class InternArticleController } } + public static class ListedArticlesListIdTotal { + + public String imgPath; + public String title; + public String price; + public String price_netto; + public int reorderMaxPrice; + public String categorie; + public int stock; + public long offer_id; + public long id; + public boolean shouldReorder; + public int warehouseUnitsPerSlot; + public String description; + + void addArticle(Article article, int stock) { + this.imgPath = article.image.path; + this.title = article.title; + this.price_netto = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); + this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); + this.categorie = article.getCategories(); + this.stock = stock; + this.offer_id = article.related.id; + this.id = article.id; + this.reorderMaxPrice = article.reorderMaxPrice; + this.shouldReorder = article.shouldReorder; + this.warehouseUnitsPerSlot = article.warehouseUnitsPerSlot; + this.description = article.description; + } + } + } diff --git a/prototype/src/main/resources/templates/intern/listedArticles/id.html b/prototype/src/main/resources/templates/intern/listedArticles/id.html index 20b64c3..7097249 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/id.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/id.html @@ -5,7 +5,7 @@ - Gelistete Artikel + Bearbeiten: Artikel @@ -15,7 +15,7 @@

@@ -57,7 +57,7 @@
-

Jetzt Shoppen und Empfehlungen erhalten!

+

Jetzt Shoppen und Empfehlungen erhalten!

diff --git a/prototype/src/main/resources/templates/shop/search.html b/prototype/src/main/resources/templates/shop/search.html index 895ba53..9513c88 100644 --- a/prototype/src/main/resources/templates/shop/search.html +++ b/prototype/src/main/resources/templates/shop/search.html @@ -21,70 +21,27 @@
- +
From 6b9db3eed5323a0acad1716ae89d93b4836ce4e5 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 15 May 2020 12:23:32 +0200 Subject: [PATCH 077/203] implement errorhandling --- .../shop/ShopArticleController.java | 5 ++-- .../controller/shop/ShopSearchController.java | 18 +++++++------- .../repos/shop/ArticleRepository.java | 9 +++---- .../resources/templates/fragments/header.html | 2 ++ .../resources/templates/shop/articles/id.html | 15 ++++++++---- .../main/resources/templates/shop/index.html | 24 +++++++++++-------- .../main/resources/templates/shop/search.html | 8 ++++--- 7 files changed, 46 insertions(+), 35 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 34cb03d..843d9a0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -40,7 +40,7 @@ public class ShopArticleController { HttpServletRequest request, HttpServletResponse response ) { - model.addAttribute("categories", categoryRepository.getCategories()); + model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar Article article = articleRepository.findArticleById(id); @@ -104,5 +104,4 @@ public class ShopArticleController { response.setContentType(MediaType.IMAGE_JPEG_VALUE); IOUtils.copy(in, response.getOutputStream()); } -} - +} \ No newline at end of file diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java index 4819607..5a204bf 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; @@ -24,25 +25,24 @@ public class ShopSearchController { @GetMapping("") public String searchArticles(@RequestParam(required = false, value = "term") String term, @RequestParam(required = false, value = "category") String category, - Model model + Model model, + HttpServletRequest request, + HttpServletResponse response ) { - - model.addAttribute("categories", categoryRepository.getCategories()); + model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar if (term != null) { //if search by Term - List
articles = articleRepository.getArticlesByTerm(term); model.addAttribute("articles", articles); - } else if (category != null) { //if search by Category - List
articles = articleRepository.getArticlesByCategory(category); model.addAttribute("articles", articles); - + } else { + request.setAttribute("error", "Es wurden keine Suchparameter angegeben."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; } - //TODO: hier eventuell noch Errorhandling für nix von beidem return "/shop/search"; } - } \ No newline at end of file diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 3a3f5d7..4520855 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -26,12 +26,9 @@ public interface ArticleRepository extends JpaRepository { @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a WHERE c.id = :customerId ORDER BY co.id DESC") List
getOrderedArticles(long customerId); - @Query("SELECT a FROM Article a WHERE a.title LIKE %:term%") List
getArticlesByTerm(String term); - @Query("SELECT a FROM Article a JOIN a.categories c WHERE :category = c.name") //TODO: Wahrscheinlich falsch! - List
getArticlesByCategory(String category); - - -} + @Query("SELECT a FROM Article a JOIN a.categories c WHERE :category = c.name") + List
getArticlesByCategory(String category); +} \ No newline at end of file diff --git a/prototype/src/main/resources/templates/fragments/header.html b/prototype/src/main/resources/templates/fragments/header.html index 1ec92b4..4f14d7c 100644 --- a/prototype/src/main/resources/templates/fragments/header.html +++ b/prototype/src/main/resources/templates/fragments/header.html @@ -39,6 +39,7 @@ document.getElementById(id).classList.toggle("invisible"); } + X
@@ -55,6 +56,7 @@ document.getElementById(id).classList.toggle("invisible"); } + X
diff --git a/prototype/src/main/resources/templates/shop/articles/id.html b/prototype/src/main/resources/templates/shop/articles/id.html index ef18fb4..00d2c73 100644 --- a/prototype/src/main/resources/templates/shop/articles/id.html +++ b/prototype/src/main/resources/templates/shop/articles/id.html @@ -23,7 +23,9 @@
-

EUR

+

EUR +

@@ -32,7 +34,9 @@
-

EUR

+

EUR +

+
@@ -59,7 +59,7 @@
-
-
@@ -78,17 +78,14 @@
+ placeholder="Optional: Zusatz Optional: Unternehmen Straße Hausnummer Postleitzeit Ort Land" th:text="${user.defaultDeliveryAddress.addressString}"/>
-
+
@@ -100,9 +97,9 @@ Deutschland
- +
- +
@@ -115,7 +112,7 @@ Deutschland
- +
From d8032c2bd7222242a9a3465e3c6ebdcb3bad8b02 Mon Sep 17 00:00:00 2001 From: localhorst Date: Tue, 19 May 2020 00:12:26 +0200 Subject: [PATCH 086/203] load offeredArticles from DB and display basic info --- prototype/e-commerce.db.copy | Bin 0 -> 147456 bytes prototype/scripts/addarticles.sql | 4 +- .../suppliers/SupplierOfferController.java | 43 ++++++++---------- .../entities/supplier/ArticleOffer.java | 8 ++++ .../repos/shop/OffersRepository.java | 16 +++++++ ...useBookingPositionSlotEntryRepository.java | 2 +- .../intern/offeredArticles/index.html | 2 +- 7 files changed, 46 insertions(+), 29 deletions(-) create mode 100644 prototype/e-commerce.db.copy create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java diff --git a/prototype/e-commerce.db.copy b/prototype/e-commerce.db.copy new file mode 100644 index 0000000000000000000000000000000000000000..e4c39c9bce811412014123a8071f9c885249b581 GIT binary patch literal 147456 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�K6J1i$MZ{5Q@)@f$6O)(|mj%An8G5yBVyB9t2BF=YuOmLmIBWM4To9#R4 zMjA_{{7)^PVewYQzxj^;gXsKS75VeOt5_E33A4|IDz`q0z0hz*_SS@8ZBJ_h*<^Ox zF4S_bJM%oxSlo?m-8jBiuffm0tuKDZ;oLUukekvP9%rDcMJcv9UymovD_mS&pnpEd zU87fDf6e6M&lJDYdTunz6x=4t>~7GCk=J|jCi_L+b1SKs=IP;0CuOO3v+urGYWPXN zzH6u6mR!1!?CK`0MCiQlu*IHc_XiKUTSN5cXu8T4->S3>>w#`2=HJlYkRwTg_4J=N znbbA4)GC%fvi{ul+Wo@LoA2|y>j>IP>w0NSm8oJ$nCMUO7OyU)yFci?eQ^=X$2)X- z(pk-uRoY7?>AtPCIP+D^x98B)kPS2=t(gmpM4Ge+rz$L=*zVJlg2zi=6jRY^dS|+; zn*4dGCAH}AmBUe|L+ygKdT%xeb+h%)xl6Ag>gd$89SA1n2Qn-Sg)kKM40K->ufP=RvF8Pbl+=`{|j78eV8_aCtnRnSoL@&VJzCa|T^+ zT^Y5)*?u7=V={zl>)!Q@aN~ZJ*@uquwAjOth}QIEY*Bmqhv1V3W<_+}zW?HB#$k?v zUe*F5Upo~;$RooFUBC31z9_kp@oJG2Y;DQ?XlKY8^{ zvX0hC_@Qd&(|OY2&CMcUX-IZ#^OHV4;E?ybxq2Enk_(S{T)G3_- ztvMyDQ$MbrEQM(|d71t?kKujW_rlAGr>|GPwr$*SNz&CeI5Or`jLeIi1^tCEkqUw@HY&y@h9w99P%PD98LΞ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 literal 0 HcmV?d00001 diff --git a/prototype/scripts/addarticles.sql b/prototype/scripts/addarticles.sql index f7e5f5b..b60497d 100644 --- a/prototype/scripts/addarticles.sql +++ b/prototype/scripts/addarticles.sql @@ -1,5 +1,5 @@ -INSERT INTO article_offers ("manufacturer", "article_number", "vat_percent", "should_be_advertised") -VALUES ("McDonalds", "1", 7, 1); +INSERT INTO article_offers ("manufacturer", "article_number", "price_per_unit_net", "title", "vat_percent", "should_be_advertised") +VALUES ("McDonalds", "1", 4242, "McPizza", 7, 1); INSERT INTO articles ("related_id", "shop_price_per_unit_net_cent", "warehouse_units_per_slot", "should_reorder", "reorder_max_price", "title", "description", "image_id") VALUES (1, 19.99, 10, 1, 15, "Huge Hamburger", "This huge Hamburger is awesome!", NULL); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java index 501d749..685f8f8 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java @@ -3,6 +3,10 @@ package org.hso.ecommerce.controller.intern.suppliers; import java.util.ArrayList; import java.util.List; +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.hso.ecommerce.repos.shop.CategoryRepository; +import org.hso.ecommerce.repos.shop.OffersRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -14,29 +18,19 @@ import org.springframework.web.bind.annotation.RequestParam; @RequestMapping("/intern/") public class SupplierOfferController { + @Autowired + private final OffersRepository offersRepository = null; + @GetMapping("supplierOffers") public String internListedArticles(Model model) { List totals = new ArrayList(); - /* - * for (Article article : articleRepository.findAll()) { UImodelArticles tmp = - * new UImodelArticles(); tmp.addListedArticle(article, - * warehouseEntryRepository.getArticleStock(article.id).orElse(0)); - * totals.add(tmp); } - */ - - UImodelOfferedArticle tmp01 = new UImodelOfferedArticle(); - UImodelOfferedArticle tmp02 = new UImodelOfferedArticle(); - UImodelOfferedArticle tmp03 = new UImodelOfferedArticle(); - - tmp01.addData("Title01", "manufacturer01", "articlenumber01", "supplierName01", 4884, "42,42 €", "ads01", 5); - tmp02.addData("Title02", "manufacturer02", "articlenumber02", "supplierName02", 4884, "42,42 €", "ads02", 6); - tmp03.addData("Title03", "manufacturer03", "articlenumber03", "supplierName03", 4884, "42,42 €", "ads03", 7); - - totals.add(tmp01); - totals.add(tmp02); - totals.add(tmp03); + for (ArticleOffer article : offersRepository.findAll()) { + UImodelOfferedArticle tmp = new UImodelOfferedArticle(); + tmp.addData(article,"supplierName01", 4884, 5); + totals.add(tmp); + } model.addAttribute("OfferedArticles", totals); return "intern/offeredArticles/index"; @@ -117,16 +111,15 @@ public class SupplierOfferController { this.listedArticleId = listedArticleId; } - public void addData(String title, String manufacturer, String articlenumber, String supplierName, - int supplierId, String price, String ads, int listedArticleId) { + public void addData(ArticleOffer article, String supplierName, int supplierId, int listedArticleId) { - this.title = title; - this.manufacturer = manufacturer; - this.articlenumber = articlenumber; + this.title = article.title; + this.manufacturer = article.manufacturer; + this.articlenumber = article.articleNumber; this.supplierName = supplierName; this.supplierId = supplierId; - this.price = price; - this.ads = ads; + this.price = String.format("%.2f", ((float) article.pricePerUnitNet / 100)); + this.ads = (article.shouldBeAdvertised) ? "Ja" : "Nein"; this.listedArticleId = listedArticleId; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java index ae86659..405fb4a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java @@ -14,6 +14,12 @@ public class ArticleOffer { @NotNull public String manufacturer; + + @NotNull + public String title; + + @NotNull + public int pricePerUnitNet; @NotNull public String articleNumber; @@ -21,4 +27,6 @@ public class ArticleOffer { public int vatPercent; public boolean shouldBeAdvertised; + + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java new file mode 100644 index 0000000..b1d6f0a --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java @@ -0,0 +1,16 @@ +package org.hso.ecommerce.repos.shop; + +import java.util.List; + +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface OffersRepository extends JpaRepository { + + @Query("SELECT a FROM ArticleOffer a") + List findAll(); + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java index b342a06..cb9902e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -15,7 +15,7 @@ public interface WarehouseBookingPositionSlotEntryRepository extends JpaReposito List getByArticle(long article); - @Query(value = "SELECT SUM(w.new_sum_articles) FROM warehouse_booking_position_entries as w WHERE w.article_id = :articleid", nativeQuery = true) + @Query(value = "SELECT SUM(w.new_sum_slot) FROM warehouse_booking_position_entries as w WHERE w.article_id = :articleid", nativeQuery = true) Optional getArticleStock(long articleid); } diff --git a/prototype/src/main/resources/templates/intern/offeredArticles/index.html b/prototype/src/main/resources/templates/intern/offeredArticles/index.html index dfb36be..cfa6913 100644 --- a/prototype/src/main/resources/templates/intern/offeredArticles/index.html +++ b/prototype/src/main/resources/templates/intern/offeredArticles/index.html @@ -45,7 +45,7 @@ - + From 1ce9a971bad4135ed8953857e68993566868e8f4 Mon Sep 17 00:00:00 2001 From: Hannes Date: Tue, 19 May 2020 11:52:41 +0200 Subject: [PATCH 087/203] show all Orders --- .../action/user/ChangeUserAction.java | 7 +++ .../ecommerce/controller/UserController.java | 59 ++++++++++++++++++- .../shop/ShopCheckoutController.java | 4 +- .../entities/shop/CustomerOrder.java | 13 ++++ .../entities/shop/CustomerOrderPosition.java | 4 ++ .../org/hso/ecommerce/entities/user/User.java | 4 ++ .../repos/shop/CustomerOderRepository.java | 11 ---- .../repos/shop/CustomerOrderRepository.java | 17 ++++++ .../templates/user/orders/index.html | 46 +++++---------- .../resources/templates/user/settings.html | 2 +- 10 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java delete mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java new file mode 100644 index 0000000..af9ef9e --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java @@ -0,0 +1,7 @@ +package org.hso.ecommerce.action.user; + +import org.hso.ecommerce.entities.user.User; + +public class ChangeUserAction { + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index c462bd0..fee6946 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -1,14 +1,19 @@ package org.hso.ecommerce.controller; +import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpSession; +import java.util.List; @Controller @RequestMapping("/user") @@ -17,6 +22,9 @@ public class UserController { @Autowired private final UserRepository userRepository = null; + @Autowired + private final CustomerOrderRepository customerOrderRepository = null; + @GetMapping("/") public String user() { return "redirect:/user/settings"; @@ -37,14 +45,63 @@ public class UserController { @GetMapping("/notifications/") public String userNotifications() { + + //TODO: implement this + return "user/notifications/index"; } @GetMapping("/orders/") - public String userOrdeers() { + public String userOrdeers(HttpSession session, + Model model) { + + List orders = customerOrderRepository.getOrdersByUserId((long) session.getAttribute("userId")); + + model.addAttribute("orders", orders); + return "user/orders/index"; } + @PostMapping("/settings/changeMail") + public String changeMail(@RequestParam("email") String email){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changePwd") + public String changePwd(){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changeAddress") + public String changeAddress(){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changeAdSettings") + public String changeAdSettings(){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changePaymentInfo") + public String changePaymentInfo(){ + + //TODO: implement this + + return "user/settings"; + } + // @GetMapping("/bonuspoints") // public String userBonuspoints() { // return "user/bonuspoints"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index c2c1c3a..e9d9e68 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -11,7 +11,7 @@ import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.shop.ArticleRepository; -import org.hso.ecommerce.repos.shop.CustomerOderRepository; +import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.user.UserRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; @@ -45,7 +45,7 @@ public class ShopCheckoutController { private final WarehouseBookingRepository warehouseBookingRepository = null; @Autowired - private final CustomerOderRepository customerOderRepository = null; + private final CustomerOrderRepository customerOderRepository = null; @Autowired private final WarehouseBookingPositionSlotEntryRepository wbeseRepo = null; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java index 1b20bb2..d9410f7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java @@ -4,6 +4,7 @@ import org.hso.ecommerce.entities.user.User; import javax.persistence.*; import javax.validation.constraints.NotNull; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @@ -43,4 +44,16 @@ public class CustomerOrder { public int totalNetCent; public int totalGrossCent; public int totalVatCent; + + public String formatInDeliverySince(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(inDeliverySince); + } + + public String formatCreated(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(created); + } + + public String formatDeliveredAt(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt); + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java index dbe53f7..2569158 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java @@ -19,4 +19,8 @@ public class CustomerOrderPosition { public int pricePerUnit; public int quantity; + + public int getSumPrice(){ + return article.getPriceGross() * quantity; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index c36b629..40196c7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -45,6 +45,10 @@ public class User { return id; } + public void setEmail(String email) { + this.email = email; + } + public boolean validatePassword(String password) { return BCrypt.checkpw(password, passwordHash); } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java deleted file mode 100644 index 407f45c..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.hso.ecommerce.repos.shop; - -import org.hso.ecommerce.entities.shop.CustomerOrder; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface CustomerOderRepository extends JpaRepository { - -} - diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java new file mode 100644 index 0000000..da42f26 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java @@ -0,0 +1,17 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CustomerOrderRepository extends JpaRepository { + + @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId") + List getOrdersByUserId(long userId); + +} + diff --git a/prototype/src/main/resources/templates/user/orders/index.html b/prototype/src/main/resources/templates/user/orders/index.html index 5e24e4a..b746f52 100644 --- a/prototype/src/main/resources/templates/user/orders/index.html +++ b/prototype/src/main/resources/templates/user/orders/index.html @@ -21,26 +21,22 @@
-
-

Bestellung vom 27.01.2020

+
+

- + + - + - + -
  • Benachrichtigungen
  • Bestellungen
  • diff --git a/prototype/src/main/resources/templates/fragments/header.html b/prototype/src/main/resources/templates/fragments/header.html index 054c332..349f72c 100644 --- a/prototype/src/main/resources/templates/fragments/header.html +++ b/prototype/src/main/resources/templates/fragments/header.html @@ -18,7 +18,6 @@ - +

    Rechungs- und Lieferinformation

    @@ -91,7 +91,7 @@ - +

    Werbung

    @@ -106,13 +106,13 @@ - +

    Zahlungsinformation

    - +
    From 7686c4cda642a5be72de6c5f8ca0223a58bb1895 Mon Sep 17 00:00:00 2001 From: Tyro Date: Tue, 19 May 2020 15:41:43 +0200 Subject: [PATCH 090/203] Create rudimentary register class --- .../hso/ecommerce/app/RequestController.java | 16 ----- .../controller/RegisterController.java | 58 +++++++++++++++++++ 2 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 36b47ce..2680f28 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -69,22 +69,6 @@ public class RequestController { return "redirect:/"; } - @GetMapping("/register") - public String register() { - return "register"; - } - - @PostMapping("/register") - public String registerPost( - @RequestParam("username") String username, - @RequestParam("password") String password, - @RequestParam("password2") String password2, - @RequestParam("type") String type - ) { - - return "redirect:/"; - } - @GetMapping("/intern/") public String intern() { return "intern/index"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java new file mode 100644 index 0000000..4699161 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -0,0 +1,58 @@ +package org.hso.ecommerce.controller; + +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Optional; + +@Controller +public class RegisterController { + + @Autowired + private final UserRepository userRepository = null; + + @PostMapping("/register") + public String register( + HttpServletRequest request, + HttpServletResponse response, + @RequestParam("username") String username, + @RequestParam("password") String password, + @RequestParam("password2") String password2, + @RequestParam("salutation") String salutation, + @RequestParam("name") String name, + @RequestParam("address") String address, + @RequestParam("type") String type, + @RequestParam("ad") String ad + ) + { + Optional user = userRepository.findByEmail(username); + if (user.isPresent()) { + request.setAttribute("error", "Email Adresse existiert bereits!"); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "register"; + } + + if (!password.equals(password2)){ + request.setAttribute("error", "Passwörter sind nicht gleich"); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "register"; + } + + User newUser = new User(); + + + return "register"; + } + + @GetMapping("/register") + public String register() { + return "register"; + } +} From 9cfb1cd0ad6a23af9a685239ab878dc1abec1d66 Mon Sep 17 00:00:00 2001 From: localhorst Date: Tue, 19 May 2020 16:30:27 +0200 Subject: [PATCH 091/203] add offered article to listed article --- prototype/e-commerce.db.copy | Bin 147456 -> 0 bytes .../intern/InternArticleController.java | 84 ++++++++++++------ .../suppliers/SupplierOfferController.java | 46 ++++++++-- .../repos/shop/ArticleRepository.java | 5 +- .../repos/shop/OffersRepository.java | 4 + .../templates/intern/listedArticles/id.html | 7 +- .../intern/offeredArticles/index.html | 12 ++- 7 files changed, 119 insertions(+), 39 deletions(-) delete mode 100644 prototype/e-commerce.db.copy diff --git a/prototype/e-commerce.db.copy b/prototype/e-commerce.db.copy deleted file mode 100644 index e4c39c9bce811412014123a8071f9c885249b581..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147456 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�K6J1i$MZ{5Q@)@f$6O)(|mj%An8G5yBVyB9t2BF=YuOmLmIBWM4To9#R4 zMjA_{{7)^PVewYQzxj^;gXsKS75VeOt5_E33A4|IDz`q0z0hz*_SS@8ZBJ_h*<^Ox zF4S_bJM%oxSlo?m-8jBiuffm0tuKDZ;oLUukekvP9%rDcMJcv9UymovD_mS&pnpEd zU87fDf6e6M&lJDYdTunz6x=4t>~7GCk=J|jCi_L+b1SKs=IP;0CuOO3v+urGYWPXN zzH6u6mR!1!?CK`0MCiQlu*IHc_XiKUTSN5cXu8T4->S3>>w#`2=HJlYkRwTg_4J=N znbbA4)GC%fvi{ul+Wo@LoA2|y>j>IP>w0NSm8oJ$nCMUO7OyU)yFci?eQ^=X$2)X- z(pk-uRoY7?>AtPCIP+D^x98B)kPS2=t(gmpM4Ge+rz$L=*zVJlg2zi=6jRY^dS|+; zn*4dGCAH}AmBUe|L+ygKdT%xeb+h%)xl6Ag>gd$89SA1n2Qn-Sg)kKM40K->ufP=RvF8Pbl+=`{|j78eV8_aCtnRnSoL@&VJzCa|T^+ zT^Y5)*?u7=V={zl>)!Q@aN~ZJ*@uquwAjOth}QIEY*Bmqhv1V3W<_+}zW?HB#$k?v zUe*F5Upo~;$RooFUBC31z9_kp@oJG2Y;DQ?XlKY8^{ zvX0hC_@Qd&(|OY2&CMcUX-IZ#^OHV4;E?ybxq2Enk_(S{T)G3_- ztvMyDQ$MbrEQM(|d71t?kKujW_rlAGr>|GPwr$*SNz&CeI5Or`jLeIi1^tCEkqUw@HY&y@h9w99P%PD98LΞ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 diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index 0081c88..7a7e7fe 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -5,8 +5,10 @@ import java.util.List; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.Category; +import org.hso.ecommerce.entities.supplier.ArticleOffer; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.hso.ecommerce.repos.shop.CategoryRepository; +import org.hso.ecommerce.repos.shop.OffersRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -26,6 +28,9 @@ public class InternArticleController { @Autowired private final CategoryRepository categoryRepository = null; + @Autowired + private final OffersRepository offersRepository = null; + @GetMapping("/") public String internListedArticles(Model model) { @@ -57,39 +62,65 @@ public class InternArticleController { } @PostMapping("/{id}/saveChanges") - public RedirectView saveChanges( - @PathVariable int id, - @RequestParam("title") String title, - @RequestParam("description") String description, - @RequestParam("units-per-slot") String warehouseUnitsPerSlot, - @RequestParam("price_netto") String pricenetto, - @RequestParam("reorderMaxPrice") String reorderMaxPrice, - @RequestParam("autobuy") Boolean shouldReorder, - @RequestParam("categories") String categories) { + public RedirectView saveChanges(@PathVariable(required = true) int id, + @RequestParam(value = "title", required = true) String title, + @RequestParam(value = "description", required = true) String description, + @RequestParam(value = "units-per-slot", required = true) String warehouseUnitsPerSlot, + @RequestParam(value = "price_netto", required = true) String pricenetto, + @RequestParam(value = "reorderMaxPrice", required = true) String reorderMaxPrice, + @RequestParam(value = "autobuy", required = true) Boolean shouldReorder, + @RequestParam(value = "categories", required = true) String categories) { - Article oldArticle = articleRepository.findArticleById(id); + Article tmpArticle = articleRepository.findArticleById(id); // get the old article // TODO img String[] separatedCategories = categories.split("\n"); - oldArticle.categories.clear(); + tmpArticle.categories.clear(); - // loop through all categories strings and create a new category if a new one; also adds the categorys to the article + // loop through all categories strings and create a new category if a new one; + // also adds the categorys to the article for (String category : separatedCategories) { - oldArticle.categories.add(categoryRepository.findCategoryByName(category.trim()) + tmpArticle.categories.add(categoryRepository.findCategoryByName(category.trim()) .orElseGet(() -> new Category(category.trim()))); } - oldArticle.shouldReorder = shouldReorder; - oldArticle.reorderMaxPrice = (int) (Float.parseFloat(reorderMaxPrice) * 100); - oldArticle.shopPricePerUnitNetCent = (int) (Float.parseFloat(pricenetto) * 100); - oldArticle.warehouseUnitsPerSlot = Integer.parseInt(warehouseUnitsPerSlot); - oldArticle.title = title; - oldArticle.description = description; + tmpArticle.shouldReorder = shouldReorder; + tmpArticle.reorderMaxPrice = (int) (Float.parseFloat(reorderMaxPrice) * 100); + tmpArticle.shopPricePerUnitNetCent = (int) (Float.parseFloat(pricenetto) * 100); + tmpArticle.warehouseUnitsPerSlot = Integer.parseInt(warehouseUnitsPerSlot); + tmpArticle.title = title; + tmpArticle.description = description; - articleRepository.save(oldArticle); // save updated article + articleRepository.save(tmpArticle); // save updated article return new RedirectView("../"); // return to overview page + + } + + @PostMapping("/addArticle/{id}") + public RedirectView addArticle(@PathVariable(required = true) String id) { + // article is not already listed, create new one + int offeredArticleID = Integer.parseInt(id); + + Article tmpArticle = new Article(); + + ArticleOffer offeredArticle = offersRepository.findOfferedArticleById(offeredArticleID); + + // set default values + tmpArticle.description = ""; + tmpArticle.reorderMaxPrice = 0; + tmpArticle.shopPricePerUnitNetCent = offeredArticle.pricePerUnitNet; + tmpArticle.shouldReorder = false; + tmpArticle.title = offeredArticle.title; + tmpArticle.warehouseUnitsPerSlot = 1; + tmpArticle.image = articleRepository.findAll().get(0).image; // TODO set any static default image + tmpArticle.related = offeredArticle; + + articleRepository.save(tmpArticle); // save new article + + // return to edit article page + return new RedirectView("../" + articleRepository.findArticleIDByRelatedID(offeredArticleID).get()); } public static class UImodelArticles { @@ -115,13 +146,13 @@ public class InternArticleController { this.title = article.title; this.price_netto = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); - + StringBuilder result = new StringBuilder(); for (Category temp : article.categories) { result.append(temp.name + " "); } - this.categorie = result.toString(); + this.categorie = result.toString(); this.stock = stock; this.offer_id = article.related.id; @@ -143,8 +174,7 @@ public class InternArticleController { public boolean shouldReorder; public String warehouseUnitsPerSlot; public String description; - - + public String getImgPath() { return imgPath; } @@ -246,15 +276,15 @@ public class InternArticleController { this.title = article.title; this.price_netto = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); - + StringBuilder result = new StringBuilder(); for (Category temp : article.categories) { result.append(temp.name); result.append("\n"); } - this.categorie = result.toString(); - + this.categorie = result.toString(); + this.stock = stock; this.offer_id = article.related.id; this.id = article.id; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java index 685f8f8..3ef21bf 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java @@ -2,17 +2,16 @@ package org.hso.ecommerce.controller.intern.suppliers; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import org.hso.ecommerce.entities.supplier.ArticleOffer; -import org.hso.ecommerce.repos.shop.CategoryRepository; +import org.hso.ecommerce.repos.shop.ArticleRepository; import org.hso.ecommerce.repos.shop.OffersRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; @Controller @RequestMapping("/intern/") @@ -21,6 +20,9 @@ public class SupplierOfferController { @Autowired private final OffersRepository offersRepository = null; + @Autowired + private final ArticleRepository articleRepository = null; + @GetMapping("supplierOffers") public String internListedArticles(Model model) { @@ -28,7 +30,7 @@ public class SupplierOfferController { for (ArticleOffer article : offersRepository.findAll()) { UImodelOfferedArticle tmp = new UImodelOfferedArticle(); - tmp.addData(article,"supplierName01", 4884, 5); + tmp.addData(article, "supplierName01", 4884, articleRepository.findArticleIDByRelatedID(article.id)); //TODO display supplier name with link totals.add(tmp); } @@ -38,6 +40,7 @@ public class SupplierOfferController { public class UImodelOfferedArticle { + long offer_id; String title; String manufacturer; String articlenumber; @@ -46,6 +49,23 @@ public class SupplierOfferController { String price; String ads; int listedArticleId; + boolean offerIsListed; //true --> offered article is listed + + public long getOffer_id() { + return offer_id; + } + + public void setOffer_id(long offer_id) { + this.offer_id = offer_id; + } + + public boolean isOfferIsListed() { + return offerIsListed; + } + + public void setOfferIsListed(boolean offerIsListed) { + this.offerIsListed = offerIsListed; + } public String getTitle() { return title; @@ -111,18 +131,26 @@ public class SupplierOfferController { this.listedArticleId = listedArticleId; } - public void addData(ArticleOffer article, String supplierName, int supplierId, int listedArticleId) { + public void addData(ArticleOffer article, String supplierName, int supplierId, + Optional listedArticleId) { + this.offer_id = article.id; this.title = article.title; this.manufacturer = article.manufacturer; this.articlenumber = article.articleNumber; this.supplierName = supplierName; this.supplierId = supplierId; this.price = String.format("%.2f", ((float) article.pricePerUnitNet / 100)); - this.ads = (article.shouldBeAdvertised) ? "Ja" : "Nein"; - this.listedArticleId = listedArticleId; + this.ads = (article.shouldBeAdvertised) ? "Ja" : "Nein"; + + if (listedArticleId.isPresent()) { + // this offer is listed --> show link + this.listedArticleId = listedArticleId.get(); + offerIsListed = true; + } else { + // this offer is not listed + offerIsListed = false; + } } - } - } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 6f79487..6ac9e1e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -7,6 +7,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface ArticleRepository extends JpaRepository { @@ -26,5 +27,7 @@ public interface ArticleRepository extends JpaRepository { @Query("SELECT a FROM CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a WHERE c.id = :customerId ORDER BY co.id DESC") List
    getOrderedArticles(long customerId); - + @Query(value = "SELECT a.id FROM articles a WHERE a.related_id = :relatedId", nativeQuery = true) + Optional findArticleIDByRelatedID(@Param("relatedId") long relatedId); + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java index b1d6f0a..20a3f90 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java @@ -5,6 +5,7 @@ import java.util.List; import org.hso.ecommerce.entities.supplier.ArticleOffer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -12,5 +13,8 @@ public interface OffersRepository extends JpaRepository { @Query("SELECT a FROM ArticleOffer a") List findAll(); + + @Query("SELECT a FROM ArticleOffer a WHERE a.id = :offeredarticleId") + ArticleOffer findOfferedArticleById(@Param("offeredarticleId") long offeredarticleId); } diff --git a/prototype/src/main/resources/templates/intern/listedArticles/id.html b/prototype/src/main/resources/templates/intern/listedArticles/id.html index b2e878a..05996d2 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/id.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/id.html @@ -28,7 +28,12 @@

    - + + + + + +

    diff --git a/prototype/src/main/resources/templates/intern/offeredArticles/index.html b/prototype/src/main/resources/templates/intern/offeredArticles/index.html index cfa6913..7a18ccc 100644 --- a/prototype/src/main/resources/templates/intern/offeredArticles/index.html +++ b/prototype/src/main/resources/templates/intern/offeredArticles/index.html @@ -47,7 +47,17 @@ - +
    LieferstatusUnterwegs
    Vorraussichtliche Ankunft: 29.01.2020
    Unterwegs
    Vorraussichtliche Ankunft: TODO TODO TODO TODO
    Angekommen
    Ankunft:
    SendeverfolgungsnummerXE51451436DE
    - Hans Maier
    - Hauptstraße 12
    - 74880 Musterstadt
    - Deutschland
    -
    Details + + +
    + + + +
    +
    From da78ab49900d524d1e7bb5bbce022fb4bdc1f685 Mon Sep 17 00:00:00 2001 From: Tyro Date: Tue, 19 May 2020 18:47:15 +0200 Subject: [PATCH 092/203] register working --- .../controller/RegisterController.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index 4699161..e38a583 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -1,5 +1,7 @@ package org.hso.ecommerce.controller; +import org.hso.ecommerce.entities.booking.PaymentMethod; +import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -45,10 +47,29 @@ public class RegisterController { return "register"; } + //set values for new user User newUser = new User(); + newUser.email = username; + newUser.setPassword(password); + newUser.email = username; + if (type.equals("bus")) + newUser.isEmployee = true; + else + newUser.isEmployee = false; + newUser.isActive = true; + newUser.created = new java.sql.Timestamp(System.currentTimeMillis()); + Address newAddress = new Address(); + newAddress.name = name; + newAddress.addressString = address; + newUser.defaultDeliveryAddress = newAddress; - return "register"; + PaymentMethod defaultPaymentMethod = PaymentMethod.fromCreditCarNumber("123456"); + newUser.defaultPayment = defaultPaymentMethod; + + userRepository.save(newUser); // save newUser + + return "login"; } @GetMapping("/register") From 9a24fa306ded8b8cfbc1ffc580550e3e17d7bd73 Mon Sep 17 00:00:00 2001 From: Philipp Schweizer Date: Tue, 19 May 2020 20:37:26 +0200 Subject: [PATCH 093/203] gitignore push --- delivery/.gitignore | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 delivery/.gitignore diff --git a/delivery/.gitignore b/delivery/.gitignore new file mode 100644 index 0000000..6c01878 --- /dev/null +++ b/delivery/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ From caf5430a0635721bd548af6b9e340ae417e59e80 Mon Sep 17 00:00:00 2001 From: Philipp Schweizer Date: Tue, 19 May 2020 20:38:22 +0200 Subject: [PATCH 094/203] delivery_api finished --- delivery/build.gradle | 30 +++ delivery/config/bank.json | 26 +++ delivery/config/hans-and-more.json | 34 ++++ .../gradle/wrapper/gradle-wrapper.properties | 6 + delivery/gradlew | 183 ++++++++++++++++++ delivery/gradlew.bat | 100 ++++++++++ delivery/settings.gradle | 1 + .../java/org/hso/ecommerce/supplier/App.java | 11 ++ .../ecommerce/supplier/RequestController.java | 33 ++++ .../hso/ecommerce/supplier/data/Delivery.java | 69 +++++++ .../supplier/data/DeliveryManager.java | 34 ++++ .../src/main/resources/application.properties | 2 + prototype/src/main/resources/test.db | Bin 0 -> 16384 bytes 13 files changed, 529 insertions(+) create mode 100644 delivery/build.gradle create mode 100644 delivery/config/bank.json create mode 100644 delivery/config/hans-and-more.json create mode 100644 delivery/gradle/wrapper/gradle-wrapper.properties create mode 100644 delivery/gradlew create mode 100644 delivery/gradlew.bat create mode 100644 delivery/settings.gradle create mode 100644 delivery/src/main/java/org/hso/ecommerce/supplier/App.java create mode 100644 delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java create mode 100644 delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java create mode 100644 delivery/src/main/java/org/hso/ecommerce/supplier/data/DeliveryManager.java create mode 100644 delivery/src/main/resources/application.properties create mode 100644 prototype/src/main/resources/test.db diff --git a/delivery/build.gradle b/delivery/build.gradle new file mode 100644 index 0000000..4b72078 --- /dev/null +++ b/delivery/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'org.springframework.boot' version '2.2.7.RELEASE' + id 'io.spring.dependency-management' version '1.0.9.RELEASE' + id 'java' +} + +group = 'org.hso.ecommerce' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +apply plugin: 'java' +apply plugin: 'idea' +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation('org.springframework.boot:spring-boot-starter-test') { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } + compile 'org.json:json:20190722' +} + +test { + useJUnitPlatform() +} diff --git a/delivery/config/bank.json b/delivery/config/bank.json new file mode 100644 index 0000000..14cf837 --- /dev/null +++ b/delivery/config/bank.json @@ -0,0 +1,26 @@ +{ + "id" : "bank", + "name" : "Bank of Cheese", + "discount" : { + "minimumDailySalesVolumeNetCent": 100, + "percentDiscount": 1 + }, + "articles": [ + { + "title": "Big Mac", + "manufacturer": "Mc Donalds", + "articleNumber": "0x1 BIGMAC", + "vatPercent": 7, + "pricePerUnitNet": 700, + "shouldBeAdvertised": true + }, + { + "title": "500£ Schein", + "manufacturer": "Bank", + "articleNumber": "500", + "vatPercent": 0, + "pricePerUnitNet": 50000, + "shouldBeAdvertised": false + } + ] +} \ No newline at end of file diff --git a/delivery/config/hans-and-more.json b/delivery/config/hans-and-more.json new file mode 100644 index 0000000..b925ddf --- /dev/null +++ b/delivery/config/hans-and-more.json @@ -0,0 +1,34 @@ +{ + "id" : "hans", + "name" : "Hans and more", + "discount" : { + "minimumDailySalesVolumeNetCent": 100000, + "percentDiscount": 2 + }, + "articles": [ + { + "title": "Big Mac", + "manufacturer": "Mc Donalds", + "articleNumber": "0x1 BIGMAC", + "vatPercent": 7, + "pricePerUnitNet": 700, + "shouldBeAdvertised": true + }, + { + "title": "Pommes", + "manufacturer": "Mc Donalds", + "articleNumber": "0x1 POmes", + "vatPercent": 7, + "pricePerUnitNet": 100, + "shouldBeAdvertised": false + }, + { + "title": "Milchshake Premium 19%", + "manufacturer": "Mc Donalds", + "articleNumber": "0x2", + "vatPercent": 19, + "pricePerUnitNet": 50, + "shouldBeAdvertised": true + } + ] +} \ No newline at end of file diff --git a/delivery/gradle/wrapper/gradle-wrapper.properties b/delivery/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..81f92f8 --- /dev/null +++ b/delivery/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 19 15:50:06 CEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/delivery/gradlew b/delivery/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/delivery/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/delivery/gradlew.bat b/delivery/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/delivery/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/delivery/settings.gradle b/delivery/settings.gradle new file mode 100644 index 0000000..9a7e6c7 --- /dev/null +++ b/delivery/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'supplier' diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/App.java b/delivery/src/main/java/org/hso/ecommerce/supplier/App.java new file mode 100644 index 0000000..bb8b42d --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/App.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.supplier; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java new file mode 100644 index 0000000..93c4050 --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java @@ -0,0 +1,33 @@ +package org.hso.ecommerce.supplier; + +import com.fasterxml.jackson.core.JsonFactoryBuilder; +import netscape.javascript.JSObject; +import org.hso.ecommerce.supplier.data.Delivery; +import org.hso.ecommerce.supplier.data.DeliveryManager; +import org.json.JSONObject; +import org.springframework.web.bind.annotation.*; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@RestController +public class RequestController { + + + + @PostMapping("/newDelivery") + public String supplier(HttpServletResponse response, HttpServletRequest request, @RequestBody Delivery delivery) { + DeliveryManager.getInstance().add(delivery); + + return delivery.getUuid().toString(); + } + + @GetMapping("/status") + public String searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) { + + Delivery delivery = DeliveryManager.getInstance().getDeliveryByeID(trackingID); + + return new JSONObject().put("status",delivery.getStatus()).put("estimatedArrival",delivery.getEstimatedArrival()).toString(); + } +} diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java new file mode 100644 index 0000000..0fdf75d --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java @@ -0,0 +1,69 @@ +package org.hso.ecommerce.supplier.data; + + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.UUID; + +public class Delivery { + + private String[] states = {"Bestellung eingegangen","Bestellung auf dem Weg","Lieferung erfolgreich"}; + private double[] timeBorder = {4,24}; + + private String name; + private String address; + private String estimatedArrival; + private Date creationTime; + private UUID uuid; + + public Delivery(String name, String address) + { + this.name = name; + this.address = address; + this.uuid = UUID.randomUUID(); + this.creationTime = new Date(); + SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); + this.estimatedArrival = formatter.format(addDays((Date)this.creationTime.clone(),1)); + } + + + public String getStatus() + { + Date now = new Date(); + Long timeNow = now.getTime(); + Long creationTime = this.creationTime.getTime(); + + Long diff = timeNow - creationTime; + double hour = (((diff / 1000) / 3600)); + + for (int i = 0; i < timeBorder.length; i++) { + + if(hour < timeBorder[i]) + return states[i]; + } + + return states[timeBorder.length]; + } + + public String getEstimatedArrival() { + + return estimatedArrival; + } + + private Date addDays(Date date, int days) + { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.DATE, days); + return cal.getTime(); + } + + public UUID getUuid() { + return uuid; + } + + public String getName() { + return name; + } +} diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/DeliveryManager.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/DeliveryManager.java new file mode 100644 index 0000000..bd22a56 --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/DeliveryManager.java @@ -0,0 +1,34 @@ +package org.hso.ecommerce.supplier.data; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class DeliveryManager { + + private List deliveryList; + private static DeliveryManager deliveryManager; + + private DeliveryManager() + { + deliveryList = new ArrayList<>(); + } + + public static DeliveryManager getInstance () { + + if (DeliveryManager.deliveryManager == null) { + DeliveryManager.deliveryManager = new DeliveryManager(); + } + return DeliveryManager.deliveryManager; + } + + public boolean add(Delivery delivery) + { + return deliveryList.add(delivery); + } + + public Delivery getDeliveryByeID(String uuid) + { + return deliveryList.parallelStream().filter(d -> d.getUuid().equals(UUID.fromString(uuid))).findAny().get(); + } +} diff --git a/delivery/src/main/resources/application.properties b/delivery/src/main/resources/application.properties new file mode 100644 index 0000000..c0d1914 --- /dev/null +++ b/delivery/src/main/resources/application.properties @@ -0,0 +1,2 @@ +server.address=::1 +server.port=8081 diff --git a/prototype/src/main/resources/test.db b/prototype/src/main/resources/test.db new file mode 100644 index 0000000000000000000000000000000000000000..bb25c444e4f6381c4dddd9231d922b0e43d3aa28 GIT binary patch literal 16384 zcmeI%Jx{_w7zgmXmf%Z*Wg`rRXEA|#5P$##AOHafKmY;|fB*y_0D*rNICHCwW|QAqF^Z%L10!ea zx^j8#_lG_W`xgVB);Ve?a$$qHh-jqlRAM_%M|`KzYVqghYJzcQY#K{#%8v76nlepo z6@Jb^Nopy{qNqy~rHxHQENL$E_+IEPMdEj6DvWtdb@+8nW~Bak*Ho)m=p{YKW$~m= zr{`1~ev31egmPhKQNE)OL}t^~I4EB$=#|}KZ@*h$*NUoMR0s$_00Izz00bZa0SG_< z0uX=z1h!RRzgQUd|J(X`u`UQe00Izz00bZa0SG_<0uX?}N}%}v&%Jk+9|#CQ00Izz X00bZa0SG_<0uX=z1h!DX;m+O%yytE! literal 0 HcmV?d00001 From 8ede86fdce839ad0533fad9b05e011b0044a44c6 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 20 May 2020 12:08:57 +0200 Subject: [PATCH 095/203] add Errorhandling to UpdateSettingsAction --- .../action/user/UpdateUserSettingsAction.java | 61 +++++++++++-------- .../ecommerce/controller/UserController.java | 55 +++++++++++------ 2 files changed, 74 insertions(+), 42 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 57cbfce..fe4805a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.action.user; import com.sun.xml.bind.v2.TODO; +import org.hibernate.sql.Update; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -15,49 +16,59 @@ public class UpdateUserSettingsAction { this.repository = repository; } - public void updateEmail(String newMail){ - if(newMail.equals("")){ - //TODO: Errorhandling + public UpdateResult updateEmail(String newMail){ + UpdateResult result = new UpdateResult(false); + if(!newMail.contains("@")){ + result.errorString = "Ändern der Email-Addresse nicht möglich. Bitte versuchen Sie es erneut."; }else{ this.user.email = newMail; - this.repository.save(this.user); //TODO: Errorhandling + this.repository.save(this.user); + result.updated = true; } + return result; } - public void updatePassword(String oldPassword, String password1, String password2){ + public UpdateResult updatePassword(String oldPassword, String password1, String password2){ + UpdateResult result = new UpdateResult(false); if(this.user.validatePassword(oldPassword)) { if(password1.equals(password2)){ this.user.setPassword(password1); this.repository.save(this.user); + result.updated = true; }else{ - //TODO Errorhandling + result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; } }else{ - //TODO: Errorhandling + result.errorString = "Das eingegebene alte Passwort stimmt nicht mit dem momentan gespeicherten Passwort überein. Bitte versuchen Sie es erneut."; } + return result; } - public void updateShippingInfo(String salutation, String name, String address){ - if(salutation.equals("") || name.equals("") || address.equals("")){ - //TODO: Errorhandling - }else{ - this.user.salutation = salutation; - this.user.name = name; - this.user.defaultDeliveryAddress.addressString = address; + public UpdateResult updateShippingInfo(String salutation, String name, String address){ + this.user.salutation = salutation; + this.user.name = name; + this.user.defaultDeliveryAddress.addressString = address; + this.repository.save(this.user); + return new UpdateResult(true); + } + + public UpdateResult updateAdvertisementFlag(boolean advertisementFlag){ + this.user.isAdvertisementActivated = advertisementFlag; + this.repository.save(this.user); + return new UpdateResult(true); + } + + public UpdateResult updatePaymentInfo(String creditCardNumber){ + UpdateResult result = new UpdateResult(false); + if(creditCardNumber.matches("[0-9]+")){ + this.user.defaultPayment.creditCardNumber = creditCardNumber; this.repository.save(this.user); + result.updated = true; + }else{ + result.errorString = "Kreditkartennummer enthält Buchstaben. Bitte versuchen Sie es erneut."; } - } - - public void updateAdvertisementFlag(boolean advertisementFlag){ - this.user.isAdvertisementActivated = advertisementFlag; //TODO: Errodhandling - this.repository.save(this.user); - } - - public void updatePaymentInfo(String creditCardNumber){ - //TODO: Errorhandling - this.user.defaultPayment.creditCardNumber = creditCardNumber; - this.repository.save(this.user); + return result; } public class UpdateResult{ diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 94d7618..803dbf9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.List; @@ -46,9 +47,9 @@ public class UserController { @GetMapping("/orders/") public String userOrdeers(HttpSession session, - Model model) { + Model model + ) { List orders = customerOrderRepository.getOrdersByUserId((long) session.getAttribute("userId")); - model.addAttribute("orders", orders); return "user/orders/index"; @@ -56,12 +57,16 @@ public class UserController { @PostMapping("/settings/changeMail") public String changeMail(HttpSession session, - @RequestParam("email") String email - ){ + @RequestParam("email") String email, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updateEmail(email); + UpdateUserSettingsAction.UpdateResult result = cusa.updateEmail(email); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @@ -70,12 +75,16 @@ public class UserController { public String changePwd(HttpSession session, @RequestParam("old-password") String oldPassword, @RequestParam("password1") String password1, - @RequestParam("password2") String password2 - ){ + @RequestParam("password2") String password2, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updatePassword(oldPassword, password1, password2); + UpdateUserSettingsAction.UpdateResult result = cusa.updatePassword(oldPassword, password1, password2); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @@ -84,36 +93,48 @@ public class UserController { public String changeAddress(HttpSession session, @RequestParam("salutation") String salutation, @RequestParam("name") String name, - @RequestParam("address") String address - ){ + @RequestParam("address") String address, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updateShippingInfo(salutation, name, address); + UpdateUserSettingsAction.UpdateResult result = cusa.updateShippingInfo(salutation, name, address); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @PostMapping("/settings/changeAdSettings") public String changeAdSettings(HttpSession session, - @RequestParam("ad") String ad - ){ + @RequestParam("ad") String ad, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updateAdvertisementFlag(ad.equals("y")); + UpdateUserSettingsAction.UpdateResult result = cusa.updateAdvertisementFlag(ad.equals("y")); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @PostMapping("/settings/changePaymentInfo") public String changePaymentInfo(HttpSession session, - @RequestParam("creditCardNumber") String creditCardNumber - ){ + @RequestParam("creditCardNumber") String creditCardNumber, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updatePaymentInfo(creditCardNumber); + UpdateUserSettingsAction.UpdateResult result = cusa.updatePaymentInfo(creditCardNumber); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } From ba570852a72cf44daece8fb2a37933a7bb1f373c Mon Sep 17 00:00:00 2001 From: localhorst Date: Thu, 21 May 2020 10:27:26 +0200 Subject: [PATCH 096/203] =?UTF-8?q?added=20id=C2=B4s=20to=20inputs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/intern/listedArticles/id.html | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/prototype/src/main/resources/templates/intern/listedArticles/id.html b/prototype/src/main/resources/templates/intern/listedArticles/id.html index 05996d2..7050d3c 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/id.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/id.html @@ -24,15 +24,13 @@

    - +

    - - - - + + Details

    @@ -40,7 +38,7 @@

    - +

    @@ -56,7 +54,7 @@

    -  EUR +  EUR

    @@ -72,14 +70,14 @@

    Bitte jede Kategorien in eine eigene Zeile

    -

    - +

    Lagerbestand: @@ -95,7 +93,7 @@

    -

    @@ -108,4 +106,4 @@

    - \ No newline at end of file + From a3efa4092af087572c0e5b06db671cce596a1983 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 23 May 2020 09:40:52 +0200 Subject: [PATCH 097/203] removed old static product images --- .../resources/static/img/no_product_img.jpg | Bin 0 -> 10261 bytes .../src/main/resources/static/img/product-1.jpg | Bin 13288 -> 0 bytes .../src/main/resources/static/img/product-2.jpg | Bin 17951 -> 0 bytes .../src/main/resources/static/img/product-3.jpg | Bin 14913 -> 0 bytes .../src/main/resources/static/img/product-4.jpg | Bin 5682 -> 0 bytes .../src/main/resources/static/img/product-5.jpg | Bin 9289 -> 0 bytes .../src/main/resources/static/img/product-6.jpg | Bin 14439 -> 0 bytes .../src/main/resources/static/img/product-7.jpg | Bin 12305 -> 0 bytes .../src/main/resources/static/img/product-8.jpg | Bin 8928 -> 0 bytes 9 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 prototype/src/main/resources/static/img/no_product_img.jpg delete mode 100644 prototype/src/main/resources/static/img/product-1.jpg delete mode 100644 prototype/src/main/resources/static/img/product-2.jpg delete mode 100644 prototype/src/main/resources/static/img/product-3.jpg delete mode 100644 prototype/src/main/resources/static/img/product-4.jpg delete mode 100644 prototype/src/main/resources/static/img/product-5.jpg delete mode 100644 prototype/src/main/resources/static/img/product-6.jpg delete mode 100644 prototype/src/main/resources/static/img/product-7.jpg delete mode 100644 prototype/src/main/resources/static/img/product-8.jpg diff --git a/prototype/src/main/resources/static/img/no_product_img.jpg b/prototype/src/main/resources/static/img/no_product_img.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f898b51fd31f83c3457a480dc88abd647cdf0368 GIT binary patch literal 10261 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 SwceM70g6f$Jd-2jk^cgCe6ub9 literal 0 HcmV?d00001 diff --git a/prototype/src/main/resources/static/img/product-1.jpg b/prototype/src/main/resources/static/img/product-1.jpg deleted file mode 100644 index 8ea7598d85d80b9d7c26f17db2b13b81c3675057..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13288 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 diff --git a/prototype/src/main/resources/static/img/product-2.jpg b/prototype/src/main/resources/static/img/product-2.jpg deleted file mode 100644 index 427b2f0ed62eaff94147f8500356d77bc7163902..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17951 zcmb4qby!=^+F*jaLyJSvgaU=)?h=Z-mjcD1SSb!gi%WpwL4&(%v0^O*2yVq4ihH~K z?)~=Lz2E+|lXLP+&dj_r&z#ruckb^m01-q%Spfh9q5x=~K7hZA09gRaKMnZL7wXf6 zih+ju)G*P}(J-(uv9Yi)v9Pdl@Nuzm@NlrOa0zko@CgVA39xaAh=~Y@pZWy<00BNN z{AU#!8YTfY7WUKA|IhaK8-N%Kumo5_1rh^Lh=HiYz`y+fIsgEOiVg$<{!hce#0H|E zp<|)qJdNuUJ&mKHprN9nV_@RoVn0ns`KM0|z{Db9Ip zXxLA~a!)h@QJ;9m!o)yBNB@Vqe-IO+Gh&eNypYz!B&E=?a7|nzBM(kWo|t^btM$&U zhe=S|^0lseUaw5w)bG>33jjP+;1jE;!~jXaZfEF%J;8y=GZWv>)e^~c$@ryMZa9mS z+eW>ut&>$jY~uVIYD82U7YslE2JnqkS= ztc~Gc7nU4g!($PqUS*E^3s{Z`yzw0Ir~So!n|w2PktI3l!rpyWbZv9juXgE}()rej z1?9f(c(BUBx$x#K=xkvaER!0R=M+SiJOq=b#H95Nu02)6F|?!d=alJfFNL^JuU)DZ zXMA7en-LIxhIJZt)mWxC#ZT=CY2?qYYvaqXH?%v>@b2TM9%%G%s&{ubE7XCH^AE}B zB=RdsPzBqr(Uj7PpRbCCH~nE*yEjE&5Fv$j9D3&p2>9oGRq0<6(*ELn6K3{(oj5){ z4ZJnBckVeFKszVewY2OI7f4PcW5mn8UawrccVWn9t5ZzE_Cu$|MdHA3tSuFpUz|jo z6!iMD-{s-)M&I(f$jS!23s)5(=kiX0(Y0ZoNty#iW%Zjh0uIu0@@GZ<#Q06gOnr6| zuUuJNgy!f-#+}XV5@QE_oxMKBw=UxIUN@KULnU#XWU>W4W&)Kp!@eCJ6P)8>Y}DDwIy)~m<+;jazxtl17}cn46h7+QR?6{F)`{&- z2WHW$Qy&`UHI4H6eM^3`S8ufq*IW%Zm3Xa(vAY9QC90W#Lu0FvLWL*GPJ%2#FHJaS z2r?N{gccKvL6EskKW8h`(I*l3F^CuYFJMYaU&=73fs+HMERMyLM+NN8Rtt#MjTs?> zb6#iR*C!{VTdi^KJdni8?d)@4-xa#8#r}MA&TU6;Vz2Lz_UGoPnV`uYEu(N4f9&h( zmyx9Ntf99;iqjAxT2IBHw;LwZX60JzouX8xW+(T4f;MGh$@4~bRMsbCt4=s}y`o+( zg&Wrww?1EO;rWSwS({O~V0w>nvXtbdI*fs0Ke2lhAWQm9ICl0tsyA4{yEZZIt&f9M zTyAmOUx561-Ff!wu+dyX(gyIU3ss+r&j|ul&ez&Xvzl`rIQYQfbmy`9d`aFwl+dAg zV&y)>n+vD2R%x*;nQ!?0=S6kV7myrndx&fS4*f)83=KEilsQXRu1ZqFap8I?U(@NB z=+Aro^@D5o;ze7XJUy>k=xmmovb;RsD#v2CxDVwsiGBl5HQLJ9(FNS5UzJ*5uVjkR zger)G>ne8#4B9f`0<=R5^1Z!8XLbO$`F9)@n9VrBS!k3kwr;5OXpGAdGv#|m%Qzph zMR+TU)YMc`MH^opXPS!mHosY!7JAq8;#9hg%-URQH&dm#W<%87l>GUwkCgXJQg=b! zq8+w&XgT2R#)MoUt=*nZftMc5k#=z!U#>~xgs*B>1f4k&@v^+!T6A%i1p+nJ+=_O{ zP;Hu2w!XP1abn~ur;gaK8Z?JF)*Nqf`){d8HnK{N;%~ z#I{lC0#^$yGuHKv_o&Id4p3yAx^(7gp0=Go5YX^Aum(egx1RGaYYX@K3pbfP4)VtL z7NK|%`@U{I=Q3*W7Nm`7&p7a;BRseF<_(`8c?C3YgWPs{UiT@(IWqW6a_z+?_nOKf$0rC4tFkA}_{tL}F-F zU#rIMyH|1~i1>s4%klhlR6*pGhvo?5=RmLy}oa(MrI$&7dV zC_A8tD;`oDplNF4Ar()ax=zl-ltmU(oj?X3?ZQ(?kvDT&EX-gpDT_2--0_6+)9y!p zi9msl^&<-#I-ym1JB}qbM{Dz%=MoUd2?Pno|TMy>^)~n%2{0UNgFeT4d}Q*smJcaMh+i z+U*DQ0w!kc`k4I@QClXzOF3~INI)tt7 zIt{rWYchSNR#W}0dDasoKOLG8ld=3#w%~aG=Y|Qg5r1jRQ$` zalVt?M+`buk^S6DNrTHHzi^BDH!Y26j22Cl!FDbC@w-KQdvdugv_26nXVFaHakviM z70rh)j%H2EeM}ZrqBFIc-S#t;w0kL0Z`?(!GIYX2R4Zpl%sDI7nm$+QpoN^ARg-iT z6!oIh>1Ge`+3TGx%o>4GD+|vvr9HXEGgYELOi?AXTyKwsuPBv~af4Kb2ukaU+)4*! zQc*qosJtvAE{={bUW|2}h>}!llr3b2*{U^cXH@1)U8Za2>3}An-@G+VWJ^3R1julP zw?{ssT2a1E3;U@O(O2oV=la^Lh%2&rQO@2ZqOc{+sLz5Y;q$2>htlTeHrgdBCRu+Z z#N{;J`5xm&4c6#l3XS4NxrrzJSo&ft?S+ZYrvU99Ub_PoXXP*J!jx)FF-84r%n6V; zyeGt>15(-QYi4tcj!4tdq)K%@h8G&yJjLwKA?M23BylHOUMcL8`($c3`48&y-hvg_dF^>a0Fi zCaR?UwJ5jgQ7reCj0^%Mz8~>34Zrx{RZ0P(8AC^{P=^tJ9^G!S?oZksrT62W7#U z&8thXy-TgrZJh$zZ*=es=Jmm}7KR|qURxG9)R4qgLva}$zP|urjE*hkrkQcvg|2=( z#ZWtYKeLKs3r(YWW0@@Gth}F`bsH=(omW?F(2@sLISfrnb& zc^^0hpRZdHaSF)R*jrg$WJCrxUHsW-kZX0D?wY5slP(gHYi3fIKl$fzUZ@kF>p*s` z(*C?}r(P2qbVkOIHysudM}17DwqGRhH9^ zSy(6GYku^f?8@+ktrOMZouw(e4KT;_s7QLRDZPVpRO-aglF`qAls6Xlyc}Rt-mhs! zgx#!$v^0!3+lHVa^ITJhSr4Z7mHQ%oXRf9>yGN<>je8E-)&08szCxzuMmqaH%f<|{ z^@>KHYnxi8*3{g2Il8!rk2Dh8ojL_fIYS^5s#f8$fNWu!k| zPpP#wW5?r_aTKCld%&KiWtXPnTEvdc6~<21U}TgECu|S16z6C2RM>z#*;4hIA*(%? z*Onc~X2c~YTf8uvnse`huCqmCQ@s#Z|A(5*!X!=OEt`(BG8aO^c7LBEv*~Ze`dm@Y z%X&;o(XN^tZ)(+2G!3N#j7$IgAt|b66Y4Rb71>w2R%_1Y@W@JGZC17Dx<2{~xENav zZy7ag?3w|qkrnx3v#EVZ&a$gG>j@;#GB_EdUYeUn9NBglo<{5CuU>`mI=#<1l9kq4i_62lZxMg1S&mrp5cchD9*=%f`}64 z^ay;)O#(!8mIp+{PSaiwTJK$|qPb!)o_bhv@0_f{Pv zU$W~p9F4;vO>3>|^2I`5(8G04JRz=#`~AOweovckG_rbQ0`48rMYKp?1NA_g#wat* zmiMuT)unU8dZ}X$Y3Y@1-L-y%kU^aLRyRpy-N9Dm-2w>qg36ll@&;=}86x6Aw;!-p zar_B4(j`kG#hbFkvHoY>qIGjk@9YRS-W)h;^1IHm>QpyJFeNW=?o3K%TQ9d%aZ!eX0epm?$r>fnY>m#7{ODUyzf zMCA2ZzpSS%WM;u{3JoHG76M2B06be+!9?L*o~vD)vzVRnSrL?uf~o4NpjSM90aZc5 zii0Nz42i&iAfEI43v>@4j4FF#K}6zc(LCW;C2BoH%1GF1ihWudKi%K`c16eiWx%jL zx7xyD)!~{qf%0(44O`6pm<7z0e4N0 z;NUfXosTH|FF>&W@GoHT+4I20n?T-)!-qMvB(3Na=Ia-23V{gZg_qhNm-r#cmwxZ= z+5+9T^=~CV0!X~MOmcS4_Qvn2oE5-! z6NUQ*usqhD#^0MqoTEO0CrQF_enY{~3hl+sv)DIu3{5{e%9AgMUx>3wQ#NuKk1p z#ZA*KImWXpcWG*3S@gC`^2HN@gWq%49{QfhN5ex~f(6FMAKUF7=RT~`Cp^}=M*Kq^ z=polnG66Hf?d``uev+;5f()JEwpa~7|H*!l};BF}R|APMCs89xoXRDon>YP9e5tNMohl-*9N!Gs>j<|H) z&1a(5xj8S^N%?4J{nXsU7eb4I2JuoW_%eHN816zZ_m$8HbDX|!SVoqYd$Q@f{2yukD4n&aGQBEkuNP!H4{ z7(UQBKm>p(W%_@y{0}R<;vT@n0~lVsDZt8S7nNyNijuDFznEtC!3Um+epe*99@zPQ z^lN!hUGA1|xT@zJbHtKUnJ<>71@XL$(iVW3oc&U3m~B+bodx`-d3zXQ&3K2qFS(kw zF(|neM3&n?pHo8n32e7}B$Sf(J&V$5W`-gxUZ=!EBuEdjGs8JrhTh|(yRG3+lSLp~ zqPpt^k~<_D!nz%tFUrV&}!}dn*ru?q`(rd|o0b zCYAR|!5`nf)LxR`Ff$-Ag*^el91LSP_Z4rb zg_)9qs8O37XJ9pTp(gqvReVb^9^1~;@*LRWJ+6ofFBxB3Y^`OsPiJVTETc8=>V~C8R&&LSP8mw)3BgQ5z>MsfJE0&O(i7MIcn> zBp(2#$u5@Hy5hoZlc>u_BqwcB(^Pw7OS!fd$M_qblFAAidTeF%VJRYMEpH;k&XXKw4Tik|! zHp7bwb=!Plxld;iHR{vihV3u!@t!AtXsbhkTcY(qP<8Ppkn=3`$f%cL!8- zF`4%Dv3SJ{Z74{yY!S|I-%j2+eJ)80oY1TKrR9L$GG3x>kMj*rtlQP*io*9Hj&NK` z#8%dY32_^*Y7)FL|MO~@Gqgih-KBiweb#xfClwjO6s|rPNL&%8>-{MbWN^t;3}Zcs z2zY%p>OVme!qQ`T%$tN#xgY(j*r?-&VjE^eE-T5eM=gWKDrt4$96&4xP2RZ3g0s;5JC#W)`HxQ{n1gzmZt? z>epLZH*q-HHpccg?)hf6SJgz=dxC#hjfSaxib=JosWAp(!ea{4$fPwuXv{#S-)pk) zsl_iSOt11`7I=epoOw*Ypg|Zx;eP>+&q&BSJJ*h9=0+7@TOx3Ofs4#IS=>!mz9uL& zac}StVVjbkXkH5Qg&_j}y1s9|rGF33r46cMi8($ z_&RtiaJYs&+r5-y2nQpEC1}FO2kVlT!DzI=@qG)$yypf-F!6MwAa;#?5l;j`QF$x5 z5wSDhihU*DTeQl+Y{!6+c-P;O;X%9m$ZL>{A+cS@ILb5ijFI= zEkQ!agOLA%3dPQ`e0^fcv?KWA+%+CY&QeC!U^e=(2kCYS!LGrf28@hQ1|vxCU&><$ zK{j-WAb+}_&c^V`uKLg}A=JO;BW>+p04{JoEu0!&-}TB`ta-3)kxSY7yPo?UBZuP- z!j`by+^kjj;7NL(^BloL#Jg?BmUU>i`Sz$kj=B!t<*AOPtpiW55djl0Vm-yW@+g%P zntB<{FG%E>M9FeF1EA?n-Wp*vA! zQZS?`_oYOW`Q)6?!leba2I+0&SxB(2JIV>d!V{gip6X2I|kT zJ}D{5#`2K+vdX~M0|X0`%ri!+;w7^`gg8@%Vt*0n*)xK!p+R#h&cv5ud>pl!N^}ok zUvsCsfZ2(PIM&ieX#;pouJvMt~xpsBA zQ1`rHR)%ZTf;0_%*e>+pDGmXlXd0oDA;VIM6t6w;LeQumy+K8MfO%$CvsPP@&)X+HFeRoMqQVo(9-}x?9{{EkkQ!3lAjG-S$tplVUDa+jbA;H z7|8G7my)z;&AX@M39HK|(g|yV1Z_WuNab*bukTn0LDO}@8oV-7?izVtu;`SYfk^T? z$s4N^SAf3lK`zbIHJNk)O;Kv|7^oXvYbMQedFoM^Q%0u==9#7;;f@5WGPPP+G zDj@g6yE%I0-}0v}Y3_0Aqpq#>XFCWybCM3;BQFYH@>`0oCc~;e$gGPg--=?PHm$}P zNXWIwW5+g&7-=CzggKh?`1_!hM(o5dlg-tRUN+S`O{HuQUBmt`BPy z$ky2rj0QQKr)$>@3H|L^utXOneW7tCLNqH=smIygoBd(2(a-~YDI#MfRU+m8rkfI( z6_#hWpCm~)$dH()eE6$=0aJZNAJlHxUiS`h?EvLhDJ^3Bn#PK06<>y|z)Ov2+tG@l z{{Uot|Mt;kP|EP4_TXk0=ZoR_g9rMmG96rs!vnnxEjSjqZM3b7iDpYcwcKq(i0#|s zI#Q)4Fila}!(#0Q)`JdAk?qH874gN?6ph2dI{a06}vY%D(pqM`=|AF7uO=CH%DWMR+A>o#VOXjph_71 z1oxKKvvV%O)KH<7PQw)A5@D`(5zjtOMUF)MoQpv+e@M1gTnX_cIuE^B8hY)fE^Wh8 zcFZ1iCe2Nc^X~Keg+}`@6bFBeN0Q=0eIkEp>$kg_$lQVLOZ|^M2e|GIu_v4{jp(e| zJ~gaLku|SqWBJ^ME&Ag|2#7ZeOFR&G4*Vkg^1Ta)k}t!X9Ok^4l)J(;udiaYD60m1 zs8D7j)=PY=*~*k|p46}{-m|w&4eqtUxd=A+oL?ib+)Wq<1Y(qJVX6w_jJYWDSS^%u zPxOc8P8eJ;NxghtTCW+dm$=rEc>Dd!es1I?JuTMTwvCEmrz=`x?uMGCB`0eRncxwf z6av_1Aetwdm95sN8}g0ppGdg${NrM2a2v7VX1-pohEmIdOhEp)OHm#Z+XGly$CrM#UmA}K^ z6-Dl}Wq1oLljat-lT{Pi_~O(MQ);h=qS(KSakS9Ar5yb(>w03na7`;^Lgf!k zJfi;+2`VESQa5$AP_!F?Hj^V6(WlEFZKUiRkegau;1EJN9YZ>>3`yQd1D@5zF+Z?c zRrA}3rZ6~l(1qyIvv!|wk8obnf(llC8l_~cGM=FqvP1`q1=barOzw$Qw8c|#n|YLj zafXi-)8OiAgHqfUuhptv(dTuATWx^D*V{N|p??7`as1NZ=k&Qc-j1aa6Nn;N9Q8kz zzmXV?Hx5oL=BC{n5{%TH-U2jQe@N$>qg5ttpGf-!*M;B=>jr~u>L2=%BSoS~Cl0PV1N(9D|% z@tJ0$0xrm6zO+5fTXl{}?RAmrc{zq1*}_h$Mq#bzD)Rjesgnu$SZ7L< z_a1NSSip^X?{v!i1QXRFt8d@LSTy*c_y*$63yo`7Sb13`5tG!aM1djFd~Tjw?qc2DJzkl zkFBg$Zqd{>=DJ9+9>E8~z@q~gjfm+ERaxH$jw9>M3umd_hKKmvt6kq+{QV8i@Q}k6 zmVTyXPug!cu^Mk%iPIuPSvX(b27LJo7=1M7lyWe!8#+Vz>?f!_i}@q4!&cjpoO4+5 zwIya<@*P}Cq+L5;<|;aX^WbjPs4Go1ke4aU;x$uw!4D23CnY`Vh;J}VBD%7?Ez@mY z$2UBdZBHj7i?vwROVSihLYDRL;X?q!F8VvLWG3D92251&KLZdlf{m(fX#)g3gCBBX zIJ(lRsEY&K4TOt9u~^cnH+oj$EeR#k@~_qgRf-rcwBtOtC7FK_rcYZFdnh5&6jQC3 zOD~!5Hfn^AJ%Lqe|g<2>J_1E;*}UPtd(% zrU^qK5WS=t;AesAKYJD~>}7r8V?M5FWv>5nw8|5V6@sC2Ev9H>!3=Hw;gi7YlhZaX z%n;{VkrSd3j+=O%fDy){tLV9XdpeY>g^i#x6u6Jp#21=e5>HTPWLwM0jSH$2XxGkD z)8;-8Eiy2o!DP5&5A5y=`j9tPtS78XJb`0J`CHF_RDO&EzNxC0e!F)%gm>RT;QAVFmcNLW2^q(S>gl@WB_qN@< z;W`VWbsMoyN>YdUaVAwSV0})K!2^xRc`J--FBavz>AB8MKnyAC3k(x2pb&`8UR?)z zy~|UPbDvyWJ7sh%$j>et6UDVL)Im%cB$ z^3Go)UW`O)l9dj;kW;Mx5s?`8bzGiNDy%TKtV%cNH3K3OF7vfV+vN*B#!TaP8E6m% zIh5QAmSaMWM?)euR$vJJG@KCm^VKScYAD+{olh}K=-X@@18pQ5TB#gbAzlhS14fXJ zHfL`ktK6^G%bZ@=w|BXHk3J}O`T2uZg}`jU0+NPM3^q4}z#geXe*HoszGDRO)~R|3 zO&kd@*t^=r7bQ%M(^9t0g2w&2isgH0kyi zG<(gQ-qO%}N_|ekret{C%vt|Ivn)=g9AQu9Z71~dM@&;(;0UVZ`=Ep_Vgc82g3MO^ z7+zwc?WG9DI^h|~>qh2V=1swz9q^f2P%@S-!LrZ81xj{V#I|B*e6y7D)qe)|Hq3;N z>WzOz$t&Z18Y$2$c?hnKiE}HzAMFV`tcE|Q{=`51<74&?kn#hb25Npqquk|c`$l9dTu)k zMG`*2=mE0%aaMF`%H84|4JEd_@=b$VdQ%{GjIv4|XBV^$nuXSq03-ZH3n-0XPa+gF z1yqEv|MGnBK*>wD`9_6e{I77NEsp0I70RT~E{^-tl?`;$&xy!GVEgVPL;Mbpe%c^w z7^nBf`i+Lh!}XF#YEL0Gd8bsUTL*5HcSZ14&wI1Z=K~d5+oB|vVA|9$x7@GdgC$I} zhE^e*1iEU$?r%P(hnJQ)aS2TYmGMp|kv;oEc;T%l`cYiO-h;jS3shOcFssS7F4i;3_T*1cL}(rWyXeCn^HkK#>nSe$hSW8drG zrHL_DCWYRWD%PMLA{iQ*xv8{YS*V%{(8?owOss49>z-^d%i1kQslxkVBPA^zUJ(*} z6bgSetCS%EPKiAU9HOcK*Infv%x!)g+n;JxKP-oOLAS?yjDwh$QoDrx*?^2h_6Tof zlZcs$PtTzJNGh83*#wZ+H{#rzpe=oDa8q5 zt>mt-l*NIbPJ=MD3OLlr?LfN-DdildwsvV%lOCA_lMvP99$Y>Zy1_(WN0{3ZOrCU3 z$Th4 zJp+T8BtJZOevxCQ%8ER?u`_D>NO^{!4zH4L*w*`;&xaW%vy;r(CC8TFlMQHUfGOza zFUx=bT-3Hc`DD2!5MYR71T86jQ$G5Qz`m7z((tTHljPbfCW32JmouJE2O%PH}}9dhtIV;msEBkusM~^rISaZg`hQ+K2AJEv+^&{23T_%;R_W12>t2(0sdWc zS26H&QsAF`vcW2?f~LlBl6j^d98^GY)qJ(G=_wK>lYaU!p@0I*X~ogUKSsdc@0j@Z zX>5tlf7QfEse96GDXuHCNjVgvD7;d>wZ(rMWB$o)v+%a?`j-ArRo^IE4dFQ9$J;Vj zPBm}NFdpT|!)C1K5Z%!=0g20tTU%_NA}MA#TotgbRoZ{%+Z`U{ZhLW-Mm$v<B>XGmv*}Og4(BXlR#kZ#&_=O`2=X7z6a_zulDCzab93rh z)ohj_eC=kd={(4!Giny>OU>QH>%FbMIV1;yf z;Q(IY&oguvx2py!^K0O7yVOU|%{1qA*3e?VSoX_9vQu z)Xx_s?;7cCOexn0vIk1_Z{-h1%kPQ_=87buB;u2p z<9a8D$J>V#d(`UgN3-lFm5_&AO{=uG$OK6>(Yf9~AR(hbWhNQ?DN>&>Ml1Rk1cYXH zMdD`me8nhqj_knWgjd>U(-;tN%8=uY^o6b^Flec|D)F~|VE;ams&1l-E=xMO2z4zR zaR_5ol;jp0+G8ss-%jrc2KP?6i0HUA^X`?z$8w}L<)V4j&n!rrc!$p?HNL#MQ-bFS znw<`3YS|e~L)D=OQ1-XX(3)J3)bM-|_1}DGzz{2XOu6NtZL?-|B{yFR97j&ark8$;r05~a1GL}~WiEH$2An9I5@B0L5U6rx#+hXRd!jLk zCXROrW)Z+6n1uhQ!gUITOILBdV3d6L%QmvXw}^LnHofLAqG+Is@0!VyLpFmF53(i* z$hx=uX(L`VoPh6B7)@OZFhxYQ-BR3oE7-WIUWT@5sm}l|=|6gYQ41|NVbTPvtHemY zDzt9MofyWX8fB(l?&~(`SmaFnAscC=k(gAa8xr4V(1eyfG&t$3pB5KpwNExx#e7#} zpW>)7&&xp$!iuG=z1tTWz5ynwCtymc#*MWZhmKRyy8vv44~R7t6ePuUDcY$a@nb`anYa7K?0 z*qyZN)?a?$LrsUk-?IJFiB*Ab!k;=Q$Kqm-^^(_L3|uRh>y8ec3=!b_291?S(T?nBqd{j&D5 zgUiUiv&eqvf}tcS5Wt9`VR_AkvaUZ$i^viPHDiHa&!ZyGNef;^P-~ewCWa&;JrFqH z6j#sUP5pxB$D9MtSQTX^7rq+qD=9NAi=T-#1XO6c)@5*w&q2JygaEy@K>isG zmYt$PArdT~U+bpU)Jy@-s2Ha>!;%*A3l*}Uf2JT}<&sGKIX+B$J;N5xsQfh(BsjK{ z2NTD>0Q)=EQ-KWN$tjt}{Vc2|yDLeQ2jj_UWn$hKROC5Ci7*8eHy?3+>ttr6m@e1I z8X3cbQ@&JG?{C>AjZYLotq@oZGx#wUue)R);yE}eldKr3^P=!hXstKvPYJeql?SH# zgzXRcFuIp|)UmXMXna#j*fJy{XhlpgRD><2K(gvx*Kxp4z>bz3Sgb`M$lXrI^S)If z$j#2p#FXOZRnE>*m;T^Mg9fYVxoBETpWKEOtwuU@t?TmEb(0Qkqu4*19RR^ z9SRi;nP4%ZHW+mz^C&XRjl{ge=P)PKvi zn-mNNf78HBN6iEXks}Be$dt?HYf&6-qFbi7nMfxHCO6t(;=*-Vj|;%FMQ)pLq#=LF zv`@B3p1U7JNxPe&k+LP~7K{#u zrTFU_edc!wD&RQ$8y;U`JSVVI#>bBjv-lrRkcXw`iv&15wU^#W77Zkfpt%qHy}bJmlAE=XYqXtg+jO5yXM4;BJXI(UNFTUq>WWMh^=YZg z4;D3qT!-#RIc6@1ro%~Sqq@DCw0vyQStK0}Kw5qZWRk-O0&+H|Vg`72k)Mw(rbx0y zWtB-oz!6|mo0BBYr1{1ceG}KK5Br+u$`(Boq9jFkvIY~3{EYh6@}IN-uejnW3=fX9 zMa%AakEjyp&SGM5CKwslgi(84nZ?j^SZ#W{)9e!JeXK$S)2*vL@hnR)5b`|!Cz}*I zRa5Dn6gt!v54woZyfjuqP&!Q(G|w~G;>|o&t<51Ql_65ceCW4y1yAKqiY50Kz{Gut z$EV7S`0l5=jQ{-*BdXrjnD5n43rEAqD_@CcTiBJg>z3Wz!j$*?*NX-#D>l0qMPi;? z(Vwo@sCBnXi}&X4tUqaut9dI3Ut3pv0X++!9>&bMYWgC1^&@a~cJ-33alcVwvzy8O zDS7qi6TjYfNd+SJFZM`&;BRvOBOHH!=dWDpQ>os{J>;X>7tpDAYH=6)ZeLW7u-(@< zaOdk^KsOwjF^Ayh@wvmN)Uv~B!jq}e z6l%Ys@}%7~^wug?LnX~<+GgH4{4hXcC0OiM344>p#JfK2yRZ=J*ha{LZp*7OLW5_q zTYk0bocX>D_+4K#-e`54VXC}>66uaqxLb)&V8jdSx#fCTo^< zs=ym@UDi5U-6^GvCycPeAuCSIASXCji6H^wYXB3^Z~ipyhGz@Utj*mjef!)FW-3Q1 zmET0Ez8eW9F_xDF;n)oYTQYp+71=5N8o=n*4b*pc%|4Q@a|+cv5$Qa)(@Z@xl)Iam zSB#)lZ|~^qcJz5ylhR4vbRZ&aAAPdZE4s=O(SSs@AS4zxmQd>9t$J9`2p< zyT?47w@Ne1vMMu;SZ3`Q^jLmc{u{*mto4U38{c_NUTY&K%O1NGb4M}at@}#`PINgG z(y^=!CT+a*;OfBWZhI4_Z}|&uW%m&jdlkIRrF>pZVC~N3%^;n}?RHo37qR z3VQqMwTUK6g50(mf6@h36&43|WyJhKZ${%7ZoasHm3BZ|!?Zqotvsl7Ya8YZc2?S~ zN*!Fj3p3H=saBAH)@IFjEa59gnbr>4HAy5Xm5oKr7g`B0UTLH^uSp9jKfSGf13{Wu zuu7S8<;QgGY}zkGc_a0uN8&8-NwU7!JWZgH2_#7ahNZy}v!hMeKU)9G(fN8H)qu; zmkEc8XS*plDJd9yKFP9_ZlclQB>M`AIwT^ori0dB%(pl;0{;TyNxOfLb^itEzo|Qu zGjnRDdGI?9IMS{Zp}SXCsUsf;YgOLaL=Xr0U#CTO28m;)GZ4Uy$5M)y9iAZ?noUc+ zu31)AYyH~lV;t_LBM9tt`Eds$`|?kqxk}Xzf%uE_ue-}q+Ae!E=ySn6-ib#M|l$KS|(@r8x|lWyxJo0#O!gDsD}VTU@gd=ZQNdHHHnt zzBespTv&sfl^>qse5pFH!KmW8+Tf5MAqX3jxG9~+jdg%PK?(IOx-yimTFfv<;)}T8*9f)7H?)Z}wtipimYF zsxSjxHbG0H+3N}0RZ&CXcf4EfTZe2}79BT7S#NhJ{;1xh?!1Y;5Ej+k(YfvJcm1xd z`CZVsDC?frd-7^Is%lB4ZKCCh4E&NczpCtFG`*>tmD2RNToxRk(*_2K*TSNj?ATD{ z`hKi8Z8yEGS`_K@(reIvHiW<7gmqyXZb>~tUUJ5l`SXp?PMh&Zqur^KSEV1Hml!s* zhkNeXEL=bu2H$D0>Q`apE!%3D9RkSWpDrQQ;i3a7@%Us?lwhZu%J z^~{*l%ntdwsK3}3b#0M4IT>fvN}%(%LsN_|c5)=uN_a`gtQZrLWEqHW2Z%&sY?wq- zxEQ9Y_yyq;$5)I-usz%p?8>Wy@>-%8F|n_KCMX zgzYAUzr1b6FZD__t4uDz(`J^9RE;v)NyVf7eq$2nQ5?omY;DkdWeK=>w#}j8tO^2}_R7f`^yrzV`gcG5)f6x(G{ z;-~STE7#&tspvt2XB4!uG1{PnWiL6ax;GwO#fji$>^%GAhqA5y8-Sk9&0r1F5)j2O%A8>v7bT2edT zL;HjTW9CznmlT{xcD*c0)uV0u-_^Sm-^$ODAdOf>G}Yf#B;H<4&J(zms)6e2a@<}4 zhOH8u*_%n7w+$YQMX3(z?HWdFKafjTXf|iJ$H|Wz@UvI5zRHhC{i>uPGXtL_GQub5 zC}WtB%TaJ!i+3>y{2lhrRZxGav$MpBrV}X_iez65ep3Rm7Yj#DS_zX!K+mWkaeDb# z44&d5`(ClXojUCLD8_F(7UAn^cZ61V9!1g*B}64UoY|ROdUNaIou5k<_LjQWAf~}2zS9k=?-J~PrS|rd zwNeife6r>GvLbL;KyuNXQ$FnU5{(V`OCJsbcqTn+M%01R#DMv zpys4_^lP2f$U;5}3{Tu}r|gy3c6p`+!Pg&h;3w!5SR=e%1F}rAJn4+vg2s6b+(onNEfTETz+F)>?KP@f0F4 zhHg{@qC>q&)E;MP07z@N8y^>)pg_mFZ*XVcN~(wG-kR%Gfv)Ny5XV8hI+EdM~DaDY#kfakG3q@-Hgjun-5|yI+y=&&xf3Qjj&Pa ztpC(c3bCI_w$J#(P1(4?b%J$oOdm3LP^XruD7|bvtJHk$;_b`DJgzHW?5T_N;|8=M^$4aeh$t;-^&HOH#+F+g8Jr z8T9L}Ptg#T{*YY~V<@Dh-gK)ALU635BU^OqE*jVF>)l(*q$_ry;*B!jg7(|(;*GkrF?2+>ZE01i1J=EE_onvF?3DL5we@I(tDc(|&`a3+TAl{UW$p3CY0 zJEN%fLlG3@BNoNtG8`W!K@*-^W`jaTOC zb4QynjGEa+zNxLZ>iP-mw&S+918yvR)3~QlsC&lb255EKG<6mPrW2PVJqeYPQrZKB zwV@}bGJdX3@xxY?xN}wd9cc4Hyi}n)bbaw}{{X-DIPV&27TjT={bOr23YOAwoh+^6 zoJMh7{{Z*z@ZA@#?>xSI_v`bl{frF&XahoF289mYfFEXr!a>`x1LQOqNIt=jOlUBJ zr=a_^7(t-I4CqkMm_eaK<V{PD zSX+R8zbbGKkWur&9ns|8!rJRqsa~vd=_KRen{8*_ySzR4Jj>;$RlTy%s9I2$EGx=N zel0~Nqn`%e{y0mfQoQOLa=f&GmT}0Vx=X~ zmblYype5GQ;z7s@NFX0xx{{1#)nv*uoL*w^TvC$RK}abh1vu%ATr6P6npw_E64Oa% x!%Ij@f2a@u`?ZRMQo>sqI+JqdZ*t;uGJik{Xx64-vkxomaF4jf||JixX8B72G diff --git a/prototype/src/main/resources/static/img/product-3.jpg b/prototype/src/main/resources/static/img/product-3.jpg deleted file mode 100644 index dbc1b2e20a08d8bacc98dcce28a79f3c5e00de4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14913 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 ZtpyiTXtx!Xl#_nxP|F#=c$@uP`(H-x3LO9d diff --git a/prototype/src/main/resources/static/img/product-4.jpg b/prototype/src/main/resources/static/img/product-4.jpg deleted file mode 100644 index 8f2d857f98e5468e92e4718e087ce3d0f3b5846c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5682 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 LipLRA(ue;5V?X(| diff --git a/prototype/src/main/resources/static/img/product-5.jpg b/prototype/src/main/resources/static/img/product-5.jpg deleted file mode 100644 index 5e0a406afbbed9d563034bc001b2831175b41056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9289 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 diff --git a/prototype/src/main/resources/static/img/product-6.jpg b/prototype/src/main/resources/static/img/product-6.jpg deleted file mode 100644 index 05d78c0a6aec4fae5e550121d0a0e7455258c178..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14439 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 Y>KZBdK>RQMLHXBg{U19i`7rap0LF-%xc~qF diff --git a/prototype/src/main/resources/static/img/product-7.jpg b/prototype/src/main/resources/static/img/product-7.jpg deleted file mode 100644 index 220140c357067eaedd69bba904c7c08949778b4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12305 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 Date: Sat, 23 May 2020 10:45:14 +0200 Subject: [PATCH 098/203] set default img if article is added to listed articles --- .../intern/InternArticleController.java | 41 +++++++++++++++++- .../ecommerce/repos/shop/ImageRepository.java | 20 +++++++++ .../resources/static/img/no_product_img.jpg | Bin 10261 -> 0 bytes 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java delete mode 100644 prototype/src/main/resources/static/img/no_product_img.jpg diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index 7a7e7fe..f47f6ca 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -1,13 +1,21 @@ package org.hso.ecommerce.controller.intern; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; + +import javax.imageio.ImageIO; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.Category; +import org.hso.ecommerce.entities.shop.Image; import org.hso.ecommerce.entities.supplier.ArticleOffer; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.hso.ecommerce.repos.shop.CategoryRepository; +import org.hso.ecommerce.repos.shop.ImageRepository; import org.hso.ecommerce.repos.shop.OffersRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +39,9 @@ public class InternArticleController { @Autowired private final OffersRepository offersRepository = null; + @Autowired + private final ImageRepository imageRepository = null; + @GetMapping("/") public String internListedArticles(Model model) { @@ -114,15 +125,41 @@ public class InternArticleController { tmpArticle.shouldReorder = false; tmpArticle.title = offeredArticle.title; tmpArticle.warehouseUnitsPerSlot = 1; - tmpArticle.image = articleRepository.findAll().get(0).image; // TODO set any static default image + setDefaultImage(tmpArticle); tmpArticle.related = offeredArticle; - articleRepository.save(tmpArticle); // save new article // return to edit article page return new RedirectView("../" + articleRepository.findArticleIDByRelatedID(offeredArticleID).get()); } + private void setDefaultImage(Article tmpArticle) { + String defaultImagePath = "./data/img/no_product_img.jpg"; // path + name of default img + Optional imageID = imageRepository.findImageByPath(defaultImagePath); // get default img + + if (imageID.isPresent()) { + // default img is in DB + tmpArticle.image = imageRepository.findImageById(imageID.get()); // set default img to new article + } else { + //default img is not in DB + File tmpFile = new File(defaultImagePath); + //test if default img file exits + if (!tmpFile.exists()) { + //fallback if the file not exists + BufferedImage bufferedImage = new BufferedImage(422, 428, BufferedImage.TYPE_INT_RGB); //create new file + try { + ImageIO.write(bufferedImage, "jpg", new File(defaultImagePath)); //save new file on disk + } catch (IOException e) { + e.printStackTrace(); + } + } + Image defaultImage = new Image(); + defaultImage.path = defaultImagePath; //set new file to default img + imageRepository.save(defaultImage); //save default img + tmpArticle.image = defaultImage; //set default img to new article + } + } + public static class UImodelArticles { public String imgPath; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java new file mode 100644 index 0000000..bcef6be --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.Image; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ImageRepository extends JpaRepository { + + @Query("SELECT i.id FROM Image i WHERE i.path = :path") + Optional findImageByPath(@Param("path") String path); + + @Query("SELECT i FROM Image i WHERE i.id = :imageId") + Image findImageById(@Param("imageId") long imageId); + +} diff --git a/prototype/src/main/resources/static/img/no_product_img.jpg b/prototype/src/main/resources/static/img/no_product_img.jpg deleted file mode 100644 index f898b51fd31f83c3457a480dc88abd647cdf0368..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10261 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 SwceM70g6f$Jd-2jk^cgCe6ub9 From 620e163a14335589475a02ac60ac7d22d3e401ba Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 23 May 2020 14:18:33 +0200 Subject: [PATCH 099/203] added feature to change an article image --- .../intern/InternArticleController.java | 67 ++++++++++++++----- .../ecommerce/repos/shop/ImageRepository.java | 2 +- .../src/main/resources/application.properties | 2 + .../src/main/resources/static/css/ecom.css | 2 +- .../templates/intern/listedArticles/id.html | 2 +- .../intern/listedArticles/index.html | 2 +- 6 files changed, 57 insertions(+), 20 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index f47f6ca..3748357 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -3,10 +3,16 @@ package org.hso.ecommerce.controller.intern; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Optional; - import javax.imageio.ImageIO; import org.hso.ecommerce.entities.shop.Article; @@ -19,9 +25,12 @@ import org.hso.ecommerce.repos.shop.ImageRepository; import org.hso.ecommerce.repos.shop.OffersRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.util.DigestUtils; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.view.RedirectView; @Controller @@ -80,12 +89,11 @@ public class InternArticleController { @RequestParam(value = "price_netto", required = true) String pricenetto, @RequestParam(value = "reorderMaxPrice", required = true) String reorderMaxPrice, @RequestParam(value = "autobuy", required = true) Boolean shouldReorder, - @RequestParam(value = "categories", required = true) String categories) { + @RequestParam(value = "categories", required = true) String categories, + @RequestParam(value = "img", required = true) MultipartFile imgFile) { Article tmpArticle = articleRepository.findArticleById(id); // get the old article - // TODO img - String[] separatedCategories = categories.split("\n"); tmpArticle.categories.clear(); @@ -102,6 +110,7 @@ public class InternArticleController { tmpArticle.shopPricePerUnitNetCent = (int) (Float.parseFloat(pricenetto) * 100); tmpArticle.warehouseUnitsPerSlot = Integer.parseInt(warehouseUnitsPerSlot); tmpArticle.title = title; + updateImage(tmpArticle, imgFile); tmpArticle.description = description; articleRepository.save(tmpArticle); // save updated article @@ -133,30 +142,56 @@ public class InternArticleController { return new RedirectView("../" + articleRepository.findArticleIDByRelatedID(offeredArticleID).get()); } - private void setDefaultImage(Article tmpArticle) { + private void setDefaultImage(Article article) { String defaultImagePath = "./data/img/no_product_img.jpg"; // path + name of default img - Optional imageID = imageRepository.findImageByPath(defaultImagePath); // get default img + Optional imageID = imageRepository.findImageIDByPath(defaultImagePath); // get default img if (imageID.isPresent()) { // default img is in DB - tmpArticle.image = imageRepository.findImageById(imageID.get()); // set default img to new article + article.image = imageRepository.findImageById(imageID.get()); // set default img to new article } else { - //default img is not in DB + // default img is not in DB File tmpFile = new File(defaultImagePath); - //test if default img file exits + // test if default img file exits if (!tmpFile.exists()) { - //fallback if the file not exists - BufferedImage bufferedImage = new BufferedImage(422, 428, BufferedImage.TYPE_INT_RGB); //create new file + // fallback if the file not exists + // create new file + BufferedImage bufferedImage = new BufferedImage(422, 428, BufferedImage.TYPE_INT_RGB); try { - ImageIO.write(bufferedImage, "jpg", new File(defaultImagePath)); //save new file on disk + ImageIO.write(bufferedImage, "jpg", new File(defaultImagePath)); // save new file on disk } catch (IOException e) { e.printStackTrace(); } } - Image defaultImage = new Image(); - defaultImage.path = defaultImagePath; //set new file to default img - imageRepository.save(defaultImage); //save default img - tmpArticle.image = defaultImage; //set default img to new article + Image defaultImage = new Image(); + defaultImage.path = defaultImagePath; // set new file to default img + imageRepository.save(defaultImage); // save default img + article.image = defaultImage; // set default img to new article + } + } + + private void updateImage(Article article, MultipartFile imgFile) { + // check if a file is present + if (imgFile.getSize() > 0) { + try { + // get file name based on MD5 hash + String fileName = DigestUtils.md5DigestAsHex(imgFile.getBytes()) + ".jpg"; + // check if img is already in system + Optional imageID = imageRepository.findImageIDByPath("./data/img/" + fileName); + if (imageID.isPresent()) { + article.image = imageRepository.findImageById(imageID.get()); // add existing img to article + } else { + // write new img file to disk + Files.newOutputStream(Paths.get("./data/img/", fileName)).write(imgFile.getBytes()); + // create new img + Image newImage = new Image(); + newImage.path = "./data/img/" + fileName; // set new file to new img + imageRepository.save(newImage); // save new img + article.image = newImage; // set new img to article + } + } catch (Exception e) { + setDefaultImage(article); // if upload failed, reset to default img + } } } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java index bcef6be..d0f3997 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java @@ -12,7 +12,7 @@ import java.util.Optional; public interface ImageRepository extends JpaRepository { @Query("SELECT i.id FROM Image i WHERE i.path = :path") - Optional findImageByPath(@Param("path") String path); + Optional findImageIDByPath(@Param("path") String path); @Query("SELECT i FROM Image i WHERE i.id = :imageId") Image findImageById(@Param("imageId") long imageId); diff --git a/prototype/src/main/resources/application.properties b/prototype/src/main/resources/application.properties index 18ec88c..aee1c7d 100644 --- a/prototype/src/main/resources/application.properties +++ b/prototype/src/main/resources/application.properties @@ -16,6 +16,8 @@ spring.jpa.show-sql=true #server.servlet.session.persistent=true # ---------------------------------------- # WEB PROPERTIES +spring.servlet.multipart.max-file-size=10MB +spring.servlet.multipart.max-request-size=10MB # ---------------------------------------- # EMBEDDED SERVER CONFIGURATION (ServerProperties) server.address=::1 diff --git a/prototype/src/main/resources/static/css/ecom.css b/prototype/src/main/resources/static/css/ecom.css index 1d8f125..2a696d2 100644 --- a/prototype/src/main/resources/static/css/ecom.css +++ b/prototype/src/main/resources/static/css/ecom.css @@ -278,7 +278,7 @@ img.s { } img.m { - width: var(--u8); + width: 20rem; } /* diff --git a/prototype/src/main/resources/templates/intern/listedArticles/id.html b/prototype/src/main/resources/templates/intern/listedArticles/id.html index 7050d3c..1fad38c 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/id.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/id.html @@ -24,7 +24,7 @@

    - +

    diff --git a/prototype/src/main/resources/templates/intern/listedArticles/index.html b/prototype/src/main/resources/templates/intern/listedArticles/index.html index 2840417..ad33f89 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/index.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/index.html @@ -53,7 +53,7 @@ - + From 019f5d8f1be0b5b6438b65653c416249bd0c8f2d Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 23 May 2020 14:19:43 +0200 Subject: [PATCH 100/203] code cleanup --- .../controller/intern/InternArticleController.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index 3748357..1d54302 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -3,13 +3,8 @@ package org.hso.ecommerce.controller.intern; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -25,7 +20,6 @@ import org.hso.ecommerce.repos.shop.ImageRepository; import org.hso.ecommerce.repos.shop.OffersRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.DigestUtils; From cff9417dc586ad282a9f039156843311f84137cc Mon Sep 17 00:00:00 2001 From: Philipp Schweizer Date: Sun, 24 May 2020 20:04:03 +0200 Subject: [PATCH 101/203] fixed pull request #24 --- delivery/config/bank.json | 26 -------------- delivery/config/hans-and-more.json | 34 ------------------- delivery/settings.gradle | 2 +- .../ecommerce/supplier/RequestController.java | 9 +++-- .../hso/ecommerce/supplier/data/Delivery.java | 4 +-- .../ecommerce/supplier/data/ReturnStatus.java | 20 +++++++++++ .../src/main/resources/application.properties | 2 +- 7 files changed, 28 insertions(+), 69 deletions(-) delete mode 100644 delivery/config/bank.json delete mode 100644 delivery/config/hans-and-more.json create mode 100644 delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java diff --git a/delivery/config/bank.json b/delivery/config/bank.json deleted file mode 100644 index 14cf837..0000000 --- a/delivery/config/bank.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "bank", - "name" : "Bank of Cheese", - "discount" : { - "minimumDailySalesVolumeNetCent": 100, - "percentDiscount": 1 - }, - "articles": [ - { - "title": "Big Mac", - "manufacturer": "Mc Donalds", - "articleNumber": "0x1 BIGMAC", - "vatPercent": 7, - "pricePerUnitNet": 700, - "shouldBeAdvertised": true - }, - { - "title": "500£ Schein", - "manufacturer": "Bank", - "articleNumber": "500", - "vatPercent": 0, - "pricePerUnitNet": 50000, - "shouldBeAdvertised": false - } - ] -} \ No newline at end of file diff --git a/delivery/config/hans-and-more.json b/delivery/config/hans-and-more.json deleted file mode 100644 index b925ddf..0000000 --- a/delivery/config/hans-and-more.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "id" : "hans", - "name" : "Hans and more", - "discount" : { - "minimumDailySalesVolumeNetCent": 100000, - "percentDiscount": 2 - }, - "articles": [ - { - "title": "Big Mac", - "manufacturer": "Mc Donalds", - "articleNumber": "0x1 BIGMAC", - "vatPercent": 7, - "pricePerUnitNet": 700, - "shouldBeAdvertised": true - }, - { - "title": "Pommes", - "manufacturer": "Mc Donalds", - "articleNumber": "0x1 POmes", - "vatPercent": 7, - "pricePerUnitNet": 100, - "shouldBeAdvertised": false - }, - { - "title": "Milchshake Premium 19%", - "manufacturer": "Mc Donalds", - "articleNumber": "0x2", - "vatPercent": 19, - "pricePerUnitNet": 50, - "shouldBeAdvertised": true - } - ] -} \ No newline at end of file diff --git a/delivery/settings.gradle b/delivery/settings.gradle index 9a7e6c7..6a6c7ab 100644 --- a/delivery/settings.gradle +++ b/delivery/settings.gradle @@ -1 +1 @@ -rootProject.name = 'supplier' +rootProject.name = 'delivery' diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java index 93c4050..ed62af7 100644 --- a/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java @@ -1,10 +1,9 @@ package org.hso.ecommerce.supplier; -import com.fasterxml.jackson.core.JsonFactoryBuilder; -import netscape.javascript.JSObject; + import org.hso.ecommerce.supplier.data.Delivery; import org.hso.ecommerce.supplier.data.DeliveryManager; -import org.json.JSONObject; +import org.hso.ecommerce.supplier.data.ReturnStatus; import org.springframework.web.bind.annotation.*; @@ -24,10 +23,10 @@ public class RequestController { } @GetMapping("/status") - public String searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) { + public ReturnStatus searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) { Delivery delivery = DeliveryManager.getInstance().getDeliveryByeID(trackingID); - return new JSONObject().put("status",delivery.getStatus()).put("estimatedArrival",delivery.getEstimatedArrival()).toString(); + return new ReturnStatus(delivery.getStatus(),delivery.getEstimatedArrival()); } } diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java index 0fdf75d..7a7b9cf 100644 --- a/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java @@ -9,7 +9,7 @@ import java.util.UUID; public class Delivery { private String[] states = {"Bestellung eingegangen","Bestellung auf dem Weg","Lieferung erfolgreich"}; - private double[] timeBorder = {4,24}; + private int[] timeBorder = {4,24}; private String name; private String address; @@ -35,7 +35,7 @@ public class Delivery { Long creationTime = this.creationTime.getTime(); Long diff = timeNow - creationTime; - double hour = (((diff / 1000) / 3600)); + double hour = (((diff / 1000.0) / 3600.0)); for (int i = 0; i < timeBorder.length; i++) { diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java new file mode 100644 index 0000000..16eaae7 --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.supplier.data; + +public class ReturnStatus { + + private String status; + private String estimatedArrival; + + public ReturnStatus(String status, String estimatedArrival) { + this.status = status; + this.estimatedArrival = estimatedArrival; + } + + public String getStatus() { + return status; + } + + public String getEstimatedArrival() { + return estimatedArrival; + } +} diff --git a/delivery/src/main/resources/application.properties b/delivery/src/main/resources/application.properties index c0d1914..eca0f47 100644 --- a/delivery/src/main/resources/application.properties +++ b/delivery/src/main/resources/application.properties @@ -1,2 +1,2 @@ server.address=::1 -server.port=8081 +server.port=8082 From 77892c9dfcfd3c7745aa7f7d0fb86d83ee00a8eb Mon Sep 17 00:00:00 2001 From: Philipp Schweizer Date: Sun, 24 May 2020 20:04:03 +0200 Subject: [PATCH 102/203] fixed pull request #25 --- delivery/config/bank.json | 26 -------------- delivery/config/hans-and-more.json | 34 ------------------- delivery/settings.gradle | 2 +- .../ecommerce/supplier/RequestController.java | 9 +++-- .../hso/ecommerce/supplier/data/Delivery.java | 4 +-- .../ecommerce/supplier/data/ReturnStatus.java | 20 +++++++++++ .../src/main/resources/application.properties | 2 +- 7 files changed, 28 insertions(+), 69 deletions(-) delete mode 100644 delivery/config/bank.json delete mode 100644 delivery/config/hans-and-more.json create mode 100644 delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java diff --git a/delivery/config/bank.json b/delivery/config/bank.json deleted file mode 100644 index 14cf837..0000000 --- a/delivery/config/bank.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id" : "bank", - "name" : "Bank of Cheese", - "discount" : { - "minimumDailySalesVolumeNetCent": 100, - "percentDiscount": 1 - }, - "articles": [ - { - "title": "Big Mac", - "manufacturer": "Mc Donalds", - "articleNumber": "0x1 BIGMAC", - "vatPercent": 7, - "pricePerUnitNet": 700, - "shouldBeAdvertised": true - }, - { - "title": "500£ Schein", - "manufacturer": "Bank", - "articleNumber": "500", - "vatPercent": 0, - "pricePerUnitNet": 50000, - "shouldBeAdvertised": false - } - ] -} \ No newline at end of file diff --git a/delivery/config/hans-and-more.json b/delivery/config/hans-and-more.json deleted file mode 100644 index b925ddf..0000000 --- a/delivery/config/hans-and-more.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "id" : "hans", - "name" : "Hans and more", - "discount" : { - "minimumDailySalesVolumeNetCent": 100000, - "percentDiscount": 2 - }, - "articles": [ - { - "title": "Big Mac", - "manufacturer": "Mc Donalds", - "articleNumber": "0x1 BIGMAC", - "vatPercent": 7, - "pricePerUnitNet": 700, - "shouldBeAdvertised": true - }, - { - "title": "Pommes", - "manufacturer": "Mc Donalds", - "articleNumber": "0x1 POmes", - "vatPercent": 7, - "pricePerUnitNet": 100, - "shouldBeAdvertised": false - }, - { - "title": "Milchshake Premium 19%", - "manufacturer": "Mc Donalds", - "articleNumber": "0x2", - "vatPercent": 19, - "pricePerUnitNet": 50, - "shouldBeAdvertised": true - } - ] -} \ No newline at end of file diff --git a/delivery/settings.gradle b/delivery/settings.gradle index 9a7e6c7..6a6c7ab 100644 --- a/delivery/settings.gradle +++ b/delivery/settings.gradle @@ -1 +1 @@ -rootProject.name = 'supplier' +rootProject.name = 'delivery' diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java index 93c4050..ed62af7 100644 --- a/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java @@ -1,10 +1,9 @@ package org.hso.ecommerce.supplier; -import com.fasterxml.jackson.core.JsonFactoryBuilder; -import netscape.javascript.JSObject; + import org.hso.ecommerce.supplier.data.Delivery; import org.hso.ecommerce.supplier.data.DeliveryManager; -import org.json.JSONObject; +import org.hso.ecommerce.supplier.data.ReturnStatus; import org.springframework.web.bind.annotation.*; @@ -24,10 +23,10 @@ public class RequestController { } @GetMapping("/status") - public String searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) { + public ReturnStatus searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) { Delivery delivery = DeliveryManager.getInstance().getDeliveryByeID(trackingID); - return new JSONObject().put("status",delivery.getStatus()).put("estimatedArrival",delivery.getEstimatedArrival()).toString(); + return new ReturnStatus(delivery.getStatus(),delivery.getEstimatedArrival()); } } diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java index 0fdf75d..7a7b9cf 100644 --- a/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java @@ -9,7 +9,7 @@ import java.util.UUID; public class Delivery { private String[] states = {"Bestellung eingegangen","Bestellung auf dem Weg","Lieferung erfolgreich"}; - private double[] timeBorder = {4,24}; + private int[] timeBorder = {4,24}; private String name; private String address; @@ -35,7 +35,7 @@ public class Delivery { Long creationTime = this.creationTime.getTime(); Long diff = timeNow - creationTime; - double hour = (((diff / 1000) / 3600)); + double hour = (((diff / 1000.0) / 3600.0)); for (int i = 0; i < timeBorder.length; i++) { diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java new file mode 100644 index 0000000..16eaae7 --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.supplier.data; + +public class ReturnStatus { + + private String status; + private String estimatedArrival; + + public ReturnStatus(String status, String estimatedArrival) { + this.status = status; + this.estimatedArrival = estimatedArrival; + } + + public String getStatus() { + return status; + } + + public String getEstimatedArrival() { + return estimatedArrival; + } +} diff --git a/delivery/src/main/resources/application.properties b/delivery/src/main/resources/application.properties index c0d1914..eca0f47 100644 --- a/delivery/src/main/resources/application.properties +++ b/delivery/src/main/resources/application.properties @@ -1,2 +1,2 @@ server.address=::1 -server.port=8081 +server.port=8082 From ac79e1cf22f0b4b6157167a59bb5e738af16adf6 Mon Sep 17 00:00:00 2001 From: Tyro Date: Sun, 24 May 2020 21:51:40 +0200 Subject: [PATCH 103/203] Check for inactive accounts Check for employee by access on /intern --- .../hso/ecommerce/app/RequestController.java | 8 +++++++- .../ecommerce/components/LoginIntercepter.java | 17 ++++++++++++++++- .../controller/RegisterController.java | 15 +++++---------- .../entities/booking/PaymentMethod.java | 3 +-- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 2680f28..bb0b5d6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -46,11 +46,17 @@ public class RequestController { return "login"; } - if (!user.get().validatePassword(password)) { + if (!user.get().validatePassword(password)) { request.setAttribute("error", "Passwort falsch."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "login"; } + + if (!user.get().isActive) { + request.setAttribute("error", "User ist deaktiviert."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "login"; + } session.setAttribute("userId", user.get().getId()); diff --git a/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java b/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java index 8e1fc1d..e537867 100644 --- a/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java +++ b/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java @@ -24,6 +24,7 @@ public class LoginIntercepter implements HandlerInterceptor { HttpSession session = request.getSession(); Object userId = session.getAttribute("userId"); + Optional user = null; if (request.getRequestURI().startsWith("/user/")) { System.out.println("USER"); @@ -43,10 +44,24 @@ public class LoginIntercepter implements HandlerInterceptor { response.sendRedirect("/login"); return false; } + + user = userRepository.findById((Long) userId); + + if(user.isPresent() && !user.get().isEmployee) + { + session.setAttribute("afterLogin", request.getRequestURI()); + response.sendRedirect("/"); + return false; + } + } + + if (!request.getRequestURI().startsWith("/login")) { + session.removeAttribute("afterLogin"); } if (userId != null) { - Optional user = userRepository.findById((Long) userId); + if (user == null) + user = userRepository.findById((Long) userId); user.ifPresent(value -> request.setAttribute("user", value)); } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index e38a583..00ade5d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -1,6 +1,5 @@ package org.hso.ecommerce.controller; -import org.hso.ecommerce.entities.booking.PaymentMethod; import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; @@ -21,7 +20,7 @@ public class RegisterController { private final UserRepository userRepository = null; @PostMapping("/register") - public String register( + public String registerPost( HttpServletRequest request, HttpServletResponse response, @RequestParam("username") String username, @@ -37,7 +36,7 @@ public class RegisterController { Optional user = userRepository.findByEmail(username); if (user.isPresent()) { request.setAttribute("error", "Email Adresse existiert bereits!"); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "register"; } @@ -52,10 +51,9 @@ public class RegisterController { newUser.email = username; newUser.setPassword(password); newUser.email = username; - if (type.equals("bus")) - newUser.isEmployee = true; - else - newUser.isEmployee = false; + newUser.isEmployee = false; + //TODO for salutation, type, ad are no attributes/fields in the class/database. Add when they are there. + newUser.isActive = true; newUser.created = new java.sql.Timestamp(System.currentTimeMillis()); @@ -64,9 +62,6 @@ public class RegisterController { newAddress.addressString = address; newUser.defaultDeliveryAddress = newAddress; - PaymentMethod defaultPaymentMethod = PaymentMethod.fromCreditCarNumber("123456"); - newUser.defaultPayment = defaultPaymentMethod; - userRepository.save(newUser); // save newUser return "login"; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java index 4f81062..af0a9c0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java @@ -1,11 +1,10 @@ package org.hso.ecommerce.entities.booking; import javax.persistence.Embeddable; -import javax.validation.constraints.NotNull; @Embeddable public class PaymentMethod { - @NotNull + public String creditCardNumber; public static PaymentMethod fromCreditCarNumber(String cardnumber) { From 33ff754b9d67dab7f1d24424ff03416e52b2ce50 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 09:36:40 +0200 Subject: [PATCH 104/203] remove advertisementflag --- .../action/user/UpdateUserSettingsAction.java | 6 ------ .../hso/ecommerce/controller/UserController.java | 16 ---------------- .../org/hso/ecommerce/entities/user/User.java | 1 - .../main/resources/templates/user/settings.html | 15 --------------- 4 files changed, 38 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index fe4805a..5732a7f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -53,12 +53,6 @@ public class UpdateUserSettingsAction { return new UpdateResult(true); } - public UpdateResult updateAdvertisementFlag(boolean advertisementFlag){ - this.user.isAdvertisementActivated = advertisementFlag; - this.repository.save(this.user); - return new UpdateResult(true); - } - public UpdateResult updatePaymentInfo(String creditCardNumber){ UpdateResult result = new UpdateResult(false); if(creditCardNumber.matches("[0-9]+")){ diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 803dbf9..5b0c50b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -107,22 +107,6 @@ public class UserController { return "user/settings"; } - @PostMapping("/settings/changeAdSettings") - public String changeAdSettings(HttpSession session, - @RequestParam("ad") String ad, - HttpServletRequest request - ) { - User user = userRepository.findById((long) session.getAttribute("userId")).get(); - - UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - UpdateUserSettingsAction.UpdateResult result = cusa.updateAdvertisementFlag(ad.equals("y")); - if (result.updated == false) { - request.setAttribute("error", result.errorString); - } - - return "user/settings"; - } - @PostMapping("/settings/changePaymentInfo") public String changePaymentInfo(HttpSession session, @RequestParam("creditCardNumber") String creditCardNumber, diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index 40196c7..61a4503 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -33,7 +33,6 @@ public class User { public boolean isActive; public boolean isEmployee; - public boolean isAdvertisementActivated; @Embedded public Address defaultDeliveryAddress; diff --git a/prototype/src/main/resources/templates/user/settings.html b/prototype/src/main/resources/templates/user/settings.html index 89e81fb..86f25e3 100644 --- a/prototype/src/main/resources/templates/user/settings.html +++ b/prototype/src/main/resources/templates/user/settings.html @@ -91,21 +91,6 @@

    -
    -
    -

    Werbung

    -
    -
    -
    - -
    - -
    -
    - -
    -
    -

    Zahlungsinformation

    From 178f98d6649d8e653b0073b54017d71820891fb3 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 10:10:42 +0200 Subject: [PATCH 105/203] fix error handling --- .../action/user/UpdateUserSettingsAction.java | 12 ++++++++---- .../org/hso/ecommerce/controller/UserController.java | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 5732a7f..9aa8370 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -33,9 +33,13 @@ public class UpdateUserSettingsAction { if(this.user.validatePassword(oldPassword)) { if(password1.equals(password2)){ - this.user.setPassword(password1); - this.repository.save(this.user); - result.updated = true; + if(!password1.equals(oldPassword)){ + this.user.setPassword(password1); + this.repository.save(this.user); + result.updated = true; + }else { + result.errorString = "Die neuen Passwörter entsprechen dem alten Passwort."; + } }else{ result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; } @@ -60,7 +64,7 @@ public class UpdateUserSettingsAction { this.repository.save(this.user); result.updated = true; }else{ - result.errorString = "Kreditkartennummer enthält Buchstaben. Bitte versuchen Sie es erneut."; + result.errorString = "Kreditkartennummer darf nur Zahlen enthalten. Bitte versuchen Sie es erneut."; } return result; } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 5b0c50b..7099dd7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -66,9 +66,10 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updateEmail(email); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } @PostMapping("/settings/changePwd") @@ -84,9 +85,10 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updatePassword(oldPassword, password1, password2); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } @PostMapping("/settings/changeAddress") @@ -102,9 +104,10 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updateShippingInfo(salutation, name, address); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } @PostMapping("/settings/changePaymentInfo") @@ -118,8 +121,9 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updatePaymentInfo(creditCardNumber); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } } From 8d65998ac205b0f00a49065a739993bf0613e5c9 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 10:21:36 +0200 Subject: [PATCH 106/203] code cleanup --- .../action/user/UpdateUserSettingsAction.java | 40 +++++++++---------- .../org/hso/ecommerce/entities/user/User.java | 6 +-- .../repos/shop/CustomerOrderRepository.java | 3 +- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 9aa8370..4796dbe 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -1,26 +1,23 @@ package org.hso.ecommerce.action.user; -import com.sun.xml.bind.v2.TODO; -import org.hibernate.sql.Update; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; public class UpdateUserSettingsAction { private User user; private UserRepository repository; - public UpdateUserSettingsAction(User user, UserRepository repository){ + public UpdateUserSettingsAction(User user, UserRepository repository) { this.user = user; this.repository = repository; } - public UpdateResult updateEmail(String newMail){ + public UpdateResult updateEmail(String newMail) { UpdateResult result = new UpdateResult(false); - if(!newMail.contains("@")){ + if (!newMail.contains("@")) { result.errorString = "Ändern der Email-Addresse nicht möglich. Bitte versuchen Sie es erneut."; - }else{ + } else { this.user.email = newMail; this.repository.save(this.user); result.updated = true; @@ -28,28 +25,27 @@ public class UpdateUserSettingsAction { return result; } - public UpdateResult updatePassword(String oldPassword, String password1, String password2){ + public UpdateResult updatePassword(String oldPassword, String password1, String password2) { UpdateResult result = new UpdateResult(false); - if(this.user.validatePassword(oldPassword)) - { - if(password1.equals(password2)){ - if(!password1.equals(oldPassword)){ + if (this.user.validatePassword(oldPassword)) { + if (password1.equals(password2)) { + if (!password1.equals(oldPassword)) { this.user.setPassword(password1); this.repository.save(this.user); result.updated = true; - }else { + } else { result.errorString = "Die neuen Passwörter entsprechen dem alten Passwort."; } - }else{ + } else { result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; } - }else{ + } else { result.errorString = "Das eingegebene alte Passwort stimmt nicht mit dem momentan gespeicherten Passwort überein. Bitte versuchen Sie es erneut."; } return result; } - public UpdateResult updateShippingInfo(String salutation, String name, String address){ + public UpdateResult updateShippingInfo(String salutation, String name, String address) { this.user.salutation = salutation; this.user.name = name; this.user.defaultDeliveryAddress.addressString = address; @@ -57,28 +53,28 @@ public class UpdateUserSettingsAction { return new UpdateResult(true); } - public UpdateResult updatePaymentInfo(String creditCardNumber){ + public UpdateResult updatePaymentInfo(String creditCardNumber) { UpdateResult result = new UpdateResult(false); - if(creditCardNumber.matches("[0-9]+")){ + if (creditCardNumber.matches("[0-9]+")) { this.user.defaultPayment.creditCardNumber = creditCardNumber; this.repository.save(this.user); result.updated = true; - }else{ + } else { result.errorString = "Kreditkartennummer darf nur Zahlen enthalten. Bitte versuchen Sie es erneut."; } return result; } - public class UpdateResult{ + public class UpdateResult { public boolean updated; //if true worked, if false not worked public String errorString; - public UpdateResult(boolean updated, String errorString){ + public UpdateResult(boolean updated, String errorString) { this.updated = updated; this.errorString = errorString; } - public UpdateResult(boolean updated){ + public UpdateResult(boolean updated) { this.updated = updated; this.errorString = ""; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index 61a4503..e08c2ee 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -24,7 +24,7 @@ public class User { @Column(unique = true) public String email; - @Column(insertable=false, updatable = false) + @Column(insertable = false, updatable = false) public String name; public String salutation; @@ -44,10 +44,6 @@ public class User { return id; } - public void setEmail(String email) { - this.email = email; - } - public boolean validatePassword(String password) { return BCrypt.checkpw(password, passwordHash); } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java index da42f26..3321232 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java @@ -13,5 +13,4 @@ public interface CustomerOrderRepository extends JpaRepository getOrdersByUserId(long userId); -} - +} \ No newline at end of file From 86a2e5d8c28cdc9c5ac9b2bf3124aa55c1604c16 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 12:17:09 +0200 Subject: [PATCH 107/203] code cleanup --- .../java/org/hso/ecommerce/controller/UserController.java | 2 -- .../java/org/hso/ecommerce/entities/shop/CustomerOrder.java | 5 +++++ .../src/main/resources/templates/user/orders/index.html | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 7099dd7..8a90d43 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -40,8 +40,6 @@ public class UserController { User user = userRepository.findById(userId).get(); model.addAttribute("user", user); - //TODO: klären wegen Geschäftskundenunterscheidung - return "user/settings"; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java index d9410f7..8b703b6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java @@ -56,4 +56,9 @@ public class CustomerOrder { public String formatDeliveredAt(){ return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt); } + + public String getEstimatedArrival() { + //TODO: get estimated arrival from api + return "TODO TODO TODO"; + } } diff --git a/prototype/src/main/resources/templates/user/orders/index.html b/prototype/src/main/resources/templates/user/orders/index.html index b746f52..327bbf2 100644 --- a/prototype/src/main/resources/templates/user/orders/index.html +++ b/prototype/src/main/resources/templates/user/orders/index.html @@ -27,7 +27,7 @@ - + From 8386629088983173b8b229566c7b402d6a86eb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Mon, 25 May 2020 17:08:15 +0200 Subject: [PATCH 108/203] Implement the reorder cronjob --- .../ecommerce/action/cronjob/ICronjob.java | 29 --- .../cronjob/ReadSupplierDataAction.java | 83 +++++++ .../hso/ecommerce/action/cronjob/Reorder.java | 38 --- .../action/cronjob/ReorderAction.java | 119 +++++++++ .../action/cronjob/UpdateOffersAction.java | 49 ++++ .../org/hso/ecommerce/api/data/Supplier.java | 10 + .../controller/cronjob/CronjobController.java | 225 +++++++++++++++++- .../entities/booking/BookingAccountEntry.java | 12 +- .../entities/booking/BookingReason.java | 4 + .../BookingAccountEntryRepository.java | 7 +- .../cronjob/BackgroundJobRepository.java | 4 +- .../repos/shop/CustomerOderRepository.java | 6 + .../supplier/ArticleOfferRepository.java | 8 + .../supplier/SupplierOrderRepository.java | 14 ++ .../repos/supplier/SupplierRepository.java | 10 + 15 files changed, 540 insertions(+), 78 deletions(-) delete mode 100644 prototype/src/main/java/org/hso/ecommerce/action/cronjob/ICronjob.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReadSupplierDataAction.java delete mode 100644 prototype/src/main/java/org/hso/ecommerce/action/cronjob/Reorder.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/supplier/ArticleOfferRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ICronjob.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ICronjob.java deleted file mode 100644 index bf2f3d8..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ICronjob.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.hso.ecommerce.action.cronjob; - -import java.util.Calendar; - -public interface ICronjob { - /** - * Calculate the earliest cronjob execution time that happens after the given reference time. - * - * @param reference Position in time to start searching. The implementor is allowed to modify the reference time. - * @return A new Calendar instance (or the same) containing the time for next execution. - */ - Calendar nextExecution(Calendar reference); - - /** - * Calculate the latest cronjob execution time that happens before or exactly at the given refernce time. - * - * @param reference Position in time to start searching. The implementor is allowed to modify the reference time. - * @return A new Calendar instance (or the same) containing the time of the last execution. - */ - Calendar previousExecution(Calendar reference); - - /** - * Execute this cronjob. - * - * @param time The point in time this execution was scheduled. In case of a missed cronjob, the actual time of this - * call might be much later. - */ - void executeAt(Calendar time); -} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReadSupplierDataAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReadSupplierDataAction.java new file mode 100644 index 0000000..50930b8 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReadSupplierDataAction.java @@ -0,0 +1,83 @@ +package org.hso.ecommerce.action.cronjob; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +import org.hso.ecommerce.api.SupplierService; +import org.hso.ecommerce.api.data.Article; +import org.hso.ecommerce.api.data.Supplier; + +public class ReadSupplierDataAction { + private List suppliers; + + public static class ArticleIdentifier { + public final String manufacturer; + public final String articleNumber; + + public ArticleIdentifier(String manufacturer, String articleNumber) { + this.manufacturer = manufacturer; + this.articleNumber = articleNumber; + } + + @Override + public int hashCode() { + return Objects.hash(manufacturer, articleNumber); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ArticleIdentifier)) { + return false; + } + ArticleIdentifier otherId = (ArticleIdentifier) other; + return this.manufacturer.equals(otherId.manufacturer) && this.articleNumber.equals(otherId.articleNumber); + } + } + + public ReadSupplierDataAction(List suppliers) { + this.suppliers = suppliers; + } + + public static class Offer { + public final org.hso.ecommerce.entities.supplier.Supplier dbSupplier; + public final Supplier apiSupplier; + + public Offer(org.hso.ecommerce.entities.supplier.Supplier dbSupplier, Supplier apiSupplier) { + this.dbSupplier = dbSupplier; + this.apiSupplier = apiSupplier; + } + } + + public static class Result { + public final ArrayList supplierData; + public final HashMap cheapestOffer; + + public Result(ArrayList supplierData, HashMap cheapestOffer) { + this.supplierData = supplierData; + this.cheapestOffer = cheapestOffer; + } + } + + public Result finish() { + ArrayList suppliers = new ArrayList<>(); + HashMap price = new HashMap<>(); + HashMap cheapest = new HashMap<>(); + for (org.hso.ecommerce.entities.supplier.Supplier supplier : this.suppliers) { + SupplierService service = new SupplierService(supplier.apiUrl); + Supplier apiSupplier = service.getSupplier(); + suppliers.add(apiSupplier); + for (Article article : apiSupplier.articles) { + ArticleIdentifier identifier = new ArticleIdentifier(article.manufacturer, article.articleNumber); + Integer previousPrice = price.get(identifier); + if (previousPrice == null || article.pricePerUnitNet < previousPrice) { + price.put(identifier, article.pricePerUnitNet); + cheapest.put(identifier, new Offer(supplier, apiSupplier)); + } + } + } + + return new Result(suppliers, cheapest); + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/Reorder.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/Reorder.java deleted file mode 100644 index 212f696..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/Reorder.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.hso.ecommerce.action.cronjob; - -import java.util.Calendar; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Reorder implements ICronjob { - private static final Logger log = LoggerFactory.getLogger(Reorder.class); - - @Override - public Calendar nextExecution(Calendar reference) { - if (reference.get(Calendar.HOUR_OF_DAY) >= 8) { - reference.add(Calendar.DAY_OF_MONTH, 1); - } - reference.set(Calendar.HOUR_OF_DAY, 8); - reference.set(Calendar.MINUTE, 0); - reference.set(Calendar.SECOND, 0); - reference.set(Calendar.MILLISECOND, 0); - return reference; - } - - @Override - public Calendar previousExecution(Calendar reference) { - if (reference.get(Calendar.HOUR_OF_DAY) < 8) { - reference.add(Calendar.DAY_OF_MONTH, -1); - } - reference.set(Calendar.HOUR_OF_DAY, 8); - reference.set(Calendar.MINUTE, 0); - reference.set(Calendar.SECOND, 0); - reference.set(Calendar.MILLISECOND, 0); - return reference; - } - - @Override - public void executeAt(Calendar time) { - log.info("Executing Reorder Cronjob"); - } -} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java new file mode 100644 index 0000000..2bad0e0 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java @@ -0,0 +1,119 @@ +package org.hso.ecommerce.action.cronjob; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.List; + +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer; +import org.hso.ecommerce.api.SupplierService; +import org.hso.ecommerce.api.data.Order; +import org.hso.ecommerce.api.data.OrderConfirmation; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ReorderAction { + private static final Logger log = LoggerFactory.getLogger(ReorderAction.class); + + private Article article; + private Integer[] orderedAmounts; + private Integer undeliveredReorders; + private List inStock; + private HashMap cheapestOffer; + private HashMap articleOffers; + + public ReorderAction( + Article article, Integer[] orderedAmounts, + Integer undeliveredReorders, + List inStock, + HashMap cheapestOffer, + HashMap articleOffers + ) { + this.article = article; + this.orderedAmounts = orderedAmounts; + this.undeliveredReorders = undeliveredReorders; + this.inStock = inStock; + this.cheapestOffer = cheapestOffer; + this.articleOffers = articleOffers; + } + + private int null_to_zero(Integer input) { + return input == null ? 0 : input; + } + + private int getAmountInStock() { + int sum = 0; + for (WarehouseBookingPositionSlotEntry entry : inStock) { + sum += entry.newSumSlot; + } + return sum; + } + + private int calculateAmountToReorder() { + // Algorithm as described in the documentation + int a = null_to_zero(orderedAmounts[0]); + int b = null_to_zero(orderedAmounts[1]); + int c = null_to_zero(orderedAmounts[2]); + + int x = Math.max(Math.max(a, b), c); + int y = Math.min(Math.min(a, b), c); + + int n = 6 * x - 2 * y; + if (n < 3) { + n = 3; + } + + int i = null_to_zero(undeliveredReorders); + int l = getAmountInStock(); + + int o = n - i - l; + + return o; + } + + public SupplierOrder finish() { + if (!article.shouldReorder) { + return null; + } + + int amount = calculateAmountToReorder(); + if (amount <= 0) { + return null; + } + + ArticleIdentifier identifier = new ArticleIdentifier(article.related.manufacturer, article.related.articleNumber); + Offer offer = cheapestOffer.get(identifier); + if (offer == null) { + log.info("Could not order \"" + article.title + "\" because there is no supplier delivering it."); + return null; + } + + ArticleOffer articleOffer = articleOffers.get(identifier); + org.hso.ecommerce.api.data.Article apiArticle = offer.apiSupplier.findArticle(identifier.manufacturer, + identifier.articleNumber); + if (apiArticle.pricePerUnitNet > article.reorderMaxPrice) { + log.info("Could not order \"" + article.title + "\" because it is currently too expensive."); + return null; + } + + Order order = new Order(); + order.manufacturer = articleOffer.manufacturer; + order.articleNumber = articleOffer.articleNumber; + order.quantity = amount; + order.maxTotalPriceCentNet = apiArticle.pricePerUnitNet * amount; + + OrderConfirmation confirm = new SupplierService(offer.dbSupplier.apiUrl).order(order); + SupplierOrder createdOrder = new SupplierOrder(); + createdOrder.created = new Timestamp(System.currentTimeMillis()); + createdOrder.supplier = offer.dbSupplier; + createdOrder.ordered = articleOffer; + createdOrder.numberOfUnits = confirm.quantity; + createdOrder.pricePerUnitNetCent = confirm.pricePerUnitNetCent; + createdOrder.totalPriceNet = confirm.totalPriceNetCharged; + return createdOrder; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java new file mode 100644 index 0000000..3046f2f --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java @@ -0,0 +1,49 @@ +package org.hso.ecommerce.action.cronjob; + +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer; +import org.hso.ecommerce.api.data.Article; +import org.hso.ecommerce.entities.supplier.ArticleOffer; + +public class UpdateOffersAction { + private List offers; + private HashMap cheapestOffer; + + public UpdateOffersAction(List offers, HashMap cheapestOffer) { + this.offers = offers; + this.cheapestOffer = cheapestOffer; + } + + private HashMap mapOffers() { + HashMap map = new HashMap<>(); + for (ArticleOffer offer : offers) { + ArticleIdentifier identifier = new ArticleIdentifier(offer.manufacturer, offer.articleNumber); + map.put(identifier, offer); + } + return map; + } + + public List finish() { + HashMap availableOffers = mapOffers(); + for (Entry cheapestOffer : cheapestOffer.entrySet()) { + String manufacturer = cheapestOffer.getKey().manufacturer; + String articleNumber = cheapestOffer.getKey().articleNumber; + ArticleOffer currentOffer = availableOffers.get(cheapestOffer.getKey()); + if (currentOffer == null) { + currentOffer = new ArticleOffer(); + currentOffer.manufacturer = manufacturer; + currentOffer.articleNumber = articleNumber; + offers.add(currentOffer); + } + Article currentOfferedArticle = cheapestOffer.getValue().apiSupplier.findArticle(manufacturer, + articleNumber); + currentOffer.vatPercent = currentOfferedArticle.vatPercent; + currentOffer.shouldBeAdvertised = currentOfferedArticle.shouldBeAdvertised; + } + return offers; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/api/data/Supplier.java b/prototype/src/main/java/org/hso/ecommerce/api/data/Supplier.java index 4ed1f7b..211f353 100644 --- a/prototype/src/main/java/org/hso/ecommerce/api/data/Supplier.java +++ b/prototype/src/main/java/org/hso/ecommerce/api/data/Supplier.java @@ -8,4 +8,14 @@ public class Supplier { public String name; public SupplierDiscount discount; public List
    articles; + + public Article findArticle(String manufacturer, String articleNumber) { + for (Article a : articles) { + if (a.manufacturer.equals(manufacturer) && a.articleNumber.equals(articleNumber)) { + return a; + } + } + + return null; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java index 4f58c58..143f1cf 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java @@ -5,18 +5,206 @@ import java.util.Calendar; import java.util.Collections; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; + import javax.annotation.PostConstruct; -import org.hso.ecommerce.action.cronjob.ICronjob; -import org.hso.ecommerce.action.cronjob.Reorder; + +import org.hso.ecommerce.action.booking.CreateBookingAction; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; +import org.hso.ecommerce.action.cronjob.ReorderAction; +import org.hso.ecommerce.action.cronjob.UpdateOffersAction; +import org.hso.ecommerce.entities.booking.Booking; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.BookingReason; import org.hso.ecommerce.entities.cron.BackgroundJob; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.hso.ecommerce.entities.supplier.Supplier; +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; +import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.shop.CustomerOderRepository; +import org.hso.ecommerce.repos.supplier.ArticleOfferRepository; +import org.hso.ecommerce.repos.supplier.SupplierOrderRepository; +import org.hso.ecommerce.repos.supplier.SupplierRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +interface ICronjob { + /** + * Calculate the earliest cronjob execution time that happens after the given reference time. + * + * @param reference Position in time to start searching. The implementor is allowed to modify the reference time. + * @return A new Calendar instance (or the same) containing the time for next execution. + */ + Calendar nextExecution(Calendar reference); + + /** + * Calculate the latest cronjob execution time that happens before or exactly at the given refernce time. + * + * @param reference Position in time to start searching. The implementor is allowed to modify the reference time. + * @return A new Calendar instance (or the same) containing the time of the last execution. + */ + Calendar previousExecution(Calendar reference); + + /** + * Execute this cronjob. + * + * @param time The point in time this execution was scheduled. In case of a missed cronjob, the actual time of + * this call might be much later. + * @param controller Back-reference that allows to use repositories. + */ + void executeAt(Calendar time, CronjobController controller); +} + +@Component +class Reorder implements ICronjob { + private static final Logger log = LoggerFactory.getLogger(Reorder.class); + + @Override + public Calendar nextExecution(Calendar reference) { + if (reference.get(Calendar.HOUR_OF_DAY) >= 8) { + reference.add(Calendar.DAY_OF_MONTH, 1); + } + reference.set(Calendar.HOUR_OF_DAY, 8); + reference.set(Calendar.MINUTE, 0); + reference.set(Calendar.SECOND, 0); + reference.set(Calendar.MILLISECOND, 0); + return reference; + } + + @Override + public Calendar previousExecution(Calendar reference) { + if (reference.get(Calendar.HOUR_OF_DAY) < 8) { + reference.add(Calendar.DAY_OF_MONTH, -1); + } + reference.set(Calendar.HOUR_OF_DAY, 8); + reference.set(Calendar.MINUTE, 0); + reference.set(Calendar.SECOND, 0); + reference.set(Calendar.MILLISECOND, 0); + return reference; + } + + /** + * Calculates the amount of ordered articles by customers for the given article type in the time between begin and + * end. + * + * @param article The article to search orders for. + * @param begin The start time for the search (included) + * @param end The end time for the search (excluded) + * @return The number of articles that were ordered by customers in the given range. + */ + private Integer getOrderedAmounts(Article article, Calendar begin, Calendar end, CronjobController controller) { + return controller.customerOrderRepository.countOrdersOfArticleInTimespan( + article.id, + new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis())); + } + + /** + * Calculates the amount of ordered articles by customers for the given article type in the three days before the + * given reference time. The return-array contains 3 fields: Index 0: Orders 72 to 48 hours ago; Index 1: Orders 48 + * to 24 hours ago; Index 2: Orders 24 to 0 hours ago. + * + * @param article The article for which the customer orders are checked. + * @param time The reference time to use for calculation of the last orders. + * @return A 3-element array containing the orders of the last three days. + */ + private Integer[] getOrderedAmounts(Article article, Calendar time, CronjobController controller) { + Calendar oneDayBefore = (Calendar) time.clone(); + oneDayBefore.add(Calendar.DAY_OF_MONTH, -1); + Calendar twoDaysBefore = (Calendar) time.clone(); + twoDaysBefore.add(Calendar.DAY_OF_MONTH, -2); + Calendar threeDaysBefore = (Calendar) time.clone(); + threeDaysBefore.add(Calendar.DAY_OF_MONTH, -3); + + return new Integer[] { // + getOrderedAmounts(article, threeDaysBefore, twoDaysBefore, controller), // + getOrderedAmounts(article, twoDaysBefore, oneDayBefore, controller), // + getOrderedAmounts(article, oneDayBefore, time, controller), // + }; + } + + private HashMap mapArticleOffers(List articleOffers) { + HashMap map = new HashMap<>(); + for (ArticleOffer articleOffer : articleOffers) { + ArticleIdentifier identifier = new ArticleIdentifier(articleOffer.manufacturer, articleOffer.articleNumber); + map.put(identifier, articleOffer); + } + return map; + } + + @Override + public void executeAt(Calendar time, CronjobController controller) { + List suppliers = controller.supplierRepository.findAll(); + ReadSupplierDataAction.Result supplierData = new ReadSupplierDataAction(suppliers).finish(); + + // Save the new offers in the database + List allOffers = controller.articleOfferRepository.findAll(); + allOffers = new UpdateOffersAction(allOffers, supplierData.cheapestOffer).finish(); + controller.articleOfferRepository.saveAll(allOffers); + + HashMap mappedOffers = mapArticleOffers(allOffers); + + // Reorder + List
    allArticles = controller.articleRepository.findAll(); + for (Article article : allArticles) { + Integer[] orderedAmounts = getOrderedAmounts(article, time, controller); + + Integer undeliveredReorders = controller.supplierOrderRepository + .countUndeliveredReorders(article.related.articleNumber); + + List inStock = controller.warehouseBookingPositionSlotEntryRepository + .getByArticle(article.id); + + ReorderAction action = new ReorderAction(article, orderedAmounts, + undeliveredReorders, + inStock, + supplierData.cheapestOffer, mappedOffers); + SupplierOrder order = action.finish(); + if (order != null) { + controller.supplierOrderRepository.save(order); + + // Create bookings for this order + int netPrice = order.totalPriceNet; + int vatPercent = order.ordered.vatPercent; + int vatAmount = netPrice * vatPercent / 100; + int grossPrice = netPrice + vatAmount; + + // Obligation towards the supplier + BookingAccountEntry mainAccount = controller.bookingAccountEntryRepository.getByMain() + .orElseGet(BookingAccountEntry::newMain); + BookingAccountEntry supplierAccount = controller.bookingAccountEntryRepository + .getBySupplier(order.supplier.id) + .orElseGet(() -> BookingAccountEntry.newSupplier(order.supplier)); + BookingReason obligationReason = new BookingReason(order); + Booking obligationBooking = new CreateBookingAction(mainAccount, + supplierAccount, + obligationReason, + grossPrice).finish(); + controller.bookingRepository.save(obligationBooking); + + // Input Tax + BookingAccountEntry vatAccount = controller.bookingAccountEntryRepository.getByVat() + .orElseGet(BookingAccountEntry::newVat); + mainAccount = controller.bookingAccountEntryRepository.getByMain().get(); + BookingReason inputTaxReason = new BookingReason(order); + Booking inputTaxBooking = new CreateBookingAction(vatAccount, mainAccount, inputTaxReason, vatAmount) + .finish(); + controller.bookingRepository.save(inputTaxBooking); + } + } + } +} + class ScheduledCronjob { public final Calendar executionTime; public final ICronjob cronjob; @@ -38,6 +226,30 @@ class CronjobController { @Autowired private final BackgroundJobRepository cronjobRepository = null; + @Autowired + final ArticleRepository articleRepository = null; + + @Autowired + final ArticleOfferRepository articleOfferRepository = null; + + @Autowired + final CustomerOderRepository customerOrderRepository = null; + + @Autowired + final BookingRepository bookingRepository = null; + + @Autowired + final BookingAccountEntryRepository bookingAccountEntryRepository = null; + + @Autowired + final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + final SupplierRepository supplierRepository = null; + + @Autowired + final SupplierOrderRepository supplierOrderRepository = null; + private static Map getCronjobs() { HashMap map = new HashMap<>(); @@ -49,7 +261,7 @@ class CronjobController { private ScheduledCronjob getNextCronjob() { Calendar currentTime = new GregorianCalendar(); - Iterable jobs = cronjobRepository.getAllJobs(); + Iterable jobs = cronjobRepository.findAll(); HashMap alreadyExecuted = new HashMap<>(); for (BackgroundJob job : jobs) { alreadyExecuted.put(job.jobName, job); @@ -100,7 +312,12 @@ class CronjobController { Thread.sleep(waitingTime); } - nextJob.cronjob.executeAt(nextJob.executionTime); + try { + nextJob.cronjob.executeAt(nextJob.executionTime, this); + } catch (Throwable t) { + log.error("Failed to execute cronjob " + nextJob.cronjob.getClass() + ":"); + t.printStackTrace(); + } nextJob.model.lastExecution = new Timestamp(nextJob.executionTime.getTimeInMillis()); cronjobRepository.save(nextJob.model); diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java index 04b3b8a..64f2931 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java @@ -16,10 +16,10 @@ public class BookingAccountEntry { public int newSumCent; - @ManyToOne(optional = true, cascade = CascadeType.ALL) + @ManyToOne(optional = true, cascade = CascadeType.MERGE) public User userAccount; - @ManyToOne(optional = true, cascade = CascadeType.ALL) + @ManyToOne(optional = true, cascade = CascadeType.MERGE) public Supplier supplierAccount; public boolean isMainAccount; @@ -46,6 +46,14 @@ public class BookingAccountEntry { return e; } + public static BookingAccountEntry newSupplier(Supplier supplier) { + BookingAccountEntry e = new BookingAccountEntry(); + e.supplierAccount = supplier; + e.newSumCent = 0; + + return e; + } + public static BookingAccountEntry newMain() { BookingAccountEntry e = new BookingAccountEntry(); e.isMainAccount = true; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java index 78e8ab5..3b93517 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java @@ -39,4 +39,8 @@ public class BookingReason { public BookingReason(CustomerPayment customerPayment) { this.customerPayment = customerPayment; } + + public BookingReason(SupplierOrder supplierOrder) { + this.supplierOrder = supplierOrder; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java index d842a3a..53f0d62 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java @@ -1,18 +1,21 @@ package org.hso.ecommerce.repos.booking; +import java.util.Optional; + import org.hso.ecommerce.entities.booking.BookingAccountEntry; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import java.util.Optional; - @Repository public interface BookingAccountEntryRepository extends JpaRepository { @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.user_account_id = :user ORDER BY e.id DESC LIMIT 1", nativeQuery = true) Optional getByUser(Long user); + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.supplier_account_id = :supplier ORDER BY e.id DESC LIMIT 1", nativeQuery = true) + Optional getBySupplier(Long supplier); + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.is_main_account = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true) Optional getByMain(); diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/cronjob/BackgroundJobRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/cronjob/BackgroundJobRepository.java index a3aad66..41163d0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/cronjob/BackgroundJobRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/cronjob/BackgroundJobRepository.java @@ -2,11 +2,9 @@ package org.hso.ecommerce.repos.cronjob; import org.hso.ecommerce.entities.cron.BackgroundJob; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @Repository public interface BackgroundJobRepository extends JpaRepository { - @Query(value = "SELECT * FROM background_jobs", nativeQuery = true) - Iterable getAllJobs(); + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java index 407f45c..2c332f5 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java @@ -2,10 +2,16 @@ package org.hso.ecommerce.repos.shop; import org.hso.ecommerce.entities.shop.CustomerOrder; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @Repository public interface CustomerOderRepository extends JpaRepository { + @Query("SELECT SUM(cop.quantity) FROM CustomerOrderPosition cop JOIN cop.order co WHERE cop.article.id = :articleId AND co.created >= :begin AND co.created < :end") + Integer countOrdersOfArticleInTimespan( + long articleId, java.sql.Timestamp begin, java.sql.Timestamp end + ); + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/ArticleOfferRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/ArticleOfferRepository.java new file mode 100644 index 0000000..41263ff --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/ArticleOfferRepository.java @@ -0,0 +1,8 @@ +package org.hso.ecommerce.repos.supplier; + +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ArticleOfferRepository extends JpaRepository { + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java new file mode 100644 index 0000000..9d15e02 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java @@ -0,0 +1,14 @@ +package org.hso.ecommerce.repos.supplier; + +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface SupplierOrderRepository extends JpaRepository { + + @Query("SELECT SUM(so.numberOfUnits) FROM SupplierOrder so JOIN so.ordered ao WHERE ao.articleNumber = :articleNumber AND so.delivered IS NULL") + Integer countUndeliveredReorders(String articleNumber); + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java new file mode 100644 index 0000000..2b81659 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java @@ -0,0 +1,10 @@ +package org.hso.ecommerce.repos.supplier; + +import org.hso.ecommerce.entities.supplier.Supplier; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SupplierRepository extends JpaRepository { + +} From ffb683bdd0891ef6c376add65ea00b10e7595145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Mon, 25 May 2020 17:19:25 +0200 Subject: [PATCH 109/203] Set the advertise flag if at least one supplier wants it to be set --- .../ecommerce/action/cronjob/UpdateOffersAction.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java index 3046f2f..f095381 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java @@ -29,6 +29,12 @@ public class UpdateOffersAction { public List finish() { HashMap availableOffers = mapOffers(); + + // Reset all advertise-flags first. They are set again below. + for (ArticleOffer offer : availableOffers.values()) { + offer.shouldBeAdvertised = false; + } + for (Entry cheapestOffer : cheapestOffer.entrySet()) { String manufacturer = cheapestOffer.getKey().manufacturer; String articleNumber = cheapestOffer.getKey().articleNumber; @@ -42,7 +48,11 @@ public class UpdateOffersAction { Article currentOfferedArticle = cheapestOffer.getValue().apiSupplier.findArticle(manufacturer, articleNumber); currentOffer.vatPercent = currentOfferedArticle.vatPercent; - currentOffer.shouldBeAdvertised = currentOfferedArticle.shouldBeAdvertised; + + // Set advertise-flag if any supplier wants it to be set + if (currentOfferedArticle.shouldBeAdvertised) { + currentOffer.shouldBeAdvertised = true; + } } return offers; } From 7c467c8641f0bfcc67e1eaba3049db013107e8d4 Mon Sep 17 00:00:00 2001 From: Danny Date: Wed, 27 May 2020 12:22:24 +0200 Subject: [PATCH 110/203] bugfix register --- .../java/org/hso/ecommerce/controller/RegisterController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index 00ade5d..a53e084 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -64,7 +64,7 @@ public class RegisterController { userRepository.save(newUser); // save newUser - return "login"; + return "redirect:/login"; } @GetMapping("/register") From 7c73f698651de80a5080c8d12aa5d24dbefc8324 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 18 May 2020 12:49:37 +0200 Subject: [PATCH 111/203] remove bonuspoints --- .../templates/fragments/customer.html | 2 +- .../templates/user/orders/index.html | 86 ++----------------- 2 files changed, 6 insertions(+), 82 deletions(-) diff --git a/prototype/src/main/resources/templates/fragments/customer.html b/prototype/src/main/resources/templates/fragments/customer.html index d47a06a..a8ef757 100644 --- a/prototype/src/main/resources/templates/fragments/customer.html +++ b/prototype/src/main/resources/templates/fragments/customer.html @@ -12,7 +12,7 @@ diff --git a/prototype/src/main/resources/templates/user/orders/index.html b/prototype/src/main/resources/templates/user/orders/index.html index a60e623..5e24e4a 100644 --- a/prototype/src/main/resources/templates/user/orders/index.html +++ b/prototype/src/main/resources/templates/user/orders/index.html @@ -42,10 +42,10 @@ Deutschland
    - +
    LieferstatusUnterwegs
    Vorraussichtliche Ankunft: TODO TODO TODO TODO
    Unterwegs
    Vorraussichtliche Ankunft:
    Angekommen
    Ankunft:
    @@ -79,94 +79,18 @@ - + - - - - - - -
    Artikel (Netto) 120,00 EUR
    Umsatzsteuer (19%) 42,00 EUR
    -

    Gesammtpreis

    -
    -

    240,79 EUR

    -
    -
    -
    -

    Bestellung vom 01.01.2020

    -
    - - - - - - - - - - - - - - - - - -
    LieferstatusAngekommen
    03.01.2020
    SendeverfolgungsnummerXE5140684351DE
    - Hans Maier
    - Hauptstraße 12
    - 74880 Musterstadt
    - Deutschland
    -
    Gutgeschriebene Bonuspunkte5
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -180,7 +104,7 @@

    Gesammtpreis

    BildNameMengePreis pro Artikel (Brutto)
    Billige Kamera 140,50 EUR
    Apfel 51,00 EUR
    PositionPreis
    Artikel (Netto) 20,00 EUR
    Umsatzsteuer (19%) 5,00 EUR
    -

    50,79 EUR

    +

    240,79 EUR

    From 106b00a9074278ef63ed22d74aa640d49beed8f6 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 18 May 2020 14:41:33 +0200 Subject: [PATCH 112/203] show usersettings from db --- .../ecommerce/app/UserRequestController.java | 36 -------------- .../ecommerce/controller/UserController.java | 49 ++++++++++++++++++- .../org/hso/ecommerce/entities/user/User.java | 6 +++ .../resources/templates/user/settings.html | 21 ++++---- 4 files changed, 63 insertions(+), 49 deletions(-) delete mode 100644 prototype/src/main/java/org/hso/ecommerce/app/UserRequestController.java diff --git a/prototype/src/main/java/org/hso/ecommerce/app/UserRequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/UserRequestController.java deleted file mode 100644 index c4f6301..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/app/UserRequestController.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.hso.ecommerce.app; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; - -@Controller -@RequestMapping("user") -public class UserRequestController { - - @GetMapping("/") - public String user() { - return "redirect:/user/settings"; - } - - @GetMapping("/settings") - public String userSettings() { - return "user/settings"; - } - - @GetMapping("/orders/") - public String userOrdeers() { - return "user/orders/index"; - } - - @GetMapping("/bonuspoints") - public String userBonuspoints() { - return "user/bonuspoints"; - } - - @GetMapping("/notifications/") - public String userNotifications() { - return "user/notifications/index"; - } -} - diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 523bf2e..c462bd0 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -1,8 +1,55 @@ package org.hso.ecommerce.controller; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpSession; @Controller -//@RequestMapping("...") +@RequestMapping("/user") public class UserController { + + @Autowired + private final UserRepository userRepository = null; + + @GetMapping("/") + public String user() { + return "redirect:/user/settings"; + } + + @GetMapping("/settings") + public String userSettings(Model model, + HttpSession session + ) { + long userId = (long) session.getAttribute("userId"); + User user = userRepository.findById(userId).get(); + model.addAttribute("user", user); + + //TODO: klären wegen Geschäftskundenunterscheidung + + return "user/settings"; + } + + @GetMapping("/notifications/") + public String userNotifications() { + return "user/notifications/index"; + } + + @GetMapping("/orders/") + public String userOrdeers() { + return "user/orders/index"; + } + +// @GetMapping("/bonuspoints") +// public String userBonuspoints() { +// return "user/bonuspoints"; +// } + + + } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index 784d89a..c36b629 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -24,10 +24,16 @@ public class User { @Column(unique = true) public String email; + @Column(insertable=false, updatable = false) + public String name; + + public String salutation; + public String passwordHash; public boolean isActive; public boolean isEmployee; + public boolean isAdvertisementActivated; @Embedded public Address defaultDeliveryAddress; diff --git a/prototype/src/main/resources/templates/user/settings.html b/prototype/src/main/resources/templates/user/settings.html index 29f6ec4..682a3bb 100644 --- a/prototype/src/main/resources/templates/user/settings.html +++ b/prototype/src/main/resources/templates/user/settings.html @@ -27,7 +27,7 @@
    - +
    @@ -59,7 +59,7 @@
    -
    -
    @@ -78,17 +78,14 @@
    + placeholder="Optional: Zusatz Optional: Unternehmen Straße Hausnummer Postleitzeit Ort Land" th:text="${user.defaultDeliveryAddress.addressString}"/>
    -
    +
    @@ -100,9 +97,9 @@ Deutschland
    - +
    - +
    @@ -115,7 +112,7 @@ Deutschland
    - +
    From b5495bda3d5c521c5a42d0a979b9935824be331d Mon Sep 17 00:00:00 2001 From: Hannes Date: Tue, 19 May 2020 11:52:41 +0200 Subject: [PATCH 113/203] show all Orders --- .../action/user/ChangeUserAction.java | 7 +++ .../ecommerce/controller/UserController.java | 59 ++++++++++++++++++- .../shop/ShopCheckoutController.java | 4 +- .../entities/shop/CustomerOrder.java | 13 ++++ .../entities/shop/CustomerOrderPosition.java | 4 ++ .../org/hso/ecommerce/entities/user/User.java | 4 ++ .../repos/shop/CustomerOderRepository.java | 11 ---- .../repos/shop/CustomerOrderRepository.java | 17 ++++++ .../templates/user/orders/index.html | 46 +++++---------- .../resources/templates/user/settings.html | 2 +- 10 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java delete mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java new file mode 100644 index 0000000..af9ef9e --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/ChangeUserAction.java @@ -0,0 +1,7 @@ +package org.hso.ecommerce.action.user; + +import org.hso.ecommerce.entities.user.User; + +public class ChangeUserAction { + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index c462bd0..fee6946 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -1,14 +1,19 @@ package org.hso.ecommerce.controller; +import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpSession; +import java.util.List; @Controller @RequestMapping("/user") @@ -17,6 +22,9 @@ public class UserController { @Autowired private final UserRepository userRepository = null; + @Autowired + private final CustomerOrderRepository customerOrderRepository = null; + @GetMapping("/") public String user() { return "redirect:/user/settings"; @@ -37,14 +45,63 @@ public class UserController { @GetMapping("/notifications/") public String userNotifications() { + + //TODO: implement this + return "user/notifications/index"; } @GetMapping("/orders/") - public String userOrdeers() { + public String userOrdeers(HttpSession session, + Model model) { + + List orders = customerOrderRepository.getOrdersByUserId((long) session.getAttribute("userId")); + + model.addAttribute("orders", orders); + return "user/orders/index"; } + @PostMapping("/settings/changeMail") + public String changeMail(@RequestParam("email") String email){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changePwd") + public String changePwd(){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changeAddress") + public String changeAddress(){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changeAdSettings") + public String changeAdSettings(){ + + //TODO: implement this + + return "user/settings"; + } + + @PostMapping("/settings/changePaymentInfo") + public String changePaymentInfo(){ + + //TODO: implement this + + return "user/settings"; + } + // @GetMapping("/bonuspoints") // public String userBonuspoints() { // return "user/bonuspoints"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index c2c1c3a..e9d9e68 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -11,7 +11,7 @@ import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.shop.ArticleRepository; -import org.hso.ecommerce.repos.shop.CustomerOderRepository; +import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.user.UserRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; @@ -45,7 +45,7 @@ public class ShopCheckoutController { private final WarehouseBookingRepository warehouseBookingRepository = null; @Autowired - private final CustomerOderRepository customerOderRepository = null; + private final CustomerOrderRepository customerOderRepository = null; @Autowired private final WarehouseBookingPositionSlotEntryRepository wbeseRepo = null; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java index 1b20bb2..d9410f7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java @@ -4,6 +4,7 @@ import org.hso.ecommerce.entities.user.User; import javax.persistence.*; import javax.validation.constraints.NotNull; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @@ -43,4 +44,16 @@ public class CustomerOrder { public int totalNetCent; public int totalGrossCent; public int totalVatCent; + + public String formatInDeliverySince(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(inDeliverySince); + } + + public String formatCreated(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(created); + } + + public String formatDeliveredAt(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt); + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java index dbe53f7..2569158 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java @@ -19,4 +19,8 @@ public class CustomerOrderPosition { public int pricePerUnit; public int quantity; + + public int getSumPrice(){ + return article.getPriceGross() * quantity; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index c36b629..40196c7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -45,6 +45,10 @@ public class User { return id; } + public void setEmail(String email) { + this.email = email; + } + public boolean validatePassword(String password) { return BCrypt.checkpw(password, passwordHash); } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java deleted file mode 100644 index 407f45c..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOderRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.hso.ecommerce.repos.shop; - -import org.hso.ecommerce.entities.shop.CustomerOrder; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface CustomerOderRepository extends JpaRepository { - -} - diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java new file mode 100644 index 0000000..da42f26 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java @@ -0,0 +1,17 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CustomerOrderRepository extends JpaRepository { + + @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId") + List getOrdersByUserId(long userId); + +} + diff --git a/prototype/src/main/resources/templates/user/orders/index.html b/prototype/src/main/resources/templates/user/orders/index.html index 5e24e4a..b746f52 100644 --- a/prototype/src/main/resources/templates/user/orders/index.html +++ b/prototype/src/main/resources/templates/user/orders/index.html @@ -21,26 +21,22 @@
    -
    -

    Bestellung vom 27.01.2020

    +
    +

    - + + - + - + -
  • Benachrichtigungen
  • Bestellungen
  • diff --git a/prototype/src/main/resources/templates/fragments/header.html b/prototype/src/main/resources/templates/fragments/header.html index 054c332..349f72c 100644 --- a/prototype/src/main/resources/templates/fragments/header.html +++ b/prototype/src/main/resources/templates/fragments/header.html @@ -18,7 +18,6 @@ - +

    Rechungs- und Lieferinformation

    @@ -91,7 +91,7 @@ - +

    Werbung

    @@ -106,13 +106,13 @@ - +

    Zahlungsinformation

    - +
    From 5b4c0be1c8fa6d30467137e9b4b755ab515ab436 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 20 May 2020 12:08:57 +0200 Subject: [PATCH 116/203] add Errorhandling to UpdateSettingsAction --- .../action/user/UpdateUserSettingsAction.java | 61 +++++++++++-------- .../ecommerce/controller/UserController.java | 55 +++++++++++------ 2 files changed, 74 insertions(+), 42 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 57cbfce..fe4805a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.action.user; import com.sun.xml.bind.v2.TODO; +import org.hibernate.sql.Update; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -15,49 +16,59 @@ public class UpdateUserSettingsAction { this.repository = repository; } - public void updateEmail(String newMail){ - if(newMail.equals("")){ - //TODO: Errorhandling + public UpdateResult updateEmail(String newMail){ + UpdateResult result = new UpdateResult(false); + if(!newMail.contains("@")){ + result.errorString = "Ändern der Email-Addresse nicht möglich. Bitte versuchen Sie es erneut."; }else{ this.user.email = newMail; - this.repository.save(this.user); //TODO: Errorhandling + this.repository.save(this.user); + result.updated = true; } + return result; } - public void updatePassword(String oldPassword, String password1, String password2){ + public UpdateResult updatePassword(String oldPassword, String password1, String password2){ + UpdateResult result = new UpdateResult(false); if(this.user.validatePassword(oldPassword)) { if(password1.equals(password2)){ this.user.setPassword(password1); this.repository.save(this.user); + result.updated = true; }else{ - //TODO Errorhandling + result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; } }else{ - //TODO: Errorhandling + result.errorString = "Das eingegebene alte Passwort stimmt nicht mit dem momentan gespeicherten Passwort überein. Bitte versuchen Sie es erneut."; } + return result; } - public void updateShippingInfo(String salutation, String name, String address){ - if(salutation.equals("") || name.equals("") || address.equals("")){ - //TODO: Errorhandling - }else{ - this.user.salutation = salutation; - this.user.name = name; - this.user.defaultDeliveryAddress.addressString = address; + public UpdateResult updateShippingInfo(String salutation, String name, String address){ + this.user.salutation = salutation; + this.user.name = name; + this.user.defaultDeliveryAddress.addressString = address; + this.repository.save(this.user); + return new UpdateResult(true); + } + + public UpdateResult updateAdvertisementFlag(boolean advertisementFlag){ + this.user.isAdvertisementActivated = advertisementFlag; + this.repository.save(this.user); + return new UpdateResult(true); + } + + public UpdateResult updatePaymentInfo(String creditCardNumber){ + UpdateResult result = new UpdateResult(false); + if(creditCardNumber.matches("[0-9]+")){ + this.user.defaultPayment.creditCardNumber = creditCardNumber; this.repository.save(this.user); + result.updated = true; + }else{ + result.errorString = "Kreditkartennummer enthält Buchstaben. Bitte versuchen Sie es erneut."; } - } - - public void updateAdvertisementFlag(boolean advertisementFlag){ - this.user.isAdvertisementActivated = advertisementFlag; //TODO: Errodhandling - this.repository.save(this.user); - } - - public void updatePaymentInfo(String creditCardNumber){ - //TODO: Errorhandling - this.user.defaultPayment.creditCardNumber = creditCardNumber; - this.repository.save(this.user); + return result; } public class UpdateResult{ diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 94d7618..803dbf9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.List; @@ -46,9 +47,9 @@ public class UserController { @GetMapping("/orders/") public String userOrdeers(HttpSession session, - Model model) { + Model model + ) { List orders = customerOrderRepository.getOrdersByUserId((long) session.getAttribute("userId")); - model.addAttribute("orders", orders); return "user/orders/index"; @@ -56,12 +57,16 @@ public class UserController { @PostMapping("/settings/changeMail") public String changeMail(HttpSession session, - @RequestParam("email") String email - ){ + @RequestParam("email") String email, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updateEmail(email); + UpdateUserSettingsAction.UpdateResult result = cusa.updateEmail(email); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @@ -70,12 +75,16 @@ public class UserController { public String changePwd(HttpSession session, @RequestParam("old-password") String oldPassword, @RequestParam("password1") String password1, - @RequestParam("password2") String password2 - ){ + @RequestParam("password2") String password2, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updatePassword(oldPassword, password1, password2); + UpdateUserSettingsAction.UpdateResult result = cusa.updatePassword(oldPassword, password1, password2); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @@ -84,36 +93,48 @@ public class UserController { public String changeAddress(HttpSession session, @RequestParam("salutation") String salutation, @RequestParam("name") String name, - @RequestParam("address") String address - ){ + @RequestParam("address") String address, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updateShippingInfo(salutation, name, address); + UpdateUserSettingsAction.UpdateResult result = cusa.updateShippingInfo(salutation, name, address); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @PostMapping("/settings/changeAdSettings") public String changeAdSettings(HttpSession session, - @RequestParam("ad") String ad - ){ + @RequestParam("ad") String ad, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updateAdvertisementFlag(ad.equals("y")); + UpdateUserSettingsAction.UpdateResult result = cusa.updateAdvertisementFlag(ad.equals("y")); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } @PostMapping("/settings/changePaymentInfo") public String changePaymentInfo(HttpSession session, - @RequestParam("creditCardNumber") String creditCardNumber - ){ + @RequestParam("creditCardNumber") String creditCardNumber, + HttpServletRequest request + ) { User user = userRepository.findById((long) session.getAttribute("userId")).get(); UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - cusa.updatePaymentInfo(creditCardNumber); + UpdateUserSettingsAction.UpdateResult result = cusa.updatePaymentInfo(creditCardNumber); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + } return "user/settings"; } From 83ad1f399924df53192e7f1630ce98f38de64b0f Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 09:36:40 +0200 Subject: [PATCH 117/203] remove advertisementflag --- .../action/user/UpdateUserSettingsAction.java | 6 ------ .../hso/ecommerce/controller/UserController.java | 16 ---------------- .../org/hso/ecommerce/entities/user/User.java | 1 - .../main/resources/templates/user/settings.html | 15 --------------- 4 files changed, 38 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index fe4805a..5732a7f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -53,12 +53,6 @@ public class UpdateUserSettingsAction { return new UpdateResult(true); } - public UpdateResult updateAdvertisementFlag(boolean advertisementFlag){ - this.user.isAdvertisementActivated = advertisementFlag; - this.repository.save(this.user); - return new UpdateResult(true); - } - public UpdateResult updatePaymentInfo(String creditCardNumber){ UpdateResult result = new UpdateResult(false); if(creditCardNumber.matches("[0-9]+")){ diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 803dbf9..5b0c50b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -107,22 +107,6 @@ public class UserController { return "user/settings"; } - @PostMapping("/settings/changeAdSettings") - public String changeAdSettings(HttpSession session, - @RequestParam("ad") String ad, - HttpServletRequest request - ) { - User user = userRepository.findById((long) session.getAttribute("userId")).get(); - - UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); - UpdateUserSettingsAction.UpdateResult result = cusa.updateAdvertisementFlag(ad.equals("y")); - if (result.updated == false) { - request.setAttribute("error", result.errorString); - } - - return "user/settings"; - } - @PostMapping("/settings/changePaymentInfo") public String changePaymentInfo(HttpSession session, @RequestParam("creditCardNumber") String creditCardNumber, diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index 40196c7..61a4503 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -33,7 +33,6 @@ public class User { public boolean isActive; public boolean isEmployee; - public boolean isAdvertisementActivated; @Embedded public Address defaultDeliveryAddress; diff --git a/prototype/src/main/resources/templates/user/settings.html b/prototype/src/main/resources/templates/user/settings.html index 89e81fb..86f25e3 100644 --- a/prototype/src/main/resources/templates/user/settings.html +++ b/prototype/src/main/resources/templates/user/settings.html @@ -91,21 +91,6 @@ -
    -
    -

    Werbung

    -
    -
    -
    - -
    - -
    -
    - -
    - -

    Zahlungsinformation

    From 60699ed847a7a34283404786535df4f772f1d8a7 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 10:10:42 +0200 Subject: [PATCH 118/203] fix error handling --- .../action/user/UpdateUserSettingsAction.java | 12 ++++++++---- .../org/hso/ecommerce/controller/UserController.java | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 5732a7f..9aa8370 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -33,9 +33,13 @@ public class UpdateUserSettingsAction { if(this.user.validatePassword(oldPassword)) { if(password1.equals(password2)){ - this.user.setPassword(password1); - this.repository.save(this.user); - result.updated = true; + if(!password1.equals(oldPassword)){ + this.user.setPassword(password1); + this.repository.save(this.user); + result.updated = true; + }else { + result.errorString = "Die neuen Passwörter entsprechen dem alten Passwort."; + } }else{ result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; } @@ -60,7 +64,7 @@ public class UpdateUserSettingsAction { this.repository.save(this.user); result.updated = true; }else{ - result.errorString = "Kreditkartennummer enthält Buchstaben. Bitte versuchen Sie es erneut."; + result.errorString = "Kreditkartennummer darf nur Zahlen enthalten. Bitte versuchen Sie es erneut."; } return result; } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 5b0c50b..7099dd7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -66,9 +66,10 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updateEmail(email); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } @PostMapping("/settings/changePwd") @@ -84,9 +85,10 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updatePassword(oldPassword, password1, password2); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } @PostMapping("/settings/changeAddress") @@ -102,9 +104,10 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updateShippingInfo(salutation, name, address); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } @PostMapping("/settings/changePaymentInfo") @@ -118,8 +121,9 @@ public class UserController { UpdateUserSettingsAction.UpdateResult result = cusa.updatePaymentInfo(creditCardNumber); if (result.updated == false) { request.setAttribute("error", result.errorString); + return "user/settings"; } - return "user/settings"; + return "redirect:/user/settings"; } } From 614a9295921c26e326385bf4c57bcaa73190b6d4 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 10:21:36 +0200 Subject: [PATCH 119/203] code cleanup --- .../action/user/UpdateUserSettingsAction.java | 40 +++++++++---------- .../org/hso/ecommerce/entities/user/User.java | 6 +-- .../repos/shop/CustomerOrderRepository.java | 3 +- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 9aa8370..4796dbe 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -1,26 +1,23 @@ package org.hso.ecommerce.action.user; -import com.sun.xml.bind.v2.TODO; -import org.hibernate.sql.Update; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; public class UpdateUserSettingsAction { private User user; private UserRepository repository; - public UpdateUserSettingsAction(User user, UserRepository repository){ + public UpdateUserSettingsAction(User user, UserRepository repository) { this.user = user; this.repository = repository; } - public UpdateResult updateEmail(String newMail){ + public UpdateResult updateEmail(String newMail) { UpdateResult result = new UpdateResult(false); - if(!newMail.contains("@")){ + if (!newMail.contains("@")) { result.errorString = "Ändern der Email-Addresse nicht möglich. Bitte versuchen Sie es erneut."; - }else{ + } else { this.user.email = newMail; this.repository.save(this.user); result.updated = true; @@ -28,28 +25,27 @@ public class UpdateUserSettingsAction { return result; } - public UpdateResult updatePassword(String oldPassword, String password1, String password2){ + public UpdateResult updatePassword(String oldPassword, String password1, String password2) { UpdateResult result = new UpdateResult(false); - if(this.user.validatePassword(oldPassword)) - { - if(password1.equals(password2)){ - if(!password1.equals(oldPassword)){ + if (this.user.validatePassword(oldPassword)) { + if (password1.equals(password2)) { + if (!password1.equals(oldPassword)) { this.user.setPassword(password1); this.repository.save(this.user); result.updated = true; - }else { + } else { result.errorString = "Die neuen Passwörter entsprechen dem alten Passwort."; } - }else{ + } else { result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; } - }else{ + } else { result.errorString = "Das eingegebene alte Passwort stimmt nicht mit dem momentan gespeicherten Passwort überein. Bitte versuchen Sie es erneut."; } return result; } - public UpdateResult updateShippingInfo(String salutation, String name, String address){ + public UpdateResult updateShippingInfo(String salutation, String name, String address) { this.user.salutation = salutation; this.user.name = name; this.user.defaultDeliveryAddress.addressString = address; @@ -57,28 +53,28 @@ public class UpdateUserSettingsAction { return new UpdateResult(true); } - public UpdateResult updatePaymentInfo(String creditCardNumber){ + public UpdateResult updatePaymentInfo(String creditCardNumber) { UpdateResult result = new UpdateResult(false); - if(creditCardNumber.matches("[0-9]+")){ + if (creditCardNumber.matches("[0-9]+")) { this.user.defaultPayment.creditCardNumber = creditCardNumber; this.repository.save(this.user); result.updated = true; - }else{ + } else { result.errorString = "Kreditkartennummer darf nur Zahlen enthalten. Bitte versuchen Sie es erneut."; } return result; } - public class UpdateResult{ + public class UpdateResult { public boolean updated; //if true worked, if false not worked public String errorString; - public UpdateResult(boolean updated, String errorString){ + public UpdateResult(boolean updated, String errorString) { this.updated = updated; this.errorString = errorString; } - public UpdateResult(boolean updated){ + public UpdateResult(boolean updated) { this.updated = updated; this.errorString = ""; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java index 61a4503..e08c2ee 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -24,7 +24,7 @@ public class User { @Column(unique = true) public String email; - @Column(insertable=false, updatable = false) + @Column(insertable = false, updatable = false) public String name; public String salutation; @@ -44,10 +44,6 @@ public class User { return id; } - public void setEmail(String email) { - this.email = email; - } - public boolean validatePassword(String password) { return BCrypt.checkpw(password, passwordHash); } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java index da42f26..3321232 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java @@ -13,5 +13,4 @@ public interface CustomerOrderRepository extends JpaRepository getOrdersByUserId(long userId); -} - +} \ No newline at end of file From 846e448f01be5a89ca8968f65b6b4cea420472c2 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 25 May 2020 12:17:09 +0200 Subject: [PATCH 120/203] code cleanup --- .../java/org/hso/ecommerce/controller/UserController.java | 2 -- .../java/org/hso/ecommerce/entities/shop/CustomerOrder.java | 5 +++++ .../src/main/resources/templates/user/orders/index.html | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 7099dd7..8a90d43 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -40,8 +40,6 @@ public class UserController { User user = userRepository.findById(userId).get(); model.addAttribute("user", user); - //TODO: klären wegen Geschäftskundenunterscheidung - return "user/settings"; } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java index d9410f7..8b703b6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java @@ -56,4 +56,9 @@ public class CustomerOrder { public String formatDeliveredAt(){ return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt); } + + public String getEstimatedArrival() { + //TODO: get estimated arrival from api + return "TODO TODO TODO"; + } } diff --git a/prototype/src/main/resources/templates/user/orders/index.html b/prototype/src/main/resources/templates/user/orders/index.html index b746f52..327bbf2 100644 --- a/prototype/src/main/resources/templates/user/orders/index.html +++ b/prototype/src/main/resources/templates/user/orders/index.html @@ -27,7 +27,7 @@
    LieferstatusUnterwegs
    Vorraussichtliche Ankunft: 29.01.2020
    Unterwegs
    Vorraussichtliche Ankunft: TODO TODO TODO TODO
    Angekommen
    Ankunft:
    SendeverfolgungsnummerXE51451436DE
    - Hans Maier
    - Hauptstraße 12
    - 74880 Musterstadt
    - Deutschland
    -
    - + From 42a02effa66c20e9cd1d8fb165d63b746102659d Mon Sep 17 00:00:00 2001 From: Philipp Schweizer Date: Wed, 27 May 2020 14:24:05 +0200 Subject: [PATCH 121/203] fixed pull request #25 --- delivery/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 delivery/gradle/wrapper/gradle-wrapper.jar diff --git a/delivery/gradle/wrapper/gradle-wrapper.jar b/delivery/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/delivery/gradle/wrapper/gradle-wrapper.properties b/delivery/gradle/wrapper/gradle-wrapper.properties index 81f92f8..feb1126 100644 --- a/delivery/gradle/wrapper/gradle-wrapper.properties +++ b/delivery/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Tue May 19 15:50:06 CEST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists From db1d3d31b22b1cf143f95b1cefb4e87b1706cd9c Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 27 May 2020 14:55:51 +0200 Subject: [PATCH 122/203] fix register --- .../controller/RegisterController.java | 6 ++---- .../main/resources/templates/register.html | 19 ------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index a53e084..4ca8ac8 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -28,9 +28,7 @@ public class RegisterController { @RequestParam("password2") String password2, @RequestParam("salutation") String salutation, @RequestParam("name") String name, - @RequestParam("address") String address, - @RequestParam("type") String type, - @RequestParam("ad") String ad + @RequestParam("address") String address ) { Optional user = userRepository.findByEmail(username); @@ -52,7 +50,7 @@ public class RegisterController { newUser.setPassword(password); newUser.email = username; newUser.isEmployee = false; - //TODO for salutation, type, ad are no attributes/fields in the class/database. Add when they are there. + newUser.salutation = salutation; newUser.isActive = true; newUser.created = new java.sql.Timestamp(System.currentTimeMillis()); diff --git a/prototype/src/main/resources/templates/register.html b/prototype/src/main/resources/templates/register.html index 078191f..fccb016 100644 --- a/prototype/src/main/resources/templates/register.html +++ b/prototype/src/main/resources/templates/register.html @@ -63,25 +63,6 @@ -

    - -
    - -
    -
    - -
    -

    Werbung

    -
    -
    -
    - -
    - -
    -
    -
    -
    From e5b28250c260e07fc08c3e7ef7f1625e3c3a4b98 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 27 May 2020 14:56:15 +0200 Subject: [PATCH 123/203] fix payment method null pointer --- .../hso/ecommerce/action/user/UpdateUserSettingsAction.java | 3 ++- .../java/org/hso/ecommerce/controller/UserController.java | 4 ++++ .../hso/ecommerce/controller/shop/ShopCheckoutController.java | 2 +- .../org/hso/ecommerce/entities/booking/PaymentMethod.java | 2 +- prototype/src/main/resources/templates/user/settings.html | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 4796dbe..91b3796 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -1,5 +1,6 @@ package org.hso.ecommerce.action.user; +import org.hso.ecommerce.entities.booking.PaymentMethod; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; @@ -56,7 +57,7 @@ public class UpdateUserSettingsAction { public UpdateResult updatePaymentInfo(String creditCardNumber) { UpdateResult result = new UpdateResult(false); if (creditCardNumber.matches("[0-9]+")) { - this.user.defaultPayment.creditCardNumber = creditCardNumber; + this.user.defaultPayment = PaymentMethod.fromCreditCardNumber(creditCardNumber); this.repository.save(this.user); result.updated = true; } else { diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 8a90d43..ff901ba 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.controller; import org.hso.ecommerce.action.user.UpdateUserSettingsAction; +import org.hso.ecommerce.entities.booking.PaymentMethod; import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.shop.CustomerOrderRepository; @@ -38,6 +39,9 @@ public class UserController { ) { long userId = (long) session.getAttribute("userId"); User user = userRepository.findById(userId).get(); + if(user.defaultPayment == null){ + user.defaultPayment = PaymentMethod.fromCreditCardNumber(""); + } model.addAttribute("user", user); return "user/settings"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java index e9d9e68..d56da9c 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopCheckoutController.java @@ -122,7 +122,7 @@ public class ShopCheckoutController { user, expectedPrice, Address.fromString(address), - PaymentMethod.fromCreditCarNumber(cardnumber), + PaymentMethod.fromCreditCardNumber(cardnumber), bookingEntryRepository.getByUser(user.id).orElse(BookingAccountEntry.newUser(user)), bookingEntryRepository.getByVat().orElse(BookingAccountEntry.newVat()), bookingEntryRepository.getByMain().orElse(BookingAccountEntry.newMain()) diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java index af0a9c0..fa9fe62 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java @@ -7,7 +7,7 @@ public class PaymentMethod { public String creditCardNumber; - public static PaymentMethod fromCreditCarNumber(String cardnumber) { + public static PaymentMethod fromCreditCardNumber(String cardnumber) { PaymentMethod m = new PaymentMethod(); m.creditCardNumber = cardnumber; diff --git a/prototype/src/main/resources/templates/user/settings.html b/prototype/src/main/resources/templates/user/settings.html index 86f25e3..13e336f 100644 --- a/prototype/src/main/resources/templates/user/settings.html +++ b/prototype/src/main/resources/templates/user/settings.html @@ -97,7 +97,7 @@
    - +
    From 9406bbc4ac38b7b027c71c1662c2958f1bb01b00 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 27 May 2020 15:10:12 +0200 Subject: [PATCH 124/203] fix nullpointer again --- .../java/org/hso/ecommerce/controller/RegisterController.java | 2 ++ .../java/org/hso/ecommerce/controller/UserController.java | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index 4ca8ac8..128ca3a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -1,5 +1,6 @@ package org.hso.ecommerce.controller; +import org.hso.ecommerce.entities.booking.PaymentMethod; import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.user.UserRepository; @@ -51,6 +52,7 @@ public class RegisterController { newUser.email = username; newUser.isEmployee = false; newUser.salutation = salutation; + newUser.defaultPayment = PaymentMethod.fromCreditCardNumber(""); newUser.isActive = true; newUser.created = new java.sql.Timestamp(System.currentTimeMillis()); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index ff901ba..8a90d43 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -1,7 +1,6 @@ package org.hso.ecommerce.controller; import org.hso.ecommerce.action.user.UpdateUserSettingsAction; -import org.hso.ecommerce.entities.booking.PaymentMethod; import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.shop.CustomerOrderRepository; @@ -39,9 +38,6 @@ public class UserController { ) { long userId = (long) session.getAttribute("userId"); User user = userRepository.findById(userId).get(); - if(user.defaultPayment == null){ - user.defaultPayment = PaymentMethod.fromCreditCardNumber(""); - } model.addAttribute("user", user); return "user/settings"; From e4d6642d61dd34efef12dc369ba843d96a20ac30 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 27 May 2020 15:52:51 +0200 Subject: [PATCH 125/203] order orders by newest first --- .../org/hso/ecommerce/repos/shop/CustomerOrderRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java index 3321232..8877706 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java @@ -10,7 +10,7 @@ import java.util.List; @Repository public interface CustomerOrderRepository extends JpaRepository { - @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId") + @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId ORDER BY co.id DESC") List getOrdersByUserId(long userId); } \ No newline at end of file From 8a2eff1fe9c41c1cec4d2cae47c74aed6a8bc398 Mon Sep 17 00:00:00 2001 From: Hannes Date: Wed, 27 May 2020 19:03:18 +0200 Subject: [PATCH 126/203] code cleanup --- .../main/resources/templates/user/orders/index.html | 10 ---------- .../src/main/resources/templates/user/settings.html | 6 ------ 2 files changed, 16 deletions(-) diff --git a/prototype/src/main/resources/templates/user/orders/index.html b/prototype/src/main/resources/templates/user/orders/index.html index 327bbf2..023910d 100644 --- a/prototype/src/main/resources/templates/user/orders/index.html +++ b/prototype/src/main/resources/templates/user/orders/index.html @@ -38,10 +38,6 @@
    -
    LieferstatusUnterwegs
    Vorraussichtliche Ankunft: TODO TODO TODO TODO
    Unterwegs
    Vorraussichtliche Ankunft:
    Angekommen
    Ankunft:
    @@ -69,12 +65,6 @@ - diff --git a/prototype/src/main/resources/templates/user/settings.html b/prototype/src/main/resources/templates/user/settings.html index 13e336f..c9a2199 100644 --- a/prototype/src/main/resources/templates/user/settings.html +++ b/prototype/src/main/resources/templates/user/settings.html @@ -80,12 +80,6 @@ +

    @@ -93,7 +93,7 @@

    -

    From 35861d36648802be421a5edf094bfb05f0408ac9 Mon Sep 17 00:00:00 2001 From: localhorst Date: Wed, 27 May 2020 21:53:58 +0200 Subject: [PATCH 129/203] fixed styling for edit/add btn --- .../main/resources/templates/intern/listedArticles/index.html | 2 +- .../main/resources/templates/intern/offeredArticles/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/resources/templates/intern/listedArticles/index.html b/prototype/src/main/resources/templates/intern/listedArticles/index.html index ad33f89..ff25abb 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/index.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/index.html @@ -56,7 +56,7 @@
    diff --git a/prototype/src/main/resources/templates/intern/offeredArticles/index.html b/prototype/src/main/resources/templates/intern/offeredArticles/index.html index 7a18ccc..79d926a 100644 --- a/prototype/src/main/resources/templates/intern/offeredArticles/index.html +++ b/prototype/src/main/resources/templates/intern/offeredArticles/index.html @@ -54,7 +54,7 @@
    - +
    From d5540c19797a8d30b26eaa87c13cc06bb9bafd87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Thu, 28 May 2020 10:58:11 +0200 Subject: [PATCH 130/203] Small changes according to comments on the pullrequest --- .../action/cronjob/ReorderAction.java | 22 +++++-------------- .../controller/cronjob/CronjobController.java | 9 +++----- .../supplier/ConfigurationReader.java | 2 +- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java index 2bad0e0..23599d8 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java @@ -2,7 +2,6 @@ package org.hso.ecommerce.action.cronjob; import java.sql.Timestamp; import java.util.HashMap; -import java.util.List; import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer; @@ -12,7 +11,6 @@ import org.hso.ecommerce.api.data.OrderConfirmation; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.supplier.ArticleOffer; import org.hso.ecommerce.entities.supplier.SupplierOrder; -import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,21 +20,21 @@ public class ReorderAction { private Article article; private Integer[] orderedAmounts; private Integer undeliveredReorders; - private List inStock; + private int amountInStock; private HashMap cheapestOffer; private HashMap articleOffers; public ReorderAction( Article article, Integer[] orderedAmounts, Integer undeliveredReorders, - List inStock, + int amountInStock, HashMap cheapestOffer, HashMap articleOffers ) { this.article = article; this.orderedAmounts = orderedAmounts; this.undeliveredReorders = undeliveredReorders; - this.inStock = inStock; + this.amountInStock = amountInStock; this.cheapestOffer = cheapestOffer; this.articleOffers = articleOffers; } @@ -45,14 +43,6 @@ public class ReorderAction { return input == null ? 0 : input; } - private int getAmountInStock() { - int sum = 0; - for (WarehouseBookingPositionSlotEntry entry : inStock) { - sum += entry.newSumSlot; - } - return sum; - } - private int calculateAmountToReorder() { // Algorithm as described in the documentation int a = null_to_zero(orderedAmounts[0]); @@ -68,11 +58,9 @@ public class ReorderAction { } int i = null_to_zero(undeliveredReorders); - int l = getAmountInStock(); + int l = amountInStock; - int o = n - i - l; - - return o; + return n - i - l; } public SupplierOrder finish() { diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java index 143f1cf..e2bad65 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java @@ -24,7 +24,6 @@ import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.supplier.ArticleOffer; import org.hso.ecommerce.entities.supplier.Supplier; import org.hso.ecommerce.entities.supplier.SupplierOrder; -import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository; @@ -68,8 +67,6 @@ interface ICronjob { @Component class Reorder implements ICronjob { - private static final Logger log = LoggerFactory.getLogger(Reorder.class); - @Override public Calendar nextExecution(Calendar reference) { if (reference.get(Calendar.HOUR_OF_DAY) >= 8) { @@ -162,12 +159,12 @@ class Reorder implements ICronjob { Integer undeliveredReorders = controller.supplierOrderRepository .countUndeliveredReorders(article.related.articleNumber); - List inStock = controller.warehouseBookingPositionSlotEntryRepository - .getByArticle(article.id); + int amountInStock = controller.warehouseBookingPositionSlotEntryRepository.getArticleStock(article.id) + .orElse(0); ReorderAction action = new ReorderAction(article, orderedAmounts, undeliveredReorders, - inStock, + amountInStock, supplierData.cheapestOffer, mappedOffers); SupplierOrder order = action.finish(); if (order != null) { diff --git a/supplier/src/main/java/org/hso/ecommerce/supplier/ConfigurationReader.java b/supplier/src/main/java/org/hso/ecommerce/supplier/ConfigurationReader.java index f4e4e20..79782d6 100644 --- a/supplier/src/main/java/org/hso/ecommerce/supplier/ConfigurationReader.java +++ b/supplier/src/main/java/org/hso/ecommerce/supplier/ConfigurationReader.java @@ -25,7 +25,7 @@ public class ConfigurationReader { try { String jsonData = Files.readString(path, StandardCharsets.UTF_8); ObjectMapper objectMapper = new ObjectMapper(); - Supplier sup = (Supplier)objectMapper.readValue(jsonData, Supplier.class); + Supplier sup = objectMapper.readValue(jsonData, Supplier.class); System.out.println("Loaded " + sup.id); return sup; } catch (IOException e) { From 2bf383b113888ceecc78fb90e2e9eaa8d967cba8 Mon Sep 17 00:00:00 2001 From: Seil0 Date: Thu, 28 May 2020 13:20:42 +0200 Subject: [PATCH 131/203] fix html warnings --- .../templates/intern/listedArticles/id.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/prototype/src/main/resources/templates/intern/listedArticles/id.html b/prototype/src/main/resources/templates/intern/listedArticles/id.html index 874cfa8..135147b 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/id.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/id.html @@ -27,18 +27,18 @@

    - + -

    + Details

    - +

    @@ -47,13 +47,13 @@

    -  EUR
    +  EUR
    (19% Mwst.)
    =  EUR Brutto

    - +  EUR

    @@ -66,17 +66,17 @@
    - +

    Bitte jede Kategorien in eine eigene Zeile

    -

    - +

    @@ -88,7 +88,7 @@

    - Lagerbuchung + Lagerbuchung

    From f4b0d6a576fe674a52bfd8a498060e3252489bae Mon Sep 17 00:00:00 2001 From: Seil0 Date: Thu, 28 May 2020 13:22:28 +0200 Subject: [PATCH 132/203] fix html warnings [2] --- .../main/resources/templates/intern/listedArticles/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/resources/templates/intern/listedArticles/index.html b/prototype/src/main/resources/templates/intern/listedArticles/index.html index ff25abb..6986f0b 100644 --- a/prototype/src/main/resources/templates/intern/listedArticles/index.html +++ b/prototype/src/main/resources/templates/intern/listedArticles/index.html @@ -29,7 +29,7 @@

    @@ -53,7 +53,7 @@ - + - + - - +
    Artikel (Netto)
    - +
    Details
    + data-target-id="main-table"/>
    From 2c2b5c1d0fdfe5e2a79d5bf97ae91d4e3fc964cb Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Fri, 29 May 2020 16:12:28 +0200 Subject: [PATCH 133/203] Add Missing Gradle Folder to Repo --- supplier/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58695 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 supplier/gradle/wrapper/gradle-wrapper.jar create mode 100644 supplier/gradle/wrapper/gradle-wrapper.properties diff --git a/supplier/gradle/wrapper/gradle-wrapper.jar b/supplier/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f3d88b1c2faf2fc91d853cd5d4242b5547257070 GIT binary patch literal 58695 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 literal 0 HcmV?d00001 diff --git a/supplier/gradle/wrapper/gradle-wrapper.properties b/supplier/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e97d5ee --- /dev/null +++ b/supplier/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon May 11 17:28:28 CEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME From d55a50ab29b71598733df790a739c6992601a8e5 Mon Sep 17 00:00:00 2001 From: Seil0 Date: Fri, 29 May 2020 16:18:22 +0200 Subject: [PATCH 134/203] [supplier] update gradle to version 6.4 --- supplier/gradle/wrapper/gradle-wrapper.jar | Bin 58695 -> 58910 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 ++--- supplier/gradlew | 2 ++ supplier/gradlew.bat | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/supplier/gradle/wrapper/gradle-wrapper.jar b/supplier/gradle/wrapper/gradle-wrapper.jar index f3d88b1c2faf2fc91d853cd5d4242b5547257070..62d4c053550b91381bbd28b1afc82d634bf73a8a 100644 GIT binary patch delta 12524 zcmY*pqN z|ERss*;ReIR@Lg&r_R7IT-GRDbpR4NMKNcwFgy&*A|eco1PnBHRth?SBneiw6$UT&^LVJ0?Naa`KfV(>F!Ez}~S z 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 delta 12163 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 diff --git a/supplier/gradle/wrapper/gradle-wrapper.properties b/supplier/gradle/wrapper/gradle-wrapper.properties index e97d5ee..4c5803d 100644 --- a/supplier/gradle/wrapper/gradle-wrapper.properties +++ b/supplier/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon May 11 17:28:28 CEST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/supplier/gradlew b/supplier/gradlew index 2fe81a7..fbd7c51 100755 --- a/supplier/gradlew +++ b/supplier/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/supplier/gradlew.bat b/supplier/gradlew.bat index 9109989..a9f778a 100644 --- a/supplier/gradlew.bat +++ b/supplier/gradlew.bat @@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% From 286edb33ed48fe24fbc541ee6a13508a0093c85b Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 30 May 2020 11:52:30 +0200 Subject: [PATCH 135/203] show supplier name at offered article page --- .../suppliers/SupplierOfferController.java | 24 ++++++------------- .../entities/supplier/ArticleOffer.java | 3 ++- .../intern/offeredArticles/index.html | 2 +- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java index 3ef21bf..7d27059 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java @@ -30,7 +30,8 @@ public class SupplierOfferController { for (ArticleOffer article : offersRepository.findAll()) { UImodelOfferedArticle tmp = new UImodelOfferedArticle(); - tmp.addData(article, "supplierName01", 4884, articleRepository.findArticleIDByRelatedID(article.id)); //TODO display supplier name with link + tmp.addData(article, articleRepository.findArticleIDByRelatedID(article.id)); // TODO display supplier name + // with link totals.add(tmp); } @@ -40,16 +41,15 @@ public class SupplierOfferController { public class UImodelOfferedArticle { - long offer_id; + long offer_id; String title; String manufacturer; String articlenumber; String supplierName; - int supplierId; String price; String ads; int listedArticleId; - boolean offerIsListed; //true --> offered article is listed + boolean offerIsListed; // true --> offered article is listed public long getOffer_id() { return offer_id; @@ -58,7 +58,7 @@ public class SupplierOfferController { public void setOffer_id(long offer_id) { this.offer_id = offer_id; } - + public boolean isOfferIsListed() { return offerIsListed; } @@ -99,14 +99,6 @@ public class SupplierOfferController { this.supplierName = supplierName; } - public int getSupplierId() { - return supplierId; - } - - public void setSupplierId(int supplierId) { - this.supplierId = supplierId; - } - public String getPrice() { return price; } @@ -131,15 +123,13 @@ public class SupplierOfferController { this.listedArticleId = listedArticleId; } - public void addData(ArticleOffer article, String supplierName, int supplierId, - Optional listedArticleId) { + public void addData(ArticleOffer article, Optional listedArticleId) { this.offer_id = article.id; this.title = article.title; this.manufacturer = article.manufacturer; this.articlenumber = article.articleNumber; - this.supplierName = supplierName; - this.supplierId = supplierId; + this.supplierName = article.cheapestSupplier.name; this.price = String.format("%.2f", ((float) article.pricePerUnitNet / 100)); this.ads = (article.shouldBeAdvertised) ? "Ja" : "Nein"; diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java index 405fb4a..8c20099 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java @@ -28,5 +28,6 @@ public class ArticleOffer { public boolean shouldBeAdvertised; - + @ManyToOne(optional = false) + public Supplier cheapestSupplier; } diff --git a/prototype/src/main/resources/templates/intern/offeredArticles/index.html b/prototype/src/main/resources/templates/intern/offeredArticles/index.html index 79d926a..04d9a12 100644 --- a/prototype/src/main/resources/templates/intern/offeredArticles/index.html +++ b/prototype/src/main/resources/templates/intern/offeredArticles/index.html @@ -44,7 +44,7 @@

    From 1b2e75a82c29eeb7807179063f44cd56fadadb43 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 30 May 2020 12:29:40 +0200 Subject: [PATCH 136/203] updates scripts --- prototype/scripts/addarticles.sql | 13 +++++++++++-- prototype/scripts/addsupplier.sql | 6 ++++++ .../intern/suppliers/SupplierOfferController.java | 3 +-- 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 prototype/scripts/addsupplier.sql diff --git a/prototype/scripts/addarticles.sql b/prototype/scripts/addarticles.sql index b60497d..e81c174 100644 --- a/prototype/scripts/addarticles.sql +++ b/prototype/scripts/addarticles.sql @@ -1,6 +1,15 @@ -INSERT INTO article_offers ("manufacturer", "article_number", "price_per_unit_net", "title", "vat_percent", "should_be_advertised") -VALUES ("McDonalds", "1", 4242, "McPizza", 7, 1); +/* +* add a supplier first +*/ +INSERT INTO article_offers ("manufacturer", "article_number", "price_per_unit_net", "title", "vat_percent", "should_be_advertised", "cheapest_supplier_id") +VALUES ("McDonalds", "1", 4242, "McPizza", 7, 1, 1); + + + +/* +* There is no need for the add article, you can add one form the UI on the offerd article page +*/ INSERT INTO articles ("related_id", "shop_price_per_unit_net_cent", "warehouse_units_per_slot", "should_reorder", "reorder_max_price", "title", "description", "image_id") VALUES (1, 19.99, 10, 1, 15, "Huge Hamburger", "This huge Hamburger is awesome!", NULL); diff --git a/prototype/scripts/addsupplier.sql b/prototype/scripts/addsupplier.sql new file mode 100644 index 0000000..a56f4ed --- /dev/null +++ b/prototype/scripts/addsupplier.sql @@ -0,0 +1,6 @@ + +INSERT INTO suppliers ("api_url", "name", "uuid") +VALUES ("https://api.com", "Conrad", "fdfdfg4gdfgdf4gfg"); + + + diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java index 7d27059..ca71682 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java @@ -30,8 +30,7 @@ public class SupplierOfferController { for (ArticleOffer article : offersRepository.findAll()) { UImodelOfferedArticle tmp = new UImodelOfferedArticle(); - tmp.addData(article, articleRepository.findArticleIDByRelatedID(article.id)); // TODO display supplier name - // with link + tmp.addData(article, articleRepository.findArticleIDByRelatedID(article.id)); totals.add(tmp); } From c8d3ab3ff06fc88f6a733a0a0c9d679b699ae030 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 30 May 2020 16:16:14 +0200 Subject: [PATCH 137/203] list suppliers page --- .../hso/ecommerce/app/RequestController.java | 12 +- .../suppliers/SupplierIndexController.java | 56 ++++++++- .../repos/supplier/SupplierRepository.java | 8 ++ .../templates/intern/suppliers/index.html | 111 ++++++++---------- 4 files changed, 115 insertions(+), 72 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index bb0b5d6..0f9c42e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -100,11 +100,12 @@ public class RequestController { return "intern/customerOrders/id"; } + /* @GetMapping("/intern/suppliers/") public String internSuppliers() { return "intern/suppliers/index"; } - +*/ @GetMapping("/intern/suppliers/{id}") public String internSuppliersId() { return "intern/suppliers/id"; @@ -120,15 +121,6 @@ public class RequestController { return "intern/supplierOrders/id"; } - /* - - @GetMapping("/intern/suppliersOffers") - public String internSuppliersOffers() { - return "intern/offeredArticles/index"; - } - */ - - @GetMapping("/intern/accounting/") public String accounting() { return "intern/accounting/index"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java index d6cefc0..56ca421 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java @@ -1,8 +1,62 @@ package org.hso.ecommerce.controller.intern.suppliers; +import java.util.ArrayList; +import java.util.List; + +import org.hso.ecommerce.entities.supplier.Supplier; +import org.hso.ecommerce.repos.supplier.SupplierRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/") public class SupplierIndexController { + + @Autowired + private final SupplierRepository supplierRepository = null; + + @GetMapping("suppliers") + public String listSuppliers(Model model) { + + List totals = new ArrayList(); + + for (Supplier supplier : supplierRepository.findAll()) { + UImodelSuppliers tmp = new UImodelSuppliers(supplier.id, supplier.name); + totals.add(tmp); + } + + model.addAttribute("suppliers", totals); + return "intern/suppliers/index"; + } + + public class UImodelSuppliers { + long id; + String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public UImodelSuppliers(long id, String name) { + this.id = id; + this.name = name; + } + + } + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java index 2b81659..f1023df 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java @@ -1,10 +1,18 @@ package org.hso.ecommerce.repos.supplier; +import java.util.List; + import org.hso.ecommerce.entities.supplier.Supplier; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @Repository public interface SupplierRepository extends JpaRepository { + + @Query("SELECT a FROM Supplier a") + List findAll(); + + } diff --git a/prototype/src/main/resources/templates/intern/suppliers/index.html b/prototype/src/main/resources/templates/intern/suppliers/index.html index a735be8..4fdc843 100644 --- a/prototype/src/main/resources/templates/intern/suppliers/index.html +++ b/prototype/src/main/resources/templates/intern/suppliers/index.html @@ -1,63 +1,52 @@ - - - - - - Lieferanten - - - - - - - -
    - -
    -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    NrName
    0015Cheap AGDetails
    5012Not Cheap GmbH & Co. KGDetails
    7400Hans MÜller GmbHDetails
    -

    -
    -
    -
    - - - + + + + Lieferanten + + + + + + +
    + +
    +

    + + + + + + + + + + + + + + + + + + +
    + +
    NrName
    Details
    +

    +
    +
    +
    + + \ No newline at end of file From 5d7ecbdfb71db0263cb8dda6db199b1179044fa3 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 30 May 2020 16:43:07 +0200 Subject: [PATCH 138/203] basic detail page for supplier --- .../hso/ecommerce/app/RequestController.java | 5 +- .../suppliers/SupplierIndexController.java | 135 ++++++++++++++++++ .../repos/supplier/SupplierRepository.java | 4 +- .../templates/intern/suppliers/id.html | 3 +- 4 files changed, 142 insertions(+), 5 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 0f9c42e..b49241c 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -105,12 +105,13 @@ public class RequestController { public String internSuppliers() { return "intern/suppliers/index"; } -*/ + @GetMapping("/intern/suppliers/{id}") public String internSuppliersId() { return "intern/suppliers/id"; } - +*/ + @GetMapping("/intern/supplierOrders/") public String internSupplierOrders() { return "intern/supplierOrders/index"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java index 56ca421..d9d214a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller @@ -32,6 +33,24 @@ public class SupplierIndexController { return "intern/suppliers/index"; } + @GetMapping("/suppliers/{id}") + public String internListedArticlesId(Model model, @PathVariable String id) { + + System.out.println("hEre!"); + + int supplierId = Integer.parseInt(id); + + UImodelSupplierDetailOrders orders = new UImodelSupplierDetailOrders(supplierId, "01.01.1970", "orderd article", + "netto €", "42", "total €", "31.12.1970"); + + UImodelSupplierDetail total = new UImodelSupplierDetail(supplierRepository.findSupplierById(supplierId).name, + "42€", orders); + + model.addAttribute("SupplierDetail", total); + + return "intern/suppliers/id"; + } + public class UImodelSuppliers { long id; String name; @@ -59,4 +78,120 @@ public class SupplierIndexController { } + public class UImodelSupplierDetail { + + String name; + String balance; + UImodelSupplierDetailOrders orders; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getBalance() { + return balance; + } + + public void setBalance(String balance) { + this.balance = balance; + } + + public UImodelSupplierDetailOrders getOrders() { + return orders; + } + + public void setOrders(UImodelSupplierDetailOrders orders) { + this.orders = orders; + } + + public UImodelSupplierDetail(String name, String balance, UImodelSupplierDetailOrders orders) { + this.name = name; + this.balance = balance; + this.orders = orders; + } + + } + + public class UImodelSupplierDetailOrders { + long id; + String dateOrder; + String articleName; + String priceNetto; + String quantity; + String price_total; + String dateArrival; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getDateOrder() { + return dateOrder; + } + + public void setDateOrder(String dateOrder) { + this.dateOrder = dateOrder; + } + + public String getArticleName() { + return articleName; + } + + public void setArticleName(String articleName) { + this.articleName = articleName; + } + + public String getPriceNetto() { + return priceNetto; + } + + public void setPriceNetto(String priceNetto) { + this.priceNetto = priceNetto; + } + + public String getQuantity() { + return quantity; + } + + public void setQuantity(String quantity) { + this.quantity = quantity; + } + + public String getPrice_total() { + return price_total; + } + + public void setPrice_total(String price_total) { + this.price_total = price_total; + } + + public String getDateArrival() { + return dateArrival; + } + + public void setDateArrival(String dateArrival) { + this.dateArrival = dateArrival; + } + + public UImodelSupplierDetailOrders(long id, String dateOrder, String articleName, String priceNetto, + String quantity, String price_total, String dateArrival) { + this.id = id; + this.dateOrder = dateOrder; + this.articleName = articleName; + this.priceNetto = priceNetto; + this.quantity = quantity; + this.price_total = price_total; + this.dateArrival = dateArrival; + } + + } + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java index f1023df..4437125 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java @@ -5,6 +5,7 @@ import java.util.List; import org.hso.ecommerce.entities.supplier.Supplier; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository @@ -13,6 +14,7 @@ public interface SupplierRepository extends JpaRepository { @Query("SELECT a FROM Supplier a") List findAll(); + @Query("SELECT a FROM Supplier a WHERE a.id = :supplierId") + Supplier findSupplierById(@Param("supplierId") long supplierId); - } diff --git a/prototype/src/main/resources/templates/intern/suppliers/id.html b/prototype/src/main/resources/templates/intern/suppliers/id.html index f4f6788..308d009 100644 --- a/prototype/src/main/resources/templates/intern/suppliers/id.html +++ b/prototype/src/main/resources/templates/intern/suppliers/id.html @@ -15,8 +15,7 @@
    + + +
    + Unterwegs +
    +
    From 1cb52a20d475e1482932f209a0c71f04362500d2 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 30 May 2020 23:32:01 +0200 Subject: [PATCH 143/203] finished supplier orders --- .../hso/ecommerce/app/RequestController.java | 22 --- .../suppliers/SupplierOrderController.java | 134 +++++++++++++- .../supplier/SupplierOrderRepository.java | 3 + .../intern/supplierOrders/index.html | 170 +++++++----------- 4 files changed, 205 insertions(+), 124 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index b49241c..b43478a 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -100,28 +100,6 @@ public class RequestController { return "intern/customerOrders/id"; } - /* - @GetMapping("/intern/suppliers/") - public String internSuppliers() { - return "intern/suppliers/index"; - } - - @GetMapping("/intern/suppliers/{id}") - public String internSuppliersId() { - return "intern/suppliers/id"; - } -*/ - - @GetMapping("/intern/supplierOrders/") - public String internSupplierOrders() { - return "intern/supplierOrders/index"; - } - - @GetMapping("/intern/supplierOrders/{id}") - public String internSupplierOrdersId() { - return "intern/supplierOrders/id"; - } - @GetMapping("/intern/accounting/") public String accounting() { return "intern/accounting/index"; diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java index ef3acb4..b659666 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java @@ -1,8 +1,140 @@ package org.hso.ecommerce.controller.intern.suppliers; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.hso.ecommerce.repos.supplier.SupplierOrderRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/") public class SupplierOrderController { + + @Autowired + private final SupplierOrderRepository supplierOrderRepository = null; + + @GetMapping("supplierOrders") + public String listSuppliers(Model model) { + + List totals = new ArrayList(); + + for (SupplierOrder orders : supplierOrderRepository.findAll()) { + totals.add(new UImodelSupplierOrder(orders)); + } + + model.addAttribute("orders", totals); + + return "intern/supplierOrders/index"; + } + + public class UImodelSupplierOrder { + long id; + String dateOrder; + String supplierName; + String articleName; + long articleId; + String priceNetto; + String quantity; + String price_total; + boolean arrived; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getDateOrder() { + return dateOrder; + } + + public void setDateOrder(String dateOrder) { + this.dateOrder = dateOrder; + } + + public String getArticleName() { + return articleName; + } + + public void setArticleName(String articleName) { + this.articleName = articleName; + } + + public String getPriceNetto() { + return priceNetto; + } + + public void setPriceNetto(String priceNetto) { + this.priceNetto = priceNetto; + } + + public String getQuantity() { + return quantity; + } + + public void setQuantity(String quantity) { + this.quantity = quantity; + } + + public String getPrice_total() { + return price_total; + } + + public void setPrice_total(String price_total) { + this.price_total = price_total; + } + + public long getArticleId() { + return articleId; + } + + public void setArticleId(long articleId) { + this.articleId = articleId; + } + + public boolean isArrived() { + return arrived; + } + + public void setArrived(boolean arrived) { + this.arrived = arrived; + } + + public String getSupplierName() { + return supplierName; + } + + public void setSupplierName(String supplierName) { + this.supplierName = supplierName; + } + + public UImodelSupplierOrder(SupplierOrder order) { + this.id = order.id; + this.supplierName = order.supplier.name; + this.articleName = order.ordered.title; + this.articleId = order.ordered.id; + this.priceNetto = String.format("%.2f", ((float) order.pricePerUnitNetCent / 100)); + this.quantity = String.valueOf(order.numberOfUnits); + this.price_total = String.format("%.2f", ((float) order.totalPriceNet / 100)); + + Date date = new Date(); + date.setTime(order.created.getTime()); + this.dateOrder = new SimpleDateFormat("dd.MM.yyyy").format(date); + + if (order.delivered != null) { + arrived = true; + } else { + arrived = false; + } + } + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java index 73a041c..31c05cf 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java @@ -18,5 +18,8 @@ public interface SupplierOrderRepository extends JpaRepository findOrderBySupplierID(@Param("supplierId") long supplierId); + + @Query("SELECT a FROM SupplierOrder a") + List findAll(); } diff --git a/prototype/src/main/resources/templates/intern/supplierOrders/index.html b/prototype/src/main/resources/templates/intern/supplierOrders/index.html index eafe977..71db072 100644 --- a/prototype/src/main/resources/templates/intern/supplierOrders/index.html +++ b/prototype/src/main/resources/templates/intern/supplierOrders/index.html @@ -1,103 +1,71 @@ - - - - - - Lieferanten Bestellungen - - - - - - - -
    - -
    -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    BestellnummerDatumLeiferantArtikelPreis/Stk (Netto)MengeGesamtpreis (Netto)Status
    45452019-18-10Cheap AGKamera20,00 EUR10200,00 EURUnterwegs
    Angekommen
    24552019-18-10Cheap AGKamera20,00 EUR11220,00 EURUnterwegs
    Angekommen
    12242019-18-10Cheap AGKamera20,00 EUR11220,00 EURAngekommen
    45202019-18-10Cheap AGKamera20,00 EUR11220,00 EURAngekommen
    45212019-18-10Cheap AGKamera20,00 EUR11220,00 EURAngekommen
    -

    -
    -
    -
    - - - + + + + Lieferanten Bestellungen + + + + + + +
    + +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    BestellnummerDatumLeiferantArtikelPreis/Stk (Netto)MengeGesamtpreis (Netto)Status
    + + +
    + Unterwegs +
    +
    +

    +
    +
    +
    + + \ No newline at end of file From 9238162a4c3d6b4c19cd99f0bde73ed0873228ad Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 29 May 2020 11:06:00 +0200 Subject: [PATCH 144/203] implement better search --- .../action/shop/SearchByTermAction.java | 40 +++++++++++++++++++ .../controller/shop/ShopSearchController.java | 3 +- .../repos/shop/ArticleRepository.java | 5 ++- 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/shop/SearchByTermAction.java diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/SearchByTermAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/SearchByTermAction.java new file mode 100644 index 0000000..cfdfffe --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/SearchByTermAction.java @@ -0,0 +1,40 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SearchByTermAction { + + public static List
    searchByTerm(String sourceTerm, ArticleRepository repository) { + + List terms = Arrays.asList(sourceTerm.split(" ")); + List
    resultArticles = new ArrayList<>(); + + terms.forEach(term -> { + List
    titleArticles = repository.getArticlesByTermInTitle(term); //search in Title + titleArticles.forEach(article -> { + if(!resultArticles.contains(article)){ + resultArticles.add(article); + } + }); + + }); + + terms.forEach(term -> { + List
    descArticles = repository.getArticlesByTermInDescription(term); //search by Term + descArticles.forEach(article -> { + if(!resultArticles.contains(article)){ + resultArticles.add(article); + } + }); + + }); + + return resultArticles; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java index 1d14c61..ad1d107 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java @@ -1,5 +1,6 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.action.shop.SearchByTermAction; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.repos.shop.ArticleRepository; import org.hso.ecommerce.repos.shop.CategoryRepository; @@ -32,7 +33,7 @@ public class ShopSearchController { model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar if (term != null) { //if search by Term - List
    articles = articleRepository.getArticlesByTerm(term); //search by Term + List
    articles = SearchByTermAction.searchByTerm(term, articleRepository); model.addAttribute("articles", articles); } else if (category != null) { //if search by Category List
    articles = articleRepository.getArticlesByCategory(category); //search by Category diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java index 0340f59..a7f7597 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -31,7 +31,10 @@ public interface ArticleRepository extends JpaRepository { Optional findArticleIDByRelatedID(@Param("relatedId") long relatedId); @Query(value = "Select a.* from articles as a, warehouse_booking_position_entries as wbpe where wbpe.article_id = a.id and a.title LIKE %:term% group by wbpe.slot_id having max(wbpe.id) and wbpe.new_sum_slot != 0", nativeQuery = true) - List
    getArticlesByTerm(String term); + List
    getArticlesByTermInTitle(String term); + + @Query(value = "Select a.* from articles as a, warehouse_booking_position_entries as wbpe where wbpe.article_id = a.id and a.description LIKE %:term% group by wbpe.slot_id having max(wbpe.id) and wbpe.new_sum_slot != 0", nativeQuery = true) + List
    getArticlesByTermInDescription(String term); @Query(value = "Select a.* from articles as a, categories as c, article_categories_bindings as acb, warehouse_booking_position_entries as wbpe where wbpe.article_id = a.id and acb.articles_id = a.id and acb.categories_id = c.id and c.name = :category group by wbpe.slot_id having max(wbpe.id) and wbpe.new_sum_slot != 0", nativeQuery = true) List
    getArticlesByCategory(String category); From 38fcda708fe118bd59c8480e413a78c1721fbdef Mon Sep 17 00:00:00 2001 From: localhorst Date: Mon, 1 Jun 2020 10:53:10 +0200 Subject: [PATCH 145/203] check if article.image is null --- .../controller/intern/InternArticleController.java | 13 +++++++++---- .../controller/shop/ShopArticleController.java | 9 ++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java index 35d76f8..62b615d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternArticleController.java @@ -148,9 +148,9 @@ public class InternArticleController { File tmpFile = new File(defaultImagePath); // test if default img file exits if (!tmpFile.exists()) { - // fallback if the file not exists + // fallback if the file not exists // create new file - BufferedImage bufferedImage = new BufferedImage(422, 428, BufferedImage.TYPE_INT_RGB); + BufferedImage bufferedImage = new BufferedImage(422, 428, BufferedImage.TYPE_INT_RGB); try { ImageIO.write(bufferedImage, "jpg", new File(defaultImagePath)); // save new file on disk } catch (IOException e) { @@ -208,7 +208,10 @@ public class InternArticleController { public long id; void addListedArticle(Article article, int stock) { - this.imgPath = article.image.path; + + if (article.image != null) { + this.imgPath = article.image.path; + } this.title = article.title; this.price_netto = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); @@ -338,7 +341,9 @@ public class InternArticleController { } void addArticle(Article article, int stock) { - this.imgPath = article.image.path; + if (article.image != null) { + this.imgPath = article.image.path; + } this.title = article.title; this.price_netto = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 235669c..18144f7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -100,8 +100,11 @@ public class ShopArticleController { @PathVariable("id") Long id ) throws IOException { Article article = articleRepository.findArticleById(id); - InputStream in = new FileInputStream(article.image.path); - response.setContentType(MediaType.IMAGE_JPEG_VALUE); - IOUtils.copy(in, response.getOutputStream()); + + if(article.image != null) { + InputStream in = new FileInputStream(article.image.path); + response.setContentType(MediaType.IMAGE_JPEG_VALUE); + IOUtils.copy(in, response.getOutputStream()); + } } } \ No newline at end of file From 0fb1d5704e658bf0642697cc66432aa644250386 Mon Sep 17 00:00:00 2001 From: Danny Date: Mon, 1 Jun 2020 11:11:34 +0200 Subject: [PATCH 146/203] Bufix: After register, you now logged in --- .../org/hso/ecommerce/controller/RegisterController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index a53e084..448af79 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -64,7 +64,10 @@ public class RegisterController { userRepository.save(newUser); // save newUser - return "redirect:/login"; + user = userRepository.findByEmail(username); + session.setAttribute("userId", user.get().getId()); + + return "redirect:/"; } @GetMapping("/register") From 922e3cadef4c875a922ab38519e7ddf07974333a Mon Sep 17 00:00:00 2001 From: Danny Date: Mon, 1 Jun 2020 11:13:03 +0200 Subject: [PATCH 147/203] Bugfix register --- .../java/org/hso/ecommerce/controller/RegisterController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index 448af79..15acf9f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import java.util.Optional; @Controller @@ -30,7 +31,8 @@ public class RegisterController { @RequestParam("name") String name, @RequestParam("address") String address, @RequestParam("type") String type, - @RequestParam("ad") String ad + @RequestParam("ad") String ad, + HttpSession session ) { Optional user = userRepository.findByEmail(username); From 839bebcd8912db2f0aad21745618df55637d7ed1 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 1 Jun 2020 11:58:08 +0200 Subject: [PATCH 148/203] trim searchterm --- .../org/hso/ecommerce/controller/shop/ShopSearchController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java index ad1d107..cacdafe 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java @@ -32,6 +32,8 @@ public class ShopSearchController { ) { model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar + term = term.trim(); + if (term != null) { //if search by Term List
    articles = SearchByTermAction.searchByTerm(term, articleRepository); model.addAttribute("articles", articles); From 6988d3f213f21bbedbee4de966d4dcd078c1a8bd Mon Sep 17 00:00:00 2001 From: Seil0 Date: Mon, 1 Jun 2020 17:10:04 +0200 Subject: [PATCH 149/203] minor code clean up --- .../controller/intern/suppliers/SupplierOfferController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java index ca71682..edc7bc2 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java @@ -26,7 +26,7 @@ public class SupplierOfferController { @GetMapping("supplierOffers") public String internListedArticles(Model model) { - List totals = new ArrayList(); + List totals = new ArrayList<>(); for (ArticleOffer article : offersRepository.findAll()) { UImodelOfferedArticle tmp = new UImodelOfferedArticle(); From ff4c984f54aa3c32c53cbcbeba0d3275b0a97ea6 Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 1 Jun 2020 20:24:45 +0200 Subject: [PATCH 150/203] fix error message --- .../org/hso/ecommerce/action/user/UpdateUserSettingsAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java index 91b3796..ba17181 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -35,7 +35,7 @@ public class UpdateUserSettingsAction { this.repository.save(this.user); result.updated = true; } else { - result.errorString = "Die neuen Passwörter entsprechen dem alten Passwort."; + result.errorString = "Das neue Passwort entspricht dem alten Passwort."; } } else { result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; From b7134a60da9da6c467354aa9343a235b5e2e55ea Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 1 Jun 2020 20:24:59 +0200 Subject: [PATCH 151/203] rename CustomerOrderRepo in Cronjob --- .../hso/ecommerce/controller/cronjob/CronjobController.java | 4 ++-- .../hso/ecommerce/repos/shop/CustomerOrderRepository.java | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java index e2bad65..0f54221 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java @@ -28,7 +28,7 @@ import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository; import org.hso.ecommerce.repos.shop.ArticleRepository; -import org.hso.ecommerce.repos.shop.CustomerOderRepository; +import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.supplier.ArticleOfferRepository; import org.hso.ecommerce.repos.supplier.SupplierOrderRepository; import org.hso.ecommerce.repos.supplier.SupplierRepository; @@ -230,7 +230,7 @@ class CronjobController { final ArticleOfferRepository articleOfferRepository = null; @Autowired - final CustomerOderRepository customerOrderRepository = null; + final CustomerOrderRepository customerOrderRepository = null; @Autowired final BookingRepository bookingRepository = null; diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java index 8877706..86c88e6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java @@ -10,6 +10,11 @@ import java.util.List; @Repository public interface CustomerOrderRepository extends JpaRepository { + @Query("SELECT SUM(cop.quantity) FROM CustomerOrderPosition cop JOIN cop.order co WHERE cop.article.id = :articleId AND co.created >= :begin AND co.created < :end") + Integer countOrdersOfArticleInTimespan( + long articleId, java.sql.Timestamp begin, java.sql.Timestamp end + ); + @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId ORDER BY co.id DESC") List getOrdersByUserId(long userId); From 976e2d1949c780dc2dc3c0c6a14c65a422447f1b Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Mon, 1 Jun 2020 20:25:51 +0200 Subject: [PATCH 152/203] Implement Warehouse.... Sry, only one commit. No time to make history ;D --- prototype/gradlew | 0 .../CalculateWarehouseStatsAction.java | 73 +++++++++ .../warehouse/CreateManuelBookingAction.java | 92 ++++++++++++ .../hso/ecommerce/app/RequestController.java | 44 ------ .../intern/WarehouseController.java | 19 ++- .../warehouse/ManuelBookingController.java | 138 ++++++++++++++++++ .../intern/warehouse/SlotsController.java | 44 ++++++ .../intern/warehouse/TodoController.java | 102 +++++++++++++ .../entities/booking/BookingReason.java | 5 + .../WarehouseBookingPositionSlotEntry.java | 10 ++ ...useBookingPositionSlotEntryRepository.java | 8 +- .../warehouse/WarehouseBookingRepository.java | 6 + .../templates/intern/warehouse/addManual.html | 25 ++-- .../intern/warehouse/id_progress.html | 67 ++++++--- .../templates/intern/warehouse/index.html | 132 ++++++++--------- .../intern/warehouse/slots/index.html | 63 +++----- .../templates/intern/warehouse/todo.html | 108 +++++++------- 17 files changed, 682 insertions(+), 254 deletions(-) mode change 100644 => 100755 prototype/gradlew create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/warehouse/CalculateWarehouseStatsAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/action/warehouse/CreateManuelBookingAction.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/ManuelBookingController.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/SlotsController.java create mode 100644 prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/TodoController.java diff --git a/prototype/gradlew b/prototype/gradlew old mode 100644 new mode 100755 diff --git a/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CalculateWarehouseStatsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CalculateWarehouseStatsAction.java new file mode 100644 index 0000000..ca5c5dc --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CalculateWarehouseStatsAction.java @@ -0,0 +1,73 @@ +package org.hso.ecommerce.action.warehouse; + +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; + +import java.util.HashSet; +import java.util.List; + +public class CalculateWarehouseStatsAction { + + private List entryList; + + public CalculateWarehouseStatsAction(List everyCurrentEntry) { + this.entryList = everyCurrentEntry; + } + + public WarehouseStats finish() { + int numArticles = calculateNumArticles(); + double efficiency = calculateEfficiency(); + double ratioUsedSlots = calculateRatioSlotsUsed(); + + return new WarehouseStats(numArticles, efficiency, ratioUsedSlots); + } + + private double calculateRatioSlotsUsed() { + int used = 0; + + for (WarehouseBookingPositionSlotEntry entry : entryList) { + if (entry.newSumSlot > 0) { + used++; + } + } + + return ((double) used) / entryList.size(); + } + + private double calculateEfficiency() { + double e = 0; + + for (WarehouseBookingPositionSlotEntry entry : entryList) { + if (entry.newSumSlot > 0) { + e += entry.newSumSlot / (double) entry.article.warehouseUnitsPerSlot; + } else { + e += 0; + } + } + + return e / entryList.size(); + } + + private int calculateNumArticles() { + HashSet articleIds = new HashSet<>(); + + for (WarehouseBookingPositionSlotEntry entry : entryList) { + if (entry.newSumSlot > 0) { + articleIds.add(entry.article.id); + } + } + + return articleIds.size(); + } + + private static class WarehouseStats { + public int numArticles; + public double efficiency; + public double ratioUsedSlots; + + WarehouseStats(int numArticles, double efficiency, double ratioUsedSlots) { + this.numArticles = numArticles; + this.efficiency = efficiency; + this.ratioUsedSlots = ratioUsedSlots; + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CreateManuelBookingAction.java b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CreateManuelBookingAction.java new file mode 100644 index 0000000..4d35e63 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CreateManuelBookingAction.java @@ -0,0 +1,92 @@ +package org.hso.ecommerce.action.warehouse; + +import org.hso.ecommerce.entities.booking.BookingReason; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; + +import java.sql.Timestamp; +import java.util.Date; +import java.util.Optional; + +public class CreateManuelBookingAction { + + private Article article; + private int amount; + private Optional source; + private Optional destination; + private String reason; + + public CreateManuelBookingAction(Article article, int amount, Optional source, Optional destination, String reason) { + this.article = article; + this.amount = amount; + this.source = source; + this.destination = destination; + this.reason = reason; + } + + public WarehouseBooking finish() throws ArticleSlotConstraintFailedException { + WarehouseBooking booking = new WarehouseBooking(); + booking.created = new Timestamp(new Date().getTime()); + booking.reason = new BookingReason(reason); + + if (source.isPresent()) { + + if (source.get().article.id != article.id) { + throw new ArticleSlotConstraintArticleTypeFailedException(); + } + + WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); + bookingPosition.booking = booking; + + bookingPosition.article = article; + bookingPosition.amount = -amount; + bookingPosition.slotEntry = source.get().copyAddAmount(-amount); + + if (bookingPosition.slotEntry.newSumSlot < 0 || bookingPosition.slotEntry.newSumSlot > article.warehouseUnitsPerSlot) { + throw new ArticleSlotConstraintFailedException("The quantity of article can only be set in bounds."); + } + + booking.positions.add(bookingPosition); + } + + if (destination.isPresent()) { + + if (destination.get().article.id != article.id) { + throw new ArticleSlotConstraintArticleTypeFailedException(); + } + + WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); + bookingPosition.booking = booking; + + bookingPosition.article = article; + bookingPosition.amount = amount; + bookingPosition.slotEntry = destination.get().copyAddAmount(amount); + + if (bookingPosition.slotEntry.newSumSlot < 0 || bookingPosition.slotEntry.newSumSlot > article.warehouseUnitsPerSlot) { + throw new ArticleSlotConstraintFailedException("The quantity of article can only be set in bounds."); + } + + booking.positions.add(bookingPosition); + } + + return booking; + } + + public static class ArticleSlotConstraintFailedException extends Exception { + public ArticleSlotConstraintFailedException(String s) { + super(s); + } + + public ArticleSlotConstraintFailedException() { + super("The quantity of article can only be set in bounds."); + } + } + + public static class ArticleSlotConstraintArticleTypeFailedException extends ArticleSlotConstraintFailedException { + public ArticleSlotConstraintArticleTypeFailedException() { + super("The Article in the slot entry does not match."); + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index bb0b5d6..9d63f4b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -119,15 +119,6 @@ public class RequestController { public String internSupplierOrdersId() { return "intern/supplierOrders/id"; } - - /* - - @GetMapping("/intern/suppliersOffers") - public String internSuppliersOffers() { - return "intern/offeredArticles/index"; - } - */ - @GetMapping("/intern/accounting/") public String accounting() { @@ -148,39 +139,4 @@ public class RequestController { public String accountingAddManual() { return "intern/accounting/addManual"; } - - @GetMapping("/intern/warehouse/") - public String accountingWarehouse() { - return "intern/warehouse/index"; - } - - @GetMapping("/intern/warehouse/todo") - public String accountingWarehouseTodo() { - return "intern/warehouse/todo"; - } - - @GetMapping("/intern/warehouse/addManual") - public String accountingWarehouseAddManual() { - return "intern/warehouse/addManual"; - } - - @PostMapping("/intern/warehouse/progress/{id}") - public String accountingWarehouseProgressIdPost(HttpServletResponse response) { - if ((notSoRandom++) % 2 == 1) { - return "redirect:/intern/warehouse/progress/450"; - } else { - response.setStatus(409); - return "intern/warehouse/error_progress_failed"; - } - } - - @GetMapping("/intern/warehouse/progress/{id}") - public String accountingWarehouseProgressId() { - return "intern/warehouse/id_progress"; - } - - @GetMapping("/intern/warehouse/slots/") - public String accountingWarehouseSlots() { - return "intern/warehouse/slots/index"; - } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java index 9b618f7..1e26c3d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java @@ -1,8 +1,25 @@ package org.hso.ecommerce.controller.intern; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/warehouse/") public class WarehouseController { + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @GetMapping("/") + public String accountingWarehouse( + HttpServletRequest request + ) { + request.setAttribute("bookings", warehouseBookingRepository.findAll()); + return "intern/warehouse/index"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/ManuelBookingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/ManuelBookingController.java new file mode 100644 index 0000000..35ab3fc --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/ManuelBookingController.java @@ -0,0 +1,138 @@ +package org.hso.ecommerce.controller.intern.warehouse; + +import org.hso.ecommerce.action.warehouse.CreateManuelBookingAction; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.warehouse.Slot; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.warehouse.SlotRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Optional; + +@Controller +@RequestMapping("/intern/warehouse/") +public class ManuelBookingController { + + @Autowired + private final ArticleRepository articleRepository = null; + + @Autowired + private final SlotRepository slotRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @GetMapping("addManual") + public String warehouseAddManual( + HttpServletRequest request + ) { + + request.setAttribute("articles", articleRepository.findAll()); + request.setAttribute("slots", slotRepository.findAll()); + + return "intern/warehouse/addManual"; + } + + @PostMapping("addManual") + public String warehouseAddMaualPost( + HttpServletRequest request, + HttpServletResponse response, + @RequestParam("articleId") String articleIdText, + @RequestParam("amount") Integer amount, + @RequestParam("reason") String reason, + @RequestParam("sourceIsSlot") Boolean sourceIsSlot, + @RequestParam("sourceSlot") Integer sourceSlotNum, + @RequestParam("destinationIsSlot") Boolean destinationIsSlot, + @RequestParam("destinationSlot") Integer destinationSlotNum + ) { + + // The suggestions for articleId in the UI show articles names, seperated by a " - ". + // The Number must be extracted first. + long articleId = -1; + try { + articleId = Long.parseLong(articleIdText.split(" - ", 2)[0].trim()); + } catch (NumberFormatException e) { + request.setAttribute("error", "Die Artikel Id konnte nicht erkannt werden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "intern/warehouse/addManual"; + } + + + Optional
    optionalArticle = articleRepository.findById(articleId); + Article article = null; + if (!optionalArticle.isPresent()) { + request.setAttribute("error", "Der Artikel konnte nicht gefunden werden."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } else { + article = optionalArticle.get(); + } + + if (amount <= 0) { + request.setAttribute("error", "Eine Anzahl <= 0 kann nicht verbucht werden."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + if (sourceIsSlot == false && destinationIsSlot == false) { + request.setAttribute("error", "Jede Buchung benötigt ein Ziel oder eine Quelle."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + Optional sourceSlot = Optional.empty(); + if (sourceIsSlot == true) { + sourceSlot = warehouseBookingPositionSlotEntryRepository.getBySlotNum(sourceSlotNum); + if (!sourceSlot.isPresent()) { + request.setAttribute("error", "Quelllagerplatz wurde nicht gefunden oder ist leer."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + } + + Optional destinationSlot = Optional.empty(); + if (destinationIsSlot == true) { + Optional slot = slotRepository.findBySlotNum(destinationSlotNum); + if (!slot.isPresent()) { + request.setAttribute("error", "Ziellagerplatz wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + Article finalArticle = article; + destinationSlot = Optional.of(warehouseBookingPositionSlotEntryRepository.getBySlotNum(destinationSlotNum).orElseGet( + () -> WarehouseBookingPositionSlotEntry.empty(finalArticle, slot.get()) + )); + } + + try { + warehouseBookingRepository.save( + new CreateManuelBookingAction(article, amount, sourceSlot, destinationSlot, reason).finish() + ); + } catch (CreateManuelBookingAction.ArticleSlotConstraintArticleTypeFailedException e) { + request.setAttribute("error", "Es befindet sich der falsche Artikeltyp in Quell- oder Ziellagerplatz. "); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } catch (CreateManuelBookingAction.ArticleSlotConstraintFailedException e) { + request.setAttribute("error", "Die maximale Anzahl an lagerbaren Artikeln im Ziellagerplatz wurde überschritten."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + return "redirect:/intern/warehouse/todo"; + } + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/SlotsController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/SlotsController.java new file mode 100644 index 0000000..1f6bdd3 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/SlotsController.java @@ -0,0 +1,44 @@ +package org.hso.ecommerce.controller.intern.warehouse; + +import org.hso.ecommerce.action.warehouse.CalculateWarehouseStatsAction; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.hso.ecommerce.repos.warehouse.SlotRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.stream.Collectors; + +@Controller +@RequestMapping("/intern/warehouse/") +public class SlotsController { + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + private final SlotRepository slotRepository = null; + + @GetMapping("slots/") + public String accountingWarehouseSlots( + HttpServletRequest request + ) { + + // Doing this in a single would be hard and error prone. + // Writing native queries should be minimized. + // Therefore this method was prefered + List entries = slotRepository.findAll().stream().map( + s -> warehouseBookingPositionSlotEntryRepository + .getBySlotNum(s.slotNum) + .orElseGet(() -> WarehouseBookingPositionSlotEntry.empty(null, s)) + ).collect(Collectors.toList()); + request.setAttribute("entries", entries); + + request.setAttribute("stats", new CalculateWarehouseStatsAction(entries).finish()); + return "intern/warehouse/slots/index"; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/TodoController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/TodoController.java new file mode 100644 index 0000000..48d130a --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/TodoController.java @@ -0,0 +1,102 @@ +package org.hso.ecommerce.controller.intern.warehouse; + +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Optional; + +@Controller +@RequestMapping("/intern/warehouse/") +public class TodoController { + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @GetMapping("todo") + public String accountingWarehouseTodo( + HttpServletRequest request + ) { + request.setAttribute("bookings", warehouseBookingRepository.findNotDone()); + return "intern/warehouse/todo"; + } + + + @PostMapping("progress/{id}") + public String postProgressId( + HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id + ) { + Optional booking = warehouseBookingRepository.findById(id); + if (!booking.isPresent()) { + request.setAttribute("error", "Die Buchung wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "error/404"; + } + + if (booking.get().isInProgress) { + response.setStatus(409); + return "intern/warehouse/error_progress_failed"; + } + + booking.get().isInProgress = true; + warehouseBookingRepository.save(booking.get()); + + return "redirect:/intern/warehouse/progress/" + id; + } + + @PostMapping("progress/{id}/finish") + public String postProgressIdFinish( + HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id + ) { + Optional booking = warehouseBookingRepository.findById(id); + if (!booking.isPresent()) { + request.setAttribute("error", "Die Buchung wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "error/404"; + } + + booking.get().isInProgress = true; + booking.get().isDone = true; + + warehouseBookingRepository.save(booking.get()); + + return "redirect:/intern/warehouse/todo"; + } + + @GetMapping("progress/{id}") + public String getProgressId(HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id) { + Optional booking = warehouseBookingRepository.findById(id); + if (!booking.isPresent()) { + request.setAttribute("error", "Die Buchung wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "error/404"; + } + + if (booking.get().isDone) { + request.setAttribute("info", "Die Buchung wurde schon abgeschlossen."); + } + + if (!booking.get().isInProgress) { + // Only reachable if path is manipulated. + request.setAttribute("error", "Die Buchung wurde noch nicht zugewiesen!"); + } + + request.setAttribute("booking", booking.get()); + return "intern/warehouse/id_progress"; + } + + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java index 3b93517..8d6572f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java @@ -43,4 +43,9 @@ public class BookingReason { public BookingReason(SupplierOrder supplierOrder) { this.supplierOrder = supplierOrder; } + + public BookingReason(String comment) { + this.isManuel = true; + this.comment = comment; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java index 444b921..5c7d909 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingPositionSlotEntry.java @@ -38,4 +38,14 @@ public class WarehouseBookingPositionSlotEntry { return e; } + + public static WarehouseBookingPositionSlotEntry empty(Article article, Slot slot) { + WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry(); + + e.article = article; + e.slot = slot; + e.newSumSlot = 0; + + return e; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java index cb9902e..804e8b7 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -13,10 +13,12 @@ public interface WarehouseBookingPositionSlotEntryRepository extends JpaReposito @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) List getByArticle(long article); - - + @Query(value = "SELECT SUM(w.new_sum_slot) FROM warehouse_booking_position_entries as w WHERE w.article_id = :articleid", nativeQuery = true) Optional getArticleStock(long articleid); - + + @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND s.slot_num = :slotnum GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) + Optional getBySlotNum(long slotnum); + } diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java index 8d01092..08434c4 100644 --- a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java @@ -2,10 +2,16 @@ package org.hso.ecommerce.repos.warehouse; import org.hso.ecommerce.entities.warehouse.WarehouseBooking; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface WarehouseBookingRepository extends JpaRepository { + @Query("Select b FROM WarehouseBooking b WHERE b.isDone = 0") + List findNotDone(); + } diff --git a/prototype/src/main/resources/templates/intern/warehouse/addManual.html b/prototype/src/main/resources/templates/intern/warehouse/addManual.html index 774ff56..f4c9f08 100644 --- a/prototype/src/main/resources/templates/intern/warehouse/addManual.html +++ b/prototype/src/main/resources/templates/intern/warehouse/addManual.html @@ -20,14 +20,12 @@
    -
    +
    - + -
    @@ -36,20 +34,23 @@
    - +
    - +
    - + - + + +
    @@ -58,13 +59,13 @@
    - +
    - + - +
    diff --git a/prototype/src/main/resources/templates/intern/warehouse/id_progress.html b/prototype/src/main/resources/templates/intern/warehouse/id_progress.html index 5e050cd..5d26a1b 100644 --- a/prototype/src/main/resources/templates/intern/warehouse/id_progress.html +++ b/prototype/src/main/resources/templates/intern/warehouse/id_progress.html @@ -21,35 +21,54 @@ - - 2020-01-12 12:18 - - Bestellung 8408 -
    - Hans Maier
    - Hauptstraße 12
    - 74880 Musterstadt
    - Deutschland
    -
    + + + + Manuell: +
    
    +                
    +                
    +                    Startbuchung
    +                
    +                
    +                    
    +                        Bestellung: 
    +                    
    +                    
    
    +                
    +                
    +                    
    +                        Lieferung: 
    +                    
                     
                 
    -            
    -                
    -                Kamera
    -                1
    -                Lagerplatz 01
    -                 -
    -            
    -            
    -                
    -                Spielzeugauto
    -                2
    -                Lagerplatz 02
    -                 -
    +            
    +                
    +                
    +                
    +                    
    +                     -
    +                    Lagerplatz  
    +                
    +                
    +                    
    +                    Lagerplatz  
    +                     -
    +                
    +                
    +                    
    +                     -
    +                     -
    +                
                 
             
             

    - Abschließen + + +

    diff --git a/prototype/src/main/resources/templates/intern/warehouse/index.html b/prototype/src/main/resources/templates/intern/warehouse/index.html index 3a6ea70..a4170c6 100644 --- a/prototype/src/main/resources/templates/intern/warehouse/index.html +++ b/prototype/src/main/resources/templates/intern/warehouse/index.html @@ -1,9 +1,9 @@ - + - + Lager @@ -18,7 +18,7 @@

    Lagerbuchungen

    -
    +
    @@ -32,8 +32,8 @@ @@ -45,77 +45,61 @@ + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + +
    - +
    Status
    + Manuell: + + Startbuchung + + + Bestellung: + + + + Lieferung: + +
    2020-01-12 12:18 - Bestellung 8408 - Auf Warteliste
    -
    Kamera1Lagerplatz 01 -
    Spielzeugauto2Lagerplatz 02 -
    + Auf Warteliste + + Wird bearbeitet + + Fertig +
    2020-01-12 12:15 - Manuell: Ware war defekt. - In Arbeit
    -
    Kamera1Lagerplatz 01 -
    2020-01-12 12:11 - Lieferung 4545 - Fertig
    Kamera10 -Lagerplatz 01 - -
    Kamera1 -Lagerplatz 04
    -Lagerplatz + Lagerplatz + - - -

    diff --git a/prototype/src/main/resources/templates/intern/warehouse/slots/index.html b/prototype/src/main/resources/templates/intern/warehouse/slots/index.html index 29de644..3d52f6b 100644 --- a/prototype/src/main/resources/templates/intern/warehouse/slots/index.html +++ b/prototype/src/main/resources/templates/intern/warehouse/slots/index.html @@ -27,15 +27,16 @@

    Plätze in Verwendung

    -

    67%

    +

    % +

    Lagereffizienz

    -

    43%

    +

    %

    Lagerdiversität

    -

    3

    +

    @@ -52,47 +53,21 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +
    Anzahl Max.

    #1

    Kamera2020

    #2

    Kamera120

    #3

    0

    #4

    0

    #5

    Usb-Ding110

    #6

    Stativ35

    #

    - .

    diff --git a/prototype/src/main/resources/templates/intern/warehouse/todo.html b/prototype/src/main/resources/templates/intern/warehouse/todo.html index 4ff484e..e341a99 100644 --- a/prototype/src/main/resources/templates/intern/warehouse/todo.html +++ b/prototype/src/main/resources/templates/intern/warehouse/todo.html @@ -24,6 +24,7 @@
    +

    Es gibt keine offenen Lagerbuchungen!

    @@ -41,60 +42,63 @@ + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + +
    Status
    + Manuell: +
    
    +                    
    + Startbuchung + + + Bestellung: + +
    
    +                    
    + + Lieferung: + +
    2020-01-12 12:18 - Bestellung 8408 -
    - Hans Maier
    - Hauptstraße 12
    - 74880 Musterstadt
    - Deutschland
    -
    -
    -
    - -
    -
    Kamera1Lagerplatz 01 -
    Spielzeugauto2Lagerplatz 02 -
    2020-01-12 12:15 - Manuell: Ware war defekt. - Abschließen -
    Kamera1Lagerplatz 01 -
    +
    + +
    +
    +
    + +
    +
    -Lagerplatz + Lagerplatz + - - -

    From f4ba79a4cdfefb24d33a5d388c1e7e22ee4e364b Mon Sep 17 00:00:00 2001 From: Hannes Date: Mon, 1 Jun 2020 20:27:19 +0200 Subject: [PATCH 153/203] add id's to form inputs --- prototype/src/main/resources/templates/user/settings.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prototype/src/main/resources/templates/user/settings.html b/prototype/src/main/resources/templates/user/settings.html index c9a2199..80bd60e 100644 --- a/prototype/src/main/resources/templates/user/settings.html +++ b/prototype/src/main/resources/templates/user/settings.html @@ -59,7 +59,7 @@
    -
    -
    - -
    -
    + placeholder="Optional: Zusatz Optional: Unternehmen Straße Hausnummer Postleitzeit Ort Land"> +
    +
    + +
    + +
    +
    +
    +

    Werbung

    +
    +
    +
    + +
    + +
    +
    +
    + - - -
    -
    - - - + + +
    +
    + + \ No newline at end of file From a41889b2cbe1cd9e172e8168fe81c386c4e953bd Mon Sep 17 00:00:00 2001 From: localhorst Date: Fri, 12 Jun 2020 23:48:28 +0200 Subject: [PATCH 195/203] login in extern loginController, RequestController cleanup, login right after register --- .../hso/ecommerce/app/RequestController.java | 83 ++------------ .../ecommerce/controller/LoginController.java | 63 ++++++++++- .../controller/RegisterController.java | 105 +++++++++--------- 3 files changed, 123 insertions(+), 128 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 3039bb9..406e283 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -1,17 +1,7 @@ package org.hso.ecommerce.app; -import org.hso.ecommerce.entities.user.User; -import org.hso.ecommerce.repos.user.UserRepository; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import java.util.Optional; /** * TODO clean up this class @@ -19,70 +9,13 @@ import java.util.Optional; @Controller public class RequestController { - @Autowired - private final UserRepository userRepository = null; - - static int notSoRandom = 0; - - @GetMapping("/login") - public String login() { - return "login"; - } - - @PostMapping("/login") - public String loginPost( - HttpServletRequest request, - HttpServletResponse response, - @RequestParam("username") String username, - @RequestParam("password") String password, - HttpSession session - ) { - String gto = (String) session.getAttribute("afterLogin"); - - Optional user = userRepository.findByEmail(username); - if (!user.isPresent()) { - request.setAttribute("error", "Email Adresse falsch."); - response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); - return "login"; - } - - if (!user.get().validatePassword(password)) { - request.setAttribute("error", "Passwort falsch."); - response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); - return "login"; - } - - if (!user.get().isActive) { - request.setAttribute("error", "User ist deaktiviert."); - response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); - return "login"; - } - - session.setAttribute("userId", user.get().getId()); - - if (gto != null && gto.startsWith("/")) { - return "redirect:" + gto; - } else { - return "redirect:/"; - } - } - - @PostMapping("/logout") - public String logoutPost(HttpServletResponse response, - HttpSession session - ) { - session.removeAttribute("userId"); - return "redirect:/"; - } - - @GetMapping("/intern/customerOrders/") - public String internCustomerOrder() { - return "intern/customerOrders/index"; - } - - @GetMapping("/intern/customerOrders/{id}") - public String internCustomerOrdersId() { - return "intern/customerOrders/id"; - } + @GetMapping("/intern/customerOrders/") + public String internCustomerOrder() { + return "intern/customerOrders/index"; + } + @GetMapping("/intern/customerOrders/{id}") + public String internCustomerOrdersId() { + return "intern/customerOrders/id"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java b/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java index 5f4ebad..0138257 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java @@ -1,8 +1,69 @@ package org.hso.ecommerce.controller; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; @Controller -//@RequestMapping("...") +@RequestMapping("/") public class LoginController { + + @Autowired + private final UserRepository userRepository = null; + + @GetMapping("login") + public String login() { + return "login"; + } + + @PostMapping("login") + public String loginPost(HttpServletRequest request, HttpServletResponse response, + @RequestParam("username") String username, @RequestParam("password") String password, HttpSession session) { + + String gto = (String) session.getAttribute("afterLogin"); + + Optional user = userRepository.findByEmail(username); + if (!user.isPresent()) { + request.setAttribute("error", "Email Adresse falsch."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "login"; + } + + if (!user.get().validatePassword(password)) { + request.setAttribute("error", "Passwort falsch."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "login"; + } + + if (!user.get().isActive) { + request.setAttribute("error", "User ist deaktiviert."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "login"; + } + + session.setAttribute("userId", user.get().getId()); + + if (gto != null && gto.startsWith("/")) { + return "redirect:" + gto; + } else { + return "redirect:/"; + } + } + + @PostMapping("logout") + public String logoutPost(HttpServletResponse response, HttpSession session) { + session.removeAttribute("userId"); + return "redirect:/"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index efd6970..b58099d 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -16,66 +16,67 @@ import javax.servlet.http.HttpSession; import java.util.Optional; @Controller -public class RegisterController { +public class RegisterController { - @Autowired - private final UserRepository userRepository = null; + @Autowired + private final UserRepository userRepository = null; - @PostMapping("/register") - public String registerPost( - HttpServletRequest request, - HttpServletResponse response, - @RequestParam("username") String username, - @RequestParam("password") String password, - @RequestParam("password2") String password2, - @RequestParam("salutation") String salutation, - @RequestParam("name") String name, - @RequestParam("address") String address, - @RequestParam("type") String type, //TODO store - @RequestParam("ad") String ad, //TODO store - HttpSession session - ) - { - Optional user = userRepository.findByEmail(username); - if (user.isPresent()) { - request.setAttribute("error", "Email Adresse existiert bereits!"); - response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); - return "register"; - } + @PostMapping("/register") + public String registerPost(HttpServletRequest request, HttpServletResponse response, + @RequestParam("username") String username, @RequestParam("password") String password, + @RequestParam("password2") String password2, @RequestParam("salutation") String salutation, + @RequestParam("name") String name, @RequestParam("address") String address, + @RequestParam("type") String type, // TODO store + @RequestParam("ad") String ad, // TODO store + HttpSession session) { + Optional user = userRepository.findByEmail(username); + if (user.isPresent()) { + request.setAttribute("error", "Email Adresse existiert bereits!"); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "register"; + } - if (!password.equals(password2)){ - request.setAttribute("error", "Passwörter sind nicht gleich"); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return "register"; - } + if (!password.equals(password2)) { + request.setAttribute("error", "Passwörter sind nicht gleich"); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "register"; + } - //set values for new user - User newUser = new User(); - newUser.email = username; - newUser.setPassword(password); - newUser.email = username; - newUser.isEmployee = false; - newUser.salutation = salutation; - newUser.defaultPayment = PaymentMethod.fromCreditCardNumber(""); + // set values for new user + User newUser = new User(); + newUser.email = username; + newUser.setPassword(password); + newUser.email = username; + newUser.isEmployee = false; + newUser.salutation = salutation; + newUser.defaultPayment = PaymentMethod.fromCreditCardNumber(""); - newUser.isActive = true; - newUser.created = new java.sql.Timestamp(System.currentTimeMillis()); + newUser.isActive = true; + newUser.created = new java.sql.Timestamp(System.currentTimeMillis()); - Address newAddress = new Address(); - newAddress.name = name; - newAddress.addressString = address; - newUser.defaultDeliveryAddress = newAddress; + Address newAddress = new Address(); + newAddress.name = name; + newAddress.addressString = address; + newUser.defaultDeliveryAddress = newAddress; - userRepository.save(newUser); // save newUser + userRepository.save(newUser); // save newUser - user = userRepository.findByEmail(username); - session.setAttribute("userId", user.get().getId()); + user = userRepository.findByEmail(username); + session.setAttribute("userId", user.get().getId()); - return "redirect:/"; - } + String gto = (String) session.getAttribute("afterLogin"); - @GetMapping("/register") - public String register() { - return "register"; - } + //login after register + if (gto != null && gto.startsWith("/")) { + return "redirect:" + gto; + } else { + return "redirect:/"; + } + + } + + @GetMapping("/register") + public String register() { + return "register"; + } } From e0e853a57568337752a7bb22e196212da77696c9 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 13 Jun 2020 10:41:59 +0200 Subject: [PATCH 196/203] fixes from PR review --- .../org/hso/ecommerce/controller/LoginController.java | 6 +++--- .../org/hso/ecommerce/controller/RegisterController.java | 4 ++-- prototype/src/main/resources/templates/register.html | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java b/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java index 0138257..94182ce 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java @@ -35,19 +35,19 @@ public class LoginController { Optional user = userRepository.findByEmail(username); if (!user.isPresent()) { - request.setAttribute("error", "Email Adresse falsch."); + request.setAttribute("error", "Die Email Adresse falsch."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "login"; } if (!user.get().validatePassword(password)) { - request.setAttribute("error", "Passwort falsch."); + request.setAttribute("error", "Das Passwort ist falsch."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "login"; } if (!user.get().isActive) { - request.setAttribute("error", "User ist deaktiviert."); + request.setAttribute("error", "Dieses Konto ist deaktiviert.."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "login"; } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java index b58099d..36fcdef 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -31,13 +31,13 @@ public class RegisterController { HttpSession session) { Optional user = userRepository.findByEmail(username); if (user.isPresent()) { - request.setAttribute("error", "Email Adresse existiert bereits!"); + request.setAttribute("error", "Die Email Adresse existiert bereits."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return "register"; } if (!password.equals(password2)) { - request.setAttribute("error", "Passwörter sind nicht gleich"); + request.setAttribute("error", "Die Passwörter stimmen nicht überein."); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return "register"; } diff --git a/prototype/src/main/resources/templates/register.html b/prototype/src/main/resources/templates/register.html index a2ef401..de4b839 100644 --- a/prototype/src/main/resources/templates/register.html +++ b/prototype/src/main/resources/templates/register.html @@ -56,9 +56,9 @@
    -
    +
    -
    +

    Werbung

    @@ -66,9 +66,9 @@
    -
    +
    -
    +
    From 327ba182f5af06f42f43e20a0f58803db59dfe81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Sat, 13 Jun 2020 11:10:20 +0200 Subject: [PATCH 197/203] Fix the cash booking sum on customer order Closes #57 --- .../hso/ecommerce/action/shop/CreateOrderAction.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java index 8b35be8..1b891db 100644 --- a/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java @@ -74,9 +74,15 @@ public class CreateOrderAction { CustomerPayment payment = createPayment(); List bookingList = new ArrayList<>(); - bookingList.add(new CreateBookingAction(latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish()); - bookingList.add(new CreateBookingAction(null, latestUserBooking, new BookingReason(payment), order.totalGrossCent).finish()); - bookingList.add(new CreateBookingAction(latestMainBooking, latestVatBooking, new BookingReason(order), order.totalVatCent).finish()); + Booking purchaseBooking = new CreateBookingAction( + latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish(); + Booking paymentBooking = new CreateBookingAction( + null, purchaseBooking.source /* userAccount */, new BookingReason(payment), order.totalGrossCent).finish(); + Booking vatBooking = new CreateBookingAction( + purchaseBooking.destination /* mainAccount */, latestVatBooking, new BookingReason(order), order.totalVatCent).finish(); + bookingList.add(purchaseBooking); + bookingList.add(paymentBooking); + bookingList.add(vatBooking); WarehouseBooking warehouseBooking = createWarehouseBooking(order); From 9fa2c1f3a7519730cebb51e1ff083ce2bb164aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20F=C3=BCrderer?= Date: Sat, 13 Jun 2020 11:46:16 +0200 Subject: [PATCH 198/203] Allow partial discount on supplier orders Closes #46 --- .../org/hso/ecommerce/supplier/RequestController.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/supplier/src/main/java/org/hso/ecommerce/supplier/RequestController.java b/supplier/src/main/java/org/hso/ecommerce/supplier/RequestController.java index 239a8ac..013daaa 100644 --- a/supplier/src/main/java/org/hso/ecommerce/supplier/RequestController.java +++ b/supplier/src/main/java/org/hso/ecommerce/supplier/RequestController.java @@ -62,10 +62,15 @@ public class RequestController { } int priceNet = a.pricePerUnitNet * order.quantity; - int discount = 0; - if(dailyVolume >= s.discount.minimumDailySalesVolumeNetCent) { - discount = (priceNet * s.discount.percentDiscount) / 100; + int discountableNetAmount = 0; + if (dailyVolume >= s.discount.minimumDailySalesVolumeNetCent) { + // grant discount on the full price + discountableNetAmount = priceNet; + } else if (dailyVolume + priceNet > s.discount.minimumDailySalesVolumeNetCent) { + // grant partial discount + discountableNetAmount = dailyVolume + priceNet - s.discount.minimumDailySalesVolumeNetCent; } + int discount = (discountableNetAmount * s.discount.percentDiscount) / 100; OrderConfirmation confirmation = new OrderConfirmation(); confirmation.articleNumber = order.articleNumber; From 7397698929892092e0a2f7546aceabaf36b82698 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sat, 13 Jun 2020 11:54:25 +0200 Subject: [PATCH 199/203] fix check for missing article --- .../controller/intern/suppliers/SupplierOrderController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java index 952427b..3259a17 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java @@ -74,7 +74,7 @@ public class SupplierOrderController { final Article article = articleRepository.findArticleByArticleOffer(order.ordered).orElse(null); - if (order == null) { + if (article == null) { model.addAttribute("error", "Der bestellte Artikel wurde nicht angelegt, er hätte nicht bestellt werden dürfen."); response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); return listSuppliers(model); From 15616e05f34ab2d2069f4f45bcf7a573bead36f7 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 14 Jun 2020 15:54:56 +0200 Subject: [PATCH 200/203] [HOTFIX] Prevent path directory traversal attack for deploy --- .../controller/shop/ShopArticleController.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 18144f7..bca16ab 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -102,9 +103,14 @@ public class ShopArticleController { Article article = articleRepository.findArticleById(id); if(article.image != null) { - InputStream in = new FileInputStream(article.image.path); - response.setContentType(MediaType.IMAGE_JPEG_VALUE); - IOUtils.copy(in, response.getOutputStream()); + File file = new File(article.image.path); + if (file.getCanonicalPath().startsWith("./data/img/")) { + InputStream in = new FileInputStream(file); + response.setContentType(MediaType.IMAGE_JPEG_VALUE); + IOUtils.copy(in, response.getOutputStream()); + } else { + throw new RuntimeException("Got illegal file path. DB was modified."); + } } } } \ No newline at end of file From 3bb3c5b513ad64841ecbaea05095a221f3c47174 Mon Sep 17 00:00:00 2001 From: CodeSteak Date: Sun, 14 Jun 2020 16:51:59 +0200 Subject: [PATCH 201/203] [HOTFIX] of HOTFIX: Use CanonicalPath for check --- .../ecommerce/controller/shop/ShopArticleController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index bca16ab..b1df109 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -101,10 +101,12 @@ public class ShopArticleController { @PathVariable("id") Long id ) throws IOException { Article article = articleRepository.findArticleById(id); - + if(article.image != null) { File file = new File(article.image.path); - if (file.getCanonicalPath().startsWith("./data/img/")) { + File allowedPath = new File("./data/img/"); + + if (file.getCanonicalPath().startsWith(allowedPath.getCanonicalPath())) { InputStream in = new FileInputStream(file); response.setContentType(MediaType.IMAGE_JPEG_VALUE); IOUtils.copy(in, response.getOutputStream()); From fa610cae3b5594cc289648cd90618883e24465fb Mon Sep 17 00:00:00 2001 From: Hannes Date: Sun, 14 Jun 2020 17:56:09 +0200 Subject: [PATCH 202/203] if no orders show hint --- .../src/main/resources/templates/user/orders/index.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prototype/src/main/resources/templates/user/orders/index.html b/prototype/src/main/resources/templates/user/orders/index.html index 023910d..35187bd 100644 --- a/prototype/src/main/resources/templates/user/orders/index.html +++ b/prototype/src/main/resources/templates/user/orders/index.html @@ -21,7 +21,10 @@
    -
    +
    +

    Mit diesem Account wurden noch keine Bestellungen getätigt.

    +
    +

    From 985169117dd1f1b8afcbaa34558108875d251ac6 Mon Sep 17 00:00:00 2001 From: Hannes Date: Sun, 14 Jun 2020 18:19:59 +0200 Subject: [PATCH 203/203] fix nullpointer in settings --- .../java/org/hso/ecommerce/controller/UserController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 8a90d43..97c5bf6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -1,6 +1,7 @@ package org.hso.ecommerce.controller; import org.hso.ecommerce.action.user.UpdateUserSettingsAction; +import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.repos.shop.CustomerOrderRepository; @@ -38,6 +39,9 @@ public class UserController { ) { long userId = (long) session.getAttribute("userId"); User user = userRepository.findById(userId).get(); + if(user.defaultDeliveryAddress == null){ + user.defaultDeliveryAddress = new Address(); + } model.addAttribute("user", user); return "user/settings";