From d66b3d1385877d9df7c0483e38ab1b80660fd659 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 9 May 2020 23:01:25 +0200 Subject: [PATCH 01/31] 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 @@ - Kamera + KameraÖ 100,50 EUR (84.45 EUR) Úberwachung, Elektronik From 2135c486467aad221bcef59cefba614387936232 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sun, 10 May 2020 15:26:50 +0200 Subject: [PATCH 02/31] 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"> + Bild Name Preis (Netto) Kategorien - Lagerbestand (Aktiv) - Artikel - Id (bearbeiten) - - - - - KameraÖ - 100,50 EUR - (84.45 EUR) - Úberwachung, Elektronik - 301 - 5051 - 890 - - - - Earbuds - 63,95 EUR - (53,73 EUR) - Kopfhörer, Elektronik - 12 - 840 - 13850 - - - - USB-Magic Light - 11,90 EUR - (10,00 EUR) - Sonstiges, Elektronik - 3 - 8401 - 5784 - - - - 3D Magic Stativ - 15,99 EUR - (13.44 EUR) - Úberwachung, Elektronik - 4 - 2135 - 4564 - - - - Ersatzfernbedinung - 7,95 EUR - (6.68 EUR) - Úberwachung, Elektronik - 0 - 4565 - 4566 + Lagerbestand + Angebot + ID + + + + + + + + + + + + +

From 7a42122a9a1bd340773b478e8b9833bb0f694e4d Mon Sep 17 00:00:00 2001 From: Seil0 Date: Sun, 10 May 2020 15:31:20 +0200 Subject: [PATCH 03/31] 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 8a28aabaf3bc619d26efcc3ff45ae4b1f502d9b8 Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 1 May 2020 12:19:52 +0200 Subject: [PATCH 04/31] 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 05/31] 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 06/31] 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 07/31] 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 08/31] 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 09/31] 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 19/31] 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 10/31] 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 11/31] 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 12/31] 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 13/31] 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 14/31] 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 15/31] 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 16/31] 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 17/31] 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 18/31] 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 20/31] 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 21/31] 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 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 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 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 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 zcmb7qWmsEXv}S@!fkLsOrMR}Zdy#}fAXxEIptuBgD228-1S_sVin~LLyGtQRad-Ej z-`xA%xp(Hz?BqGmdCs!Vd7(BVLv|o|11wb0fbn9Ilvq$kPv`E2t*|WK6C(R0KmWEp#lN_77Q$O zOf>Ar`EVZVb)Eu%sHiBYXz1uzxHtrljUFq|0O*7mm_&5kSkGQc6O*VJ!+2yqMQ4(d z(?jFRe~n`^F!FwIbcSb*PLK(zYihgXR8+Px^9j7s&@yof_#7A$n?3e8H1*>e0nz`i z)V~EC6%7Lu3k8V%SPgoV5{Qa|hJuEPgOB=8tG`ndqSIjzaZ5i_!+dEBBPQX|_yjK} zqvw@@MrZyST_)w@S9f&E%C2Y|8(%tyCYDcN_1GTIq){~Q!>srZCw0RlN2{r8lV1UC0s67W zpXgqsfdDArNGY))V6U?{>4rgz)`a2f3gl+O&N)5)vLSutQY!y~J~Ky?JbO{{-tIdc z`-&4U;*EoYxDHs2@=Cwp%5?~G(Mjm`;D)xzf}oM_-sEm$pP;qH=!)C=`07qhV%_0t z@m$i~XXb}@_s#Get)F-6@rUWP$9_HoODC2(tKSAMIVrTrUc#5alU z8-Bm#@ZX={w`Xup<#LA6_3~^O=|hF$v!41BQo$iOk!5JN z?1a;GTJ2xAWL)+cnFYqcxS}pz!+DS)^IOhtigc2x0$gjwU+SP*wn)lTJ=wzc+pg== z@SX<%v1MuBv0vpBZg0-L0i=-jELpT;+AkS#wsHUaG_|M8M?JUv-Yw=XEhP!EHgR44 z0C;!Azqr2T+rXc1r7cl3OM?3)REdgz{DN>%)>e zlHB`XdIIIK=eD*H$*6r9Z&Uuqsd-<`HnH%nj+Kp73}EUtfezd=RTDT&Pof0T+5(gR zo~z#bir3`AeQTo+olC zN26HTz6y21#bk#~z2Ye?OF5g!D>c#^zpdMIjmPY|E)i@zT`I zUKs?RXCLOS;O1eCbuS--*|H6Flv^_valbg*3c!yFx?DI+pqk@bT<$6wF(>V6Mxdba zB+)nJ?w@&FLfZN{cVn$rO6He@PWc9TP&64Q1Xr{a=-Y?qwcXsV!wUMI76{DG65fji z+r;G4c;e{4N4-7+!B7g>7QUP!Gid`2?5&Vq4t7K6lm${F_R8RSZ(sF zdS0@IY3!8$*6?BxvYRM6>A3@|3cB#7Z!H`b-?6P3Ijf%3OR^2s&h1lI(j^?o7ns3h zhgPV?_`_7SbnRmNRNBCiY$-nqxIWHZy-ZR*U-z1Dh$&1y2qNBG;n&8OFDBtnrp7^MsR5pPuKZ z!3Q+OVo9}jQog}hC7!hpN=+xt`lf;=T7t+2J zH9WWd=@~6Zwe&xn1wy~X&J!;<`FA)q=bM-<n!fFSj6{FHY=uIi*?uZ0*c-*qdllh;octM>BRJxiG?eGO{=#WufM> zYetM_s5v(j$Kn@USF=oa;&TD{uP+2o!y`^EGCU5QhvR(iNgZ6B1}X=KFNM8J`?ijo zn<42xjTFV2no6nCXqQZmNpI8HS2t>91JVWzvea|+D{?z>X6E3DD$-<^cYSGKuxt?Z zI!*uxaQ5rRL0FyIlx7pefWsWF)2(k#&HKCBFj8nsPt;85!1l!PGP;}R-H>p3iY>hR z+!}d@oN4U(VXd~woI6`G$dkji==`OY(j_Z{hxk|l8$2v_{KCuH;K;Wi6Nbjpx3I)I zKHL{JX1UV$%6YWr67$Wn9Z7+qplgKckL+V}<*JpmA?uIHx1BLjxodOwJZm;p~ZBGO3Kg3ePx`VfCCrj1MD+G3D3q)elQkk|H zpPT>k9@S~{^PaaG;Pa8pTi&}3XQwq@tY(~xcLwT+HK*)ahFx977ZB2_3r{+voP45}2 zE(|J8r;1f`Avd?tELr2!eJjy&&0ZMia#$Kh798f}b0TznMJ=9+T3P98gyN}wSea(j zv?AdbxU;uyW{e2+I@f+-GuTG4dz2Dx&A~PsDQh_Z%WMK2207%taOu>YaQyLuEur8t zT-dzBd(hc_V)6O!W<{I97#UE?IPr-7g%L{>UXV{ z`Cy#HTTPUB0(Yc^l1fn=>(8)N01?Ts5D7TpmyP8_Qc)hOTqgtK5-D(%Rih*^_ovig zVO!H;EUu}bjW=v{b?GuWC)SKPfy1RuNpu5irbRP6@!NmmGzEV8Awe9Rr;4%_0osw= zZP}4Us?U}5et4<0!_0%tGU490`S?DMunvPvEu&Q05U{gpM|jFCndSdy%LS%`l$e5B zV9Wgj`*vDAMc?g+Vq7d#l>~i7A!!+ARijN1;P#n$MGrp|3V`|-ed>t`+el;2sPp<% z#8vg%zzD3ONeYEvGmqEkb?*v`hWapXYf*ISQ44T~SqhS_R?Ni@w-7-)YG(AV1k$Q8R$x-X*;29R-r1*)E~%zlic*@p0q4CPY2=_7@HiM z9t+j6zi$q6Q#t+GHH*+zS#0mnGGivrCRu>SxP!cW`Z;snzFV}NYj{gKFW8WT@c_st zcGs!N9e>FRZ)M=w*_2gM6Z2<-5O(pZ9xtWh2YM?dgGcff^he{#QeySshV}&110ht_ z9op)LHV1p#ntL=l3W-

R}&{oAFv+rc=;HJ^LcclyB+=)o&81C;4vhXHZDT#vF3; zNlc$gqfzu`{!9^s;y6Gpi*oZSA+565gHOx-%!@9;R zw}JA)Uude7c&t!&9Jf0VVZv# ziki^={Pq;B>#r*yyENvyFUPj0Q1=5hal#4f0kELyy71}ttM#3}M2D|2%tfOv_sVat z;GW5reLm|5X7Wa_`1SrX0b|$A9~?7@K0Q0|=NMoF22d@Ec4e5H4fX5S_K;4_AIMQR z5_T}#8Q6%kQ$qhbC1sFH<*hSPcxb(?3E|~fw52Bx9oMI;=4U38&|G#;32r6S7&aK| z64xhhtnnYGA2fdVZF5^$HJPZfrn(Eb|Myv`S8|pUVDriGA!SSTC zL3ffk$A$0|L-mvoS=pMM2V|V%n>i~3@du)%<)Y$K2RpKRmHhX!Wu+j=-c!lk2SA(6 zqD)z_ z*h}_IU*%)%$2grg0asO}@9q5LfMx&qbC{}>gC@yVdvYZdb@Cc6t!|#`VkW?@R2^N? zbc+{f=c^vt7KekK?5P;D8VfLVvRzB#TwlGTwd%=8GQ+ke5B(otCGi!eS?fW*2Y}k? zIO(^esRYzgwHZI804;u>E;J8$_!}?T7!WMIi=zvgV1R2$dz!((b@-@uGnr|&nH4nXb zBDU=N(gyed7lPxiFJ^Ms&B}<4ebwFcTyfJTTX*obkZ~G!!oZ2TML}O+(Kd~eF;79= zh+fl+%RDKyoH_2IJ9Z9g6m}K=p+QK#x~!|l;7_q&H$U18M{%!Xss|Sdi%1Sb0BL2L zYHU7?DoXn2mU@Fh$3AQSfg#n0F%VND(>0>OHZLfwq_p765lOMA$i!^Pj*4YHsOkc0 z=^UFz1+{*~DYoRyNoEpCb$%va*7ChiuB^lAr4fBwt$SY|)cX)HTuG1AO&^mRq(xa+ zlxWc}-|gbLJIEVzhGZa>3iKBYh3*dGxrfa;HgM+}uFO0VX6sk?=>EGK4y2q6fjYkp z;Ep&eTJ>-X&X5pMUJ&Kz@MWm>@PfwqI$B_B{#Eao8U&Z3&3^PwC#r0;9MCPyVdKseQxYr?4paaoa5yvZzsIy#=)3Y2N9p$v@C5= zs^oHX&AeSHotvPY(z3iISusj`0C=dMQy?#mH`^t9tsem4uVz79$-gQDM4xySZ@jm6 zEwt{V9w_kSqPNY@Hxui-A`%Y!6Fu-To5f;I*-3LOyPykd04)|czZUfk_^_e-M@fXO zx$7&7^r_tfT<>&I{JNA+0n?IeAUk(LLW07IV22HKSW0nwanqMx_Eq0x^Oy73^aFsx zsH!SisUGQ5cTKm!M@pyQpD`DzyUfH}E>xi&Kfbi2g-;BZ{C-{=sS&1>KR`Zl{62Qzbi-$0ZNJdt9Tzj zNWx}=yMql{W19!SIg@|qW;ah^qsQis_4RntTDpDH z_1-%GdHXvBhr=~Uo!To*>R zJpja12CS1*CLcBE88pP#QoGIn{5V4#{7nl5r+I}K$j1Bw$CnSY8gTLt(juJK8Me3c zUH^3*%YwBYZiJsxUwwImRJ|t7ytcp#mGUeAfgQ5|=P@Cu0cTV5R5aJtSoHY^KuOh% z*JPn*KeS>Ser=t1twaa;< zshoGV(jitoz`lZj`<87_g3@x6v~}ch(+yyQ3N)`;Ay+eZxnysVF1E!r)$%E3#xK=A zbp&SqJdU4Xv$SQW>tGee!2yAW2zc18&plmS_qdGg>~_e#_!&p@#tFoG23f6LM|8tO z+}96p#A-?B^bI|-rUvtB zmi>)h{erZC(%p$>=B;=84JB*EyEfe}trVHYV$D+0l2?0ES*KCcof>{cf45h1ByjFB zVB@;;7A?1-lj7g!N0r1?a%df;gOmTA5Q5`)9B#tuq z2XdBP;os5h-O$o>@hk9(G3V2HN)l@L4rNxnpIr;)(I$bWfnEL!CX<;HhE-MCH&(cf z%=bLh7eb$;yi_Js^!etH-uYWH^A-rMOa;J|sSB+f@NnGvDL-_rsp@;g8_tBMSw-yM z2&1QwYf^9RJOhXgB$?j0jotbOP}shJYj`@IdyG)T_C~XgtQO$6WEJJtW%>4@;^ClA z2^3H8<{I2dAUUVPD{wv_%F(gqNrk%k<4QYY#-lQLGeHJf?p-9CJ6*oSn8jZd<;um7 zXmr$uGzv>n_K({1A2wGje168KY{Ii%Bwcm;&EkYP%X+Psq+f>OZ~U*to7{3SVW58m*a-!qgv$%5OT1dle2sl1qTZD zecSk+6~K&*<^=X;6{E-Q8Dfj9iCJvbA7 zlvTFyd??YAteXBzJ0-e@195=IKLDILx4JwE6avHY|ClK-tR(V#$%gPFZ7y}4batvC z)1zFyHjRYZX%TgX4}gRvim`-O5Z#cS$tM2zo8GmSy?A;{*J-&;ycZ|4D&{3|uqE{i zGl{Qn1M|dKK?;EkrpGkqiTuTNU-c)dlLJdudNRj^te@|s93<>rW4srQqHZjFL#_LsLNd1_TdSv zukDnA@Nk8do>VV^p<-X5unD+zMV-oQc+DsKB?YQcsPEK;?Ooc+NqVFUdvXhVgC-&k zBqZk{FUrue=wMR@846T zwloylZT;}7S6W@+fwvEHZE@JlRrBhgN z)R+gnSNUdsZ`Q77s(^6X4%gDYuk0ly6lH;(z7?c<^4Y&OCh#gYFUQopKyRqnO+oC^ zUER@|u)Ejtm9?se@04`}BGG&;kJZ^-#TvI!@$uba1xrcXywx_9NoA#>pX_-#ugBL9--X0(jN2#aR)shEw z)U(0(4d@^e@tvuUz+Mg{vV%UJ~YpXDWcEo7TBTKOA3|5n1vl-Bjg1* z>lXMgRWzLTGI+S}6_5Eaq@^zO2Y~@;i_dU|jo470o}G!j2Q}6A@qG+gC9uLYDl8q9 zQZAaE9n81g^jze|hx4|Pz*z`HemI%S)y<}YaVCg}D|?y6E%2U8>VQ5O>tls{xk33e zr%q?}?v_22)d4M%0l{E?^zC>MSk5;P#KsM-H>>PP=gLL^TThGp*Zj*M!4+7Sgz3Xc zC~8tHz)cHpb7jh5%Ft@xr9z}{lwY1fLAzg%z&{rDJ*-(D=5qZ2KuT6c%<$t4jeaml z6V3FXk?(#_mI@CQZFdRlft3ZfOKcFMPHtbhyd>25CaaUZz|IB8 zu}tA*y*BJF&cWlV*@1K>fr5c4AG4vFse~OwyPDziWT&?<#{6(O3_gw5@Gmhx!w>*w zx)=CTjwltOdSC(PIaYJL+D3cV;|s4d{`<^AeJlQKtrp>I@@|X#J(8dbkTWvx!}vM3 zNyFlxYN=g1{T*GyBn?~=Zt)0|@3ov?WeT5IUr0_$9(C+~sn6Kw{@*Ug$I|Z~_d6#( z$MP+|gER)wOWIjH32$s85^wW|GY(^ANW6MgBCJXtjC({I4(tGTSv-lscRZ&+eVHJM z!Q-9(#HJ1RqyH2|7E5xj-Ql<4oDP@LjTeza7C6}Nr^v!3g=;hWU~xGS3@|DpkrB$I z2S8H{Kg}qmCzXa$ze=WUL)ur&G==Z=k;XeObq;bBFOWt1PL~Km~EQCtNld*#ci&pJ= z6YrN43uYWp&Mma^V`0ky$+U*W3h5Hj_EGdp_G4MDvMtm|bVCw`G?O%W#qLyR0z6nv zx@B(L*p*OUQk$h2KQec#Bt2(^M)Xv(cw2YPKmoLtVl81##z^*DqS@85`FhQqd|&po zrfq*ev96xM@*S^69)+$S>XGU}_xt!UaA}gsc zvy-X=FG6oQw0mvO7yUj`Fmb!~yN%+!NX_D`Fso!~(YHG6BSJita>8U(VPpUx;=H3& zAR>VU#fK}gO`?emQ?HkpYvWxz^tRy!x_UNdRqommnnLbVuVb^BIewHWi6S_}YWb2t zy)D5_{Z0mkH$rxUi&~I+!Q5rJw+UWDy9>HHXrEHx9&ktBH2p&6>J=60n%Zz?%@)nD zV-Upy0D7v|HGMJ3WnxQS(6rJSdMA7^z5BbuC*2jiRTE)Q+7Ka&aU~wuZO5T4G}11u zuMqqW2xef;dx5E*rdu24p0%idxVNW%_~#^{(w=d|YHjVp7BOy`pq*MYZxxgEH?EC2IEly~63GtCWK{SgJVL^qFez}uq#L1ivh9!_ z%zQfX?41X|+v3SGK5REbf}jvv5(gER&O9vL2iSq{o{#Uvr)?6z9MiVFpdFUdWqTy* z{!0I!QF-9!i3;&&_N7qXfNv@*D{WG@r|3r2PsAeLZAhumv!F2(V;CUPC{V*0y~BY4 zQTrre>;6k$70A(4pR?=?N$_D9BO(9t!?v2?rTYoc$!Y!Pm zB-Ae?E5sOJo^CoRbBY&fbs0j(6DW$qaX^&D1|o#XObAMXT^s4tfy6zIt|KLLSR}rb zD!sT^5U@BYDuEb9Bj^D@qLaaUekGUZ&$6~5rTk{6xy2yP;c2b$&>sTUnvtxZVjeJW zBR=9!pY}4inEHA=H+-v*yo;j5KWr+V`Oi8n4T%sru;2!*{rA-dWYCK=LQ<8pa&%)@ z+I1)5GKXmBI%1-*PKeRpG|eGLJ-2NG&VxBysX+r@vWmO3h@&zxq=sI5ch~qw&7Q?4 zdFKT@0I+dxwGHv|eQ-K~Jdy6TEx$SRKeUL2I;xfqSP;DG#gT4n`&#!|>iwca%t)G{ zjQ=tw$%0AX%94~*c9V?%uj~9LJCIACSPX#`hlqAFm0ru7Pm|ry^dSp8Bepx+hHasm zWYhH$ZD9ywJpCJOPeif4|5}eX2%t*pWp}DSD45Okrum0hty%9uKP&Yz1r<=JmumP` z5HO*Buf@NqCewM;6@)O*SSv4iwSl(o|7mb{sj?CTbq;yKal5H*WWI~rJtO7tY$ne( zO7{<$b7pzlthhxM5swOnLRAu~&6WXWUX@w61K%wnVJ%tLNr;YKDf=vGhq&ONT%KIuwKU`$|{{AsLv-9Tv&c*t@C?+4l74wa`FTp4fJxWY}q5uyT;Q|5l1icS|!X^T_cM%l_y< zslum!wvTOSvjW+Oqz?ufRqG5X7|Q#%caPTuNBjFeQ!#Z~krK`jQm@&0D>NI=S(J05 z>K}8i`jj$vQ8$@EL2oyDgzoH#PZhewT<*Szm&W(9lJQylk1Hgt zPePG74;tT!W{y zzOHYPfQ1_mag3@CM4+3NvVt9=gi}g4f7sK!gv@H{Xsk|``{a=uE;r4#V<%O=k6`*E z(n6#_-^*{rQu_-hh5*{a36?LZuR2vM&YZ@FeV40-LFA)6mV>0QpZMEPE~~vIS~<$C|m| zhHZu-+7;jb(NH9x9lyAh7#+JRoLZpQ0ouHqPccMp^7p24wGyato$)Qf4cN8Dd# zlEi1k81v;uFNbW=YvE;{?@upBh=e=HLsd8WN1MjO;bbG}MkP-7;xz)y1&xO)xnA4&6lkU5>P!@H zy~|Cu&)SyZ^ctR7=o!ziv@WK#R0?z}_{#c4=BUT~>+?=i+NV2y`~_z^k+&Y%x68CT zgyxOXTUGtQ;sxaEZuh1HCh-o!6Jbp5w8V|bkL=SO6dQw9K3X1^7xGsF5*56ojZ(Wg z^BmtB@|;fJ6yRtXT81W(QR}lUgWA7JNS$oT(R?cLp30jvS=l3b09+fNMGuc9A?^9? z8eAxS{Kjj0j(;rg~>tH~cjW%@`MsgxV}e7TvEpx#kp zZ=BSwGO(UxTa_7=Tx-j0o$kz=TUxR=%3C?X=w|y`q!Ku#0y*`2%&LmpgrlN|{Bx5- zXPJFZxz?1+7OS}8#~k$(UB#S#YMi!=t|vUbPNVCHKKUa4{Sm3v3YfcHB*r+UHj>6q3skGwupB1Z%15( z#mUFv%Sd{M)5W>ZxJcL*y+3qe%nynt&V4APiYn3VZ>|04tOOPu%l(H+$YX~Tb|+F=CjQFVaC6VGIM z7p)kWIhB-#TQcI2kR0wousG&8v_Y;@RQW@YXCz^0zkThP!nQM`Q;s$QJ%Iskl(G#y zpnWs5(PQjzpq=v?ZWw1)CN9DQmS%%^{$A>H*B}NSMt=^FcT2fk1m&r>AMxBJmky7N z9sSvpua+c~#hpF6##8dE-_{G?Cg!Z04Nm9)KfxL{Sa|EkDD(gzbN)gu6B1vFa-7D+ zcbLYIM}q0Jg`dLiu62sEF6FVNnd2I++*i`OmRv_H>ra5h6S!zc6BY+eK9G3Huf99q z9%Q=7D?Xn$%3l|lEE##pTwf!v165RPOg}K3L*9)I2t5DrIYdiF=jvEzO^=}^g zXQ)A}P^~luGd8LU?v$zeiRyV--PKEz`1i*#AY+Lek3YXf?1J+)`}|Ntz)G&k9^I|| z%nl|Q`4zW+`FpW^Jd*sg-pG=Y^I=uJ91H`FYo&tzki!Vmm3~Pxpn@CDJTFWzV7R8- zNL|gec87f@c>B9@-P1tYn{EZj>zQLFf4c!o5hrVkQBfBzfy8EgN(~D)LBj6s^=E<2 zEX*6i%V4!MeQS^y+z}mX?2%V9Y4k4UF4~W=ix*eRpOly^usy1j;O~revy+F<)?Lmm zIk2)pDD-i)wQ%BP zq@VwM-UX8aIj^q_|GtYF@f7tuttp;;K4qbTT+3Q{uM_NM-mIRfEJl=5@P;rd2>pm* z1zrJ&u^IY2uLQ9+P-s2ckgwWmwst<)_oO?Y(q`hwUXYg^BKXD6+bF(cl12)~`AQK4 zbP?3HOs;#UKOul#K=gabhpRlcKo*|tLDT8139JzKtll%>`BJPm7*GF`BlTca7?3dq zorT~<2DD5(6t&NPK}2}+&BC3?wlEC5ANk9YHG^|=>n~UPzmcx@yaO*uw+1%&sfLU^ z=;tuo0O$7>aV-vLGl*>N5*!8i~&YsKZtMV-FsbxU!?Sm7a%e?VrxaN`8jprf2 zLi-y^G7~Zw^Q9b8H!`L-oT|=A71^Pt9c|ee*m(fRTQqj1{T9j}Npf5(wNYJ9*gGm- zh{N}scUUd(*>IUZ%s)C5B2Gb#EkwyOR`{8Z=-+u|e9&PPnn(8iW`Lnkt8z5Sm$+Hm zW}-%b0{XK0LK6S&&3Ci13LNTvhp!b?QR|LrsLR@f-HQPVU2z>TiLE0Yt z((1OhyAb&xCV@blHKKo(WzKtd zrhPBSpDJK;46$`$s$AUUfGH6saOqkuoeD+_Z7UmRi$33vdjRa)*skY%b@cy&{6uZ3 z6khLOhQ5074H9ml`)e&aqCVeEMe8ts5~O6Ye_g!L1^TdMU^o^IwqzcRZ4i&1*&l8E z!MOL?jrwg~-P&P22@$E%r;4;Zu`cf)Cn}e33xu_NJiS6~-TrVlXzW4HJXdo4{>-}Y zeOUHaMI9L-f#?X%9gt?oV=<{3NiNYb`Hy(%x3j$mrh(K;gILX}A+P*11m*i&?}H2Ztz5rWou`*=r{ejtd|6wNYw~1MN_)zr zAPiOob^A`^Br~v+qf6lp&VGjJ?Upyxgd+05{61w$IwFtX-GqE1+p9t&>hGbh?D~xr! zCW|esu&z>Yvf^f%sEr)^9v{Zlm>NxDlc&#9ki=G#!{Q z#p~Cep07&qZtyYF&Noxansc~Cch8eyfnQ9+z~(S5WcAwt@MqIo^RL3>t0m6{JT!w= zGjZKmi*lwGLEMP3Lc?>ut^RmrzV!I6#c}Pr>qr^oE12H?_xK(d)(~thvZ7FLD%_JT zYhQOk4?MK(%y7Isu%{pY^3gJ))a*|-tGaRF^|Fc&wh4hFgkd!fep%{gswCNRZ*lgr z8`NUuwGq2Ys1njnQ5INC*XIhzmXXHp+N5DEE4|_`T8Yx(5>K!O8$vsz4t+RCSk|(; zJ8*tVkPwFb86ieJf9t}(W>8s`iZE8JmL2U$#*2dM9hidLMKZ*H#YZ11X0STGc(|m)O*)+h)=y<&9<*hb+E!Kp81-W5Tuk&aelyRb6=t(E8RCWuuS=+ z9To5lU6oYgU+}@-wL9!MQ%3Q{8jS5y6Wx|Q%24#=?s-1)B2>q3VYMbHw)ACs?Jmo! zk2*h6XG8THgV-;axY@hulRv*>UU2ZuR-M&=K_S0QuTLxO(f95Y-e+eN75xkXWbl97 zo3Zr_wO-Y+cN(>>?2E?ZWvEY~D;Ex8&l5{|?=>u;Y3!b{wUK8pI*!7iqV8a`yKl}gOtzIV=gPbL z!!LP5CepuIfrNJaA03k@*nB_-GMQSf}=7;aCtyzNlX1-zBoH$GWrgAwhn5yxo zLr_6{@Aio`l?CWZT1jzT4mgGzFP!9hC4s7qTauE73@RuWo$PX{Mh*B%DaHoE?3Io4 zQ7#EPUQXAr@k!VnEU4XZo~_stf+pC?7LXMyFuswgs(=JfQ*~p%d%k2rR9%Ql-b+iM zu<7_KZ#PNMwrV|ke2~1s^OsemO)cX9I>tL2I>vWv9Yru&&v4+nMoux>2A(s`HLB_! zZi!Oe{X^)&$B3wRo*K38n&Iu{nZ*!P z=Tgh zi1=0JQ^iSCGPlOx*LL)vgCKcj2pZETXrI%3c(ur4Z>`-R!?>sLYcs#khHQ)QZn-3TFXMje-u~A+7y>*X{s>dCzD7oa&kG(^;|d1&|;M=h1F$ygfL~q zn+X~bK$1|8KTs_Y0~Mlcm|M!*e(LU7;m2A*?JZ8x9~&?5pKBLBBQhf2iYrD}*y6(! zGFXxw-__-e{#`puez&}6_wCzg^2L`C6oqbL42(A$red+QI5=fr#s~7Ryp@5qRlXBL zO{82Dsrbm^&p2;h!5^i4V{V*{S2o zHz{ELzRDdVW&1aC?>`7Pl8b5rf(7j#mn{&lXOg$^+ifS8jn@)m>JazF(2KjEH@n{# zi!RbN^|A+^E}Y@e7`Kf$p%cw&_cE9|ZVV4)IUlsQ!o&-kX1f=!6aB=ceIEctY7YSV zytC?v=I2SwbN)6Ih=E-4%1V78=WE$wgBo&&pb-+IE z<1;WLn^9^;_n&WcC`F$xf`aaiRA%&Xu@|>L#kLaWdBI+OM;g9Fxty3#9!skD@`Fkh;#T2r87?Z z2C=W+k!LXd%cTC}?w3**Ot$|{&`eo59O=GRcQJP!%Q#m2{g@;WIet!Uw3+Bx(pjf^ zh7sK$Y2GNc?DDFDD5J_DjVV3|B`(wZ6Nsk0$6ua$MJ4PivywL*gN!)kV?{7^ca5MG z#=#dp+WI&?DlBwQZ@34Icj|daLPVx!@#5x7L=N&Ca+2&NcR*%kV4lwrZAZwr(i^o2 zMd3T@(<0z1as2gb%3_-R?EmX(pse=50kY9e*S2$f3K(2Y%^7PGi+P;F&SR+ zvi87K$MqF@SzjDe^QO5(Lyuy~zFnM?2f0a4`VzxvW;{OX6zW+vWSJb(#P7VnplgeY zuXwG9jhaYR;RuW3j?v^!h*w)ba|lCtiv1IT{x2frKMvY|JY~1H)-0e7I@5(al!87F zKW5uZDj4~Z`YFsRS+;i5pWcOKHd<20)fhs7Ftv*8i29x)3gyc8)qOS0Q^mCJwKCpm zye?yW@#F?ySqtJEqI_m{jx-Rp_?)Ze5J|`y3zE^0I(&kkHZWyi2F9Vf8=zG!R2yy| 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 @@