master merged into feature/customer_orders
This commit is contained in:
commit
bcbebe8e8d
|
@ -0,0 +1,36 @@
|
|||
package org.hso.ecommerce.components;
|
||||
|
||||
import org.hso.ecommerce.entities.booking.PaymentMethod;
|
||||
import org.hso.ecommerce.entities.user.User;
|
||||
import org.hso.ecommerce.repos.user.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class AdminInitializer {
|
||||
|
||||
@Autowired
|
||||
private final UserRepository userRepository = null;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
Optional<Integer> numberOfEmployees = userRepository.numberOfEmployees();
|
||||
if (numberOfEmployees.orElse(0) == 0) {
|
||||
// create first admin user
|
||||
User firstAdmin = new User();
|
||||
firstAdmin.created = new Timestamp(System.currentTimeMillis());
|
||||
firstAdmin.name = "admin";
|
||||
firstAdmin.defaultPayment = new PaymentMethod();
|
||||
firstAdmin.defaultPayment.creditCardNumber = ""; //set empty number
|
||||
firstAdmin.email = "admin";
|
||||
firstAdmin.isActive = true;
|
||||
firstAdmin.isEmployee = true;
|
||||
firstAdmin.setPassword("admin");
|
||||
userRepository.save(firstAdmin); //save to DB
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,23 +10,21 @@ import javax.annotation.PostConstruct;
|
|||
@Component
|
||||
public class SlotInitializer {
|
||||
|
||||
@Autowired
|
||||
private final SlotRepository slotRepository = null;
|
||||
@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");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ public class RegisterController {
|
|||
@RequestParam("username") String username, @RequestParam("password") String password,
|
||||
@RequestParam("password2") String password2, @RequestParam("salutation") String salutation,
|
||||
@RequestParam("name") String name, @RequestParam("address") String address,
|
||||
@RequestParam("type") String type, // TODO store
|
||||
@RequestParam("ad") String ad, // TODO store
|
||||
HttpSession session) {
|
||||
Optional<User> user = userRepository.findByEmail(username);
|
||||
if (user.isPresent()) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.hso.ecommerce.controller;
|
|||
import org.hso.ecommerce.action.user.CreateDeliveryData;
|
||||
import org.hso.ecommerce.action.user.UpdateUserSettingsAction;
|
||||
import org.hso.ecommerce.api.RestServiceForDelivery;
|
||||
import org.hso.ecommerce.entities.shop.Address;
|
||||
import org.hso.ecommerce.entities.shop.CustomerOrder;
|
||||
import org.hso.ecommerce.entities.user.User;
|
||||
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
|
||||
|
@ -47,6 +48,9 @@ public class UserController {
|
|||
) {
|
||||
long userId = (long) session.getAttribute("userId");
|
||||
User user = userRepository.findById(userId).get();
|
||||
if(user.defaultDeliveryAddress == null){
|
||||
user.defaultDeliveryAddress = new Address();
|
||||
}
|
||||
model.addAttribute("user", user);
|
||||
|
||||
return "user/settings";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hso.ecommerce.controller.cronjob;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.GregorianCalendar;
|
||||
|
@ -37,6 +38,11 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
interface ICronjob {
|
||||
/**
|
||||
|
@ -63,10 +69,22 @@ interface ICronjob {
|
|||
* @param controller Back-reference that allows to use repositories.
|
||||
*/
|
||||
void executeAt(Calendar time, CronjobController controller);
|
||||
|
||||
/**
|
||||
* Get a name for this cronjob, that can be presented to the user in the frontend.
|
||||
*
|
||||
* @return A german name of this cronjob.
|
||||
*/
|
||||
String getDisplayName();
|
||||
}
|
||||
|
||||
@Component
|
||||
class Reorder implements ICronjob {
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Nachbestellung";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar nextExecution(Calendar reference) {
|
||||
if (reference.get(Calendar.HOUR_OF_DAY) >= 8) {
|
||||
|
@ -215,6 +233,7 @@ class ScheduledCronjob {
|
|||
}
|
||||
|
||||
@Component
|
||||
@RequestMapping("intern/cronjobs")
|
||||
class CronjobController {
|
||||
private static final Logger log = LoggerFactory.getLogger(CronjobController.class);
|
||||
|
||||
|
@ -328,4 +347,42 @@ class CronjobController {
|
|||
public void onPostConstruct() {
|
||||
new Thread(this::runCronjobExecutionLoop).start();
|
||||
}
|
||||
|
||||
class ManualCronjob {
|
||||
public final String identifier;
|
||||
public final String visibleName;
|
||||
|
||||
public ManualCronjob(String identifier, String visibleName) {
|
||||
this.identifier = identifier;
|
||||
this.visibleName = visibleName;
|
||||
}
|
||||
}
|
||||
|
||||
private List<ManualCronjob> listAllCronjobs() {
|
||||
ArrayList<ManualCronjob> entries = new ArrayList<>();
|
||||
for (Entry<String, ICronjob> job : cronjobs.entrySet()) {
|
||||
entries.add(new ManualCronjob(job.getKey(), job.getValue().getDisplayName()));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String cronjobOverview(Model model) {
|
||||
model.addAttribute("jobs", listAllCronjobs());
|
||||
return "intern/cronjobs/index";
|
||||
}
|
||||
|
||||
@PostMapping("/run/{identifier}")
|
||||
public String runCronjob(Model model, @PathVariable("identifier") String identifier) {
|
||||
ICronjob jobToExecute = cronjobs.get(identifier);
|
||||
if (jobToExecute != null) {
|
||||
jobToExecute.executeAt(new GregorianCalendar(), this);
|
||||
model.addAttribute("info",
|
||||
String.format("Der Cronjob \"%s\" wurde ausgeführt.", jobToExecute.getDisplayName()));
|
||||
} else {
|
||||
model.addAttribute("error", "Der Cronjob konnte nicht gefunden werden.");
|
||||
}
|
||||
model.addAttribute("jobs", listAllCronjobs());
|
||||
return "intern/cronjobs/index";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.awt.image.BufferedImage;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -171,15 +172,22 @@ public class InternArticleController {
|
|||
if (imageID.isPresent()) {
|
||||
article.image = imageRepository.findImageById(imageID.get()); // add existing img to article
|
||||
} else {
|
||||
|
||||
Path targetPath = Paths.get("./data/img/");
|
||||
if (Files.notExists(targetPath)) {
|
||||
Files.createDirectories(targetPath);
|
||||
}
|
||||
|
||||
// write new img file to disk
|
||||
Files.newOutputStream(Paths.get("./data/img/", fileName)).write(imgFile.getBytes());
|
||||
Files.newOutputStream(Paths.get(targetPath.toString(), fileName)).write(imgFile.getBytes());
|
||||
// create new img
|
||||
Image newImage = new Image();
|
||||
newImage.path = "./data/img/" + fileName; // set new file to new img
|
||||
imageRepository.save(newImage); // save new img
|
||||
article.image = newImage; // set new img to article
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
setDefaultImage(article); // if upload failed, reset to default img
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
package org.hso.ecommerce.controller.intern.customers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.hso.ecommerce.controller.intern.accounting.AccountingController;
|
||||
import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult;
|
||||
import org.hso.ecommerce.entities.booking.Booking;
|
||||
|
@ -13,12 +9,13 @@ import org.hso.ecommerce.repos.booking.BookingRepository;
|
|||
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
|
||||
import org.hso.ecommerce.repos.user.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
|
@ -104,18 +101,18 @@ public class CustomersIndexController {
|
|||
) {
|
||||
if (!password.equals(password2)) {
|
||||
request.setAttribute("error", "Passwörter stimmen nicht überein!");
|
||||
return "/intern/customers/id";
|
||||
return "intern/customers/id";
|
||||
}
|
||||
User user = userRepository.findById(id).get();
|
||||
if (!user.validatePassword(password)) {
|
||||
request.setAttribute("error", "Die Passwörter stimmen nicht mit dem Original überein!");
|
||||
return "/intern/customers/id";
|
||||
return "intern/customers/id";
|
||||
}
|
||||
user.setPassword("12345");
|
||||
userRepository.save(user);
|
||||
request.setAttribute("info", "Passwort wurde auf 12345 geändert!");
|
||||
|
||||
return "/intern/customers/id";
|
||||
return "intern/customers/id";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -100,11 +101,18 @@ public class ShopArticleController {
|
|||
@PathVariable("id") Long id
|
||||
) throws IOException {
|
||||
Article article = articleRepository.findArticleById(id);
|
||||
|
||||
|
||||
if(article.image != null) {
|
||||
InputStream in = new FileInputStream(article.image.path);
|
||||
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
File file = new File(article.image.path);
|
||||
File allowedPath = new File("./data/img/");
|
||||
|
||||
if (file.getCanonicalPath().startsWith(allowedPath.getCanonicalPath())) {
|
||||
InputStream in = new FileInputStream(file);
|
||||
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
|
||||
IOUtils.copy(in, response.getOutputStream());
|
||||
} else {
|
||||
throw new RuntimeException("Got illegal file path. DB was modified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -109,7 +109,7 @@ public class ShopCheckoutController {
|
|||
) {
|
||||
|
||||
if (shoppingCart.getRevision() != cartRevision) {
|
||||
request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher die Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut.");
|
||||
request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher konnte der Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut.");
|
||||
response.setStatus(HttpServletResponse.SC_CONFLICT);
|
||||
return "shop/checkout";
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ import org.hso.ecommerce.repos.shop.CategoryRepository;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -45,6 +47,9 @@ public class ShopSearchController {
|
|||
return "error/404";
|
||||
}
|
||||
|
||||
return "/shop/search";
|
||||
// Show term in search box
|
||||
model.addAttribute("searchterm", term != null ? term : "");
|
||||
|
||||
return "shop/search";
|
||||
}
|
||||
}
|
|
@ -10,8 +10,9 @@ import java.util.Optional;
|
|||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
|
||||
@Query("SELECT c FROM User c WHERE c.email = :email")
|
||||
Optional<User> findByEmail(String email);
|
||||
@Query("SELECT c FROM User c WHERE c.email = :email")
|
||||
Optional<User> findByEmail(String email);
|
||||
|
||||
@Query("SELECT count(*) FROM User WHERE isEmployee = true")
|
||||
Optional<Integer> numberOfEmployees();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
.alert {
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
background: #c0392b;
|
||||
color: white;
|
||||
animation: blinking 1s linear infinite;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.alert > span {
|
||||
display: inline-block;
|
||||
padding-left: 100%;
|
||||
animation: marquee 10s linear infinite;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@keyframes marquee {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-100%); }
|
||||
}
|
||||
|
||||
@keyframes blinking {
|
||||
60% {
|
||||
color: white;
|
||||
}
|
||||
75% {
|
||||
color: #c0392b;
|
||||
}
|
||||
85% {
|
||||
color: #c0392b;
|
||||
}
|
||||
100% {
|
||||
color: white;
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
|
@ -11,7 +11,8 @@
|
|||
<div class='content-width bar-flex'>
|
||||
<a class="button no-padding" href="/"><img class="logo" th:src="@{/img/ecom-logo-base.svg}"></a>
|
||||
<form class='spacer input-icon secondary' th:action="@{/shop/search}" method="GET">
|
||||
<input type="text" name="term" placeholder="Nach Produkten suchen..."/>
|
||||
<input type="text" name="term" placeholder="Nach Produkten suchen..."
|
||||
th:value="${searchterm != null ? searchterm : ''}"/>
|
||||
<button>Finden</button>
|
||||
</form>
|
||||
<a th:unless="${user}" class="button" th:href="@{/login}">Login</a>
|
||||
|
@ -25,7 +26,10 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<a class="button" th:href="@{/shop/checkout}">Warenkorb (<span th:text="${shoppingCart.itemCount}"></span>)</a>
|
||||
<a class="button" th:href="@{/shop/checkout}">Warenkorb
|
||||
<th:block th:if="${shoppingCart.itemCount > 0}"> (<span th:text="${shoppingCart.itemCount}"></span>)
|
||||
</th:block>
|
||||
</a>
|
||||
</div>
|
||||
<div th:if="${error}" class="error" id="error-msg">
|
||||
<div class="content-width bar-flex">
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
</ul>
|
||||
</li>
|
||||
|
||||
<li><a th:href="@{/intern/cronjobs/}">Cronjobs</a></li>
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de" dir="ltr" xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.75, user-scalable=no">
|
||||
|
||||
<title>Cronjobs</title>
|
||||
<script th:src="@{/js/filterTable.js}"></script>
|
||||
<link rel="stylesheet" th:href="@{/css/ecom.css}"/>
|
||||
<link rel="stylesheet" th:href="@{/css/manual-cronjobs.css}"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav th:replace="fragments/header :: header">Header</nav>
|
||||
<div class="sidebar-layout content-width">
|
||||
<nav></nav>
|
||||
<div>
|
||||
<h1>Cronjobs</h1>
|
||||
</div>
|
||||
</div>
|
||||
<main class="sidebar-layout content-width">
|
||||
<nav th:replace="fragments/intern :: sidebar"></nav>
|
||||
<div class="content-width">
|
||||
<h3>Manuelle Ausführung von Cronjobs</h3>
|
||||
<div class="alert"><span>
|
||||
WARNUNG<br>
|
||||
Manuelles Triggern von Cronjobs kann zu unerwartetem Verhalten führen!<br>
|
||||
Diese Seite ist nur für Debugging-Zwecke gedacht.
|
||||
</span></div>
|
||||
|
||||
<p>
|
||||
<table id="main-table">
|
||||
<tr>
|
||||
<th colspan="7">
|
||||
<input type="text" placeholder="Filtern" class="smaller jsFilterTable full-width"
|
||||
data-target-id="main-table"></input>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Cronjob</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr th:each="job: ${jobs}">
|
||||
<td th:text="${job.visibleName}"></td>
|
||||
<td>
|
||||
<form th:action="@{/intern/cronjobs/run/{identifier}(identifier = ${job.identifier})}" method="post">
|
||||
<input class="button smaller" type="submit" value="Ausführen" />
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
<footer th:replace="fragments/footer :: footer"></footer>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -23,7 +23,7 @@
|
|||
<div class="sidebar-layout content-width">
|
||||
<nav></nav>
|
||||
<div>
|
||||
<h1 th:text="|Kunde ${user.id}"></h1>
|
||||
<h1 th:text="|Kunde ${user.id}|"></h1>
|
||||
|
||||
<script th:src="@{/js/back.js}"></script>
|
||||
<div class="back" data-group="intern" data-insert="true"></div>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<input class=" full-width" type="text" id="title" name="title" required="required" pattern=".+" th:value="${ArticleID.title}"/>
|
||||
</p>
|
||||
<p class="s">
|
||||
<label for="ref_disabled">Refernzierter Artikel</label>
|
||||
<label for="ref_disabled">Referenzierter Artikel</label>
|
||||
<input class="" type="text" id="ref_disabled" th:value="${ArticleID.offerID}" disabled/>
|
||||
|
||||
<input type="hidden" id="ref_hidden" th:value="${ArticleID.offerID}" name="ref-article" />
|
||||
|
@ -38,7 +38,7 @@
|
|||
<div class="spacer"></div>
|
||||
<div class="m">
|
||||
<p>
|
||||
<label for="img">Bild Hochladen</label>
|
||||
<label for="img">Bild hochladen</label>
|
||||
<input class="full-width" type="file" id="img" name="img"/>
|
||||
</p>
|
||||
<p>
|
||||
|
@ -88,7 +88,7 @@
|
|||
</p>
|
||||
<p>
|
||||
Der Wert wird nur für zukünftige Lagerbuchungen verwendet.
|
||||
Bei Problemen kann können Einheiten aus- und wieder eingebucht werden.
|
||||
Bei Problemen können Einheiten aus- und wieder eingebucht werden.
|
||||
<!-- TODO: set link g-->
|
||||
</p>
|
||||
<p>
|
||||
|
|
|
@ -54,25 +54,8 @@
|
|||
<textarea rows="5" class="full-width" type="text" name="address" id="address"
|
||||
placeholder="Optional: Zusatz Optional: Unternehmen Straße Hausnummer Postleitzeit Ort Land"></textarea>
|
||||
</div>
|
||||
<fieldset>
|
||||
<input type="radio" id="type-priv" name="type" value="priv">
|
||||
<label for="type-priv">Ich bin Privatkunde</label><br>
|
||||
<input type="radio" id="type-bus" name="type" value="bus">
|
||||
<label for="type-bus">Ich bin Geschäftskunde</label><br>
|
||||
</fieldset>
|
||||
<div>
|
||||
<h2> Werbung </h2>
|
||||
</div>
|
||||
<div>
|
||||
<fieldset>
|
||||
<input type="radio" id="ad-y" name="ad" value="y">
|
||||
<label for="ad-y">Ich möchte Werbung erhalten.</label><br>
|
||||
<input type="radio" id="ad-n" name="ad" value="n">
|
||||
<label for="ad-n">Ich möchte keine Werbung erhalten.</label><br>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div>
|
||||
<button class="full-width" type="submit" name="action" value="login">Registeren</button>
|
||||
<button class="full-width" type="submit" name="action" value="login">Registrieren</button>
|
||||
<a th:href="@{/terms}">
|
||||
Unsere AGBs finden sie hier.
|
||||
</a>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
<p> Jetzt Kunde werden und viele Vorteile sichern,
|
||||
wie z.B. personalisierte Empfehlungen. </p>
|
||||
<p>
|
||||
<a class="button" th:href="@{/register}">Registieren</a>
|
||||
<a class="button" th:href="@{/register}">Registrieren</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
<main class="sidebar-layout content-width">
|
||||
<nav th:replace="fragments/customer :: sidebar"></nav>
|
||||
<div class="content-width detailflex">
|
||||
<div th:each="order: ${orderDeliveryDataMap}">
|
||||
<h2 id="20202701" th:text="|Bestellung vom ${order.getKey().formatCreated()}|" />
|
||||
<div th:if="${orderDeliveryDataMap.isEmpty()}">
|
||||
<h2>Mit diesem Account wurden noch keine Bestellungen getätigt.</h2>
|
||||
</div>
|
||||
<div th:if="${!orderDeliveryDataMap.isEmpty()}" th:each="order: ${orderDeliveryDataMap}">
|
||||
<div>
|
||||
<table class="key-value">
|
||||
<tr>
|
||||
|
|
|
@ -91,7 +91,11 @@
|
|||
</div>
|
||||
<div>
|
||||
<div class="input-icon">
|
||||
<input class="full-width" type="text" name="creditCardNumber" th:value="${user.defaultPayment.creditCardNumber}"/>
|
||||
<input class="full-width"
|
||||
type="text"
|
||||
name="creditCardNumber"
|
||||
th:value="${user.defaultPayment != null && user.defaultPayment.creditCardNumber != null ? user.defaultPayment.creditCardNumber : ''}"
|
||||
placeholder="Jetzt Kreditkartennummer hinterlegen."/>
|
||||
<button> Kreditkartennummer ändern</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"id" : "mda",
|
||||
"name" : "MDA",
|
||||
"discount" : {
|
||||
"minimumDailySalesVolumeNetCent": 20,
|
||||
"percentDiscount": 3
|
||||
},
|
||||
"articles": [
|
||||
{
|
||||
"title": "MDA Nezyr 7",
|
||||
"manufacturer": "MDA",
|
||||
"articleNumber": "n7",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 29990,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "MDA Nezyr 5",
|
||||
"manufacturer": "MDA",
|
||||
"articleNumber": "n5",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 19700,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "MDA Nezyr 3",
|
||||
"manufacturer": "MDA",
|
||||
"articleNumber": "n3",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 8990,
|
||||
"shouldBeAdvertised": false
|
||||
},
|
||||
{
|
||||
"title": "MDA Nezyr Threadcracker 3990",
|
||||
"manufacturer": "MDA",
|
||||
"articleNumber": "ntc3990",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 404900,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "MDA Radon RX 5700",
|
||||
"manufacturer": "MDA",
|
||||
"articleNumber": "rrx5700",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 43624,
|
||||
"shouldBeAdvertised": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"id" : "nanosoft",
|
||||
"name" : "Nanosoft",
|
||||
"discount" : {
|
||||
"minimumDailySalesVolumeNetCent": 50,
|
||||
"percentDiscount": 0.5
|
||||
},
|
||||
"articles": [
|
||||
{
|
||||
"title": "Nanosoft Doors 10",
|
||||
"manufacturer": "Nanosoft",
|
||||
"articleNumber": "d10",
|
||||
"vatPercent": 7,
|
||||
"pricePerUnitNet": 2099,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Nanosoft Interior Pro 7",
|
||||
"manufacturer": "Nanosoft",
|
||||
"articleNumber": "ip7",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 90780,
|
||||
"shouldBeAdvertised": false
|
||||
},
|
||||
{
|
||||
"title": "Nanosoft Ybox Two",
|
||||
"manufacturer": "Nanosoft",
|
||||
"articleNumber": "ybox2",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 23500,
|
||||
"shouldBeAdvertised": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"id" : "outtel",
|
||||
"name" : "Outtel",
|
||||
"discount" : {
|
||||
"minimumDailySalesVolumeNetCent": 20,
|
||||
"percentDiscount": 1
|
||||
},
|
||||
"articles": [
|
||||
{
|
||||
"title": "Outtel Core o7",
|
||||
"manufacturer": "Outtel",
|
||||
"articleNumber": "o7",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 40000,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Outtel Core o5",
|
||||
"manufacturer": "Outtel",
|
||||
"articleNumber": "o5",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 25000,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Outtel Core o3",
|
||||
"manufacturer": "Outtel",
|
||||
"articleNumber": "o3",
|
||||
"vatPercent": 7,
|
||||
"pricePerUnitNet": 8000,
|
||||
"shouldBeAdvertised": false
|
||||
},
|
||||
{
|
||||
"title": "Outtel Core o9",
|
||||
"manufacturer": "Outtel",
|
||||
"articleNumber": "o9",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 55000,
|
||||
"shouldBeAdvertised": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"id" : "pear",
|
||||
"name" : "Pear",
|
||||
"discount" : {
|
||||
"minimumDailySalesVolumeNetCent": 100,
|
||||
"percentDiscount": 2
|
||||
},
|
||||
"articles": [
|
||||
{
|
||||
"title": "Pear iMobile 10",
|
||||
"manufacturer": "Pear",
|
||||
"articleNumber": "iM10",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 31500,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Pear iPlate Plus",
|
||||
"manufacturer": "Pear",
|
||||
"articleNumber": "ipp",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 44900,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Pear iDonalds 9",
|
||||
"manufacturer": "Pear",
|
||||
"articleNumber": "id9",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 234800,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Pear GroundPods",
|
||||
"manufacturer": "Pear",
|
||||
"articleNumber": "gp",
|
||||
"vatPercent": 7,
|
||||
"pricePerUnitNet": 13599,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Pear Donalsbook Pro",
|
||||
"manufacturer": "Pear",
|
||||
"articleNumber": "dbp",
|
||||
"vatPercent": 7,
|
||||
"pricePerUnitNet": 145900,
|
||||
"shouldBeAdvertised": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"id" : "sumsang",
|
||||
"name" : "Sumsang",
|
||||
"discount" : {
|
||||
"minimumDailySalesVolumeNetCent": 300,
|
||||
"percentDiscount": 2
|
||||
},
|
||||
"articles": [
|
||||
{
|
||||
"title": "Sumsang Universe S10",
|
||||
"manufacturer": "Sumsang",
|
||||
"articleNumber": "us10",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 59000,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Sumsang Universe S10e",
|
||||
"manufacturer": "Sumsang",
|
||||
"articleNumber": "us10e",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 70231,
|
||||
"shouldBeAdvertised": false
|
||||
},
|
||||
{
|
||||
"title": "Sumsang DumbTV",
|
||||
"manufacturer": "Sumsang",
|
||||
"articleNumber": "dtv",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 38395,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Sumsang UniverseWatch",
|
||||
"manufacturer": "Sumsang",
|
||||
"articleNumber": "uw",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 20494,
|
||||
"shouldBeAdvertised": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"id" : "techdealer",
|
||||
"name" : "Tech Dealer",
|
||||
"discount" : {
|
||||
"minimumDailySalesVolumeNetCent": 100,
|
||||
"percentDiscount": 2
|
||||
},
|
||||
"articles": [
|
||||
{
|
||||
"title": "TROPIC Gehäuselüfter",
|
||||
"manufacturer": "TROPIC",
|
||||
"articleNumber": "tgl",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 459,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Pirate PC-Netzteil",
|
||||
"manufacturer": "Pirate",
|
||||
"articleNumber": "ppcn",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 9355,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Pirate Void Elite RGB Wireless Gaming Headset",
|
||||
"manufacturer": "Pirate",
|
||||
"articleNumber": "pvergbwgh",
|
||||
"vatPercent": 7,
|
||||
"pricePerUnitNet": 10999,
|
||||
"shouldBeAdvertised": false
|
||||
},
|
||||
{
|
||||
"title": "Aeroheat CYLON PC-Geh<65>use",
|
||||
"manufacturer": "Aeroheat",
|
||||
"articleNumber": "acpcg",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 3999,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Illogitech C270 Webcam",
|
||||
"manufacturer": "Illogitech",
|
||||
"articleNumber": "ic270w",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 3499,
|
||||
"shouldBeAdvertised": true
|
||||
},
|
||||
{
|
||||
"title": "Illogitech Z607 Surround Sound Lautsprecher",
|
||||
"manufacturer": "Illogitech",
|
||||
"articleNumber": "iz607ssl",
|
||||
"vatPercent": 19,
|
||||
"pricePerUnitNet": 9495,
|
||||
"shouldBeAdvertised": false
|
||||
}
|
||||
]
|
||||
}
|
Reference in New Issue