diff --git a/.gitignore b/.gitignore index 3b718f8..c52eb76 100644 --- a/.gitignore +++ b/.gitignore @@ -89,3 +89,7 @@ local.properties # SQLite prototype/*.db + +prototype/images +prototype/data + diff --git a/delivery/.gitignore b/delivery/.gitignore new file mode 100644 index 0000000..6c01878 --- /dev/null +++ b/delivery/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/delivery/build.gradle b/delivery/build.gradle new file mode 100644 index 0000000..4b72078 --- /dev/null +++ b/delivery/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'org.springframework.boot' version '2.2.7.RELEASE' + id 'io.spring.dependency-management' version '1.0.9.RELEASE' + id 'java' +} + +group = 'org.hso.ecommerce' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +apply plugin: 'java' +apply plugin: 'idea' +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation('org.springframework.boot:spring-boot-starter-test') { + exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' + } + compile 'org.json:json:20190722' +} + +test { + useJUnitPlatform() +} diff --git a/delivery/gradle/wrapper/gradle-wrapper.jar b/delivery/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f3d88b1 Binary files /dev/null and b/delivery/gradle/wrapper/gradle-wrapper.jar differ diff --git a/delivery/gradle/wrapper/gradle-wrapper.properties b/delivery/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..feb1126 --- /dev/null +++ b/delivery/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 19 15:50:06 CEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/delivery/gradlew b/delivery/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/delivery/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/delivery/gradlew.bat b/delivery/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/delivery/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/delivery/settings.gradle b/delivery/settings.gradle new file mode 100644 index 0000000..6a6c7ab --- /dev/null +++ b/delivery/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'delivery' diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/App.java b/delivery/src/main/java/org/hso/ecommerce/supplier/App.java new file mode 100644 index 0000000..bb8b42d --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/App.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.supplier; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java new file mode 100644 index 0000000..ed62af7 --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/RequestController.java @@ -0,0 +1,32 @@ +package org.hso.ecommerce.supplier; + + +import org.hso.ecommerce.supplier.data.Delivery; +import org.hso.ecommerce.supplier.data.DeliveryManager; +import org.hso.ecommerce.supplier.data.ReturnStatus; +import org.springframework.web.bind.annotation.*; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@RestController +public class RequestController { + + + + @PostMapping("/newDelivery") + public String supplier(HttpServletResponse response, HttpServletRequest request, @RequestBody Delivery delivery) { + DeliveryManager.getInstance().add(delivery); + + return delivery.getUuid().toString(); + } + + @GetMapping("/status") + public ReturnStatus searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) { + + Delivery delivery = DeliveryManager.getInstance().getDeliveryByeID(trackingID); + + return new ReturnStatus(delivery.getStatus(),delivery.getEstimatedArrival()); + } +} diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java new file mode 100644 index 0000000..7a7b9cf --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/Delivery.java @@ -0,0 +1,69 @@ +package org.hso.ecommerce.supplier.data; + + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.UUID; + +public class Delivery { + + private String[] states = {"Bestellung eingegangen","Bestellung auf dem Weg","Lieferung erfolgreich"}; + private int[] timeBorder = {4,24}; + + private String name; + private String address; + private String estimatedArrival; + private Date creationTime; + private UUID uuid; + + public Delivery(String name, String address) + { + this.name = name; + this.address = address; + this.uuid = UUID.randomUUID(); + this.creationTime = new Date(); + SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); + this.estimatedArrival = formatter.format(addDays((Date)this.creationTime.clone(),1)); + } + + + public String getStatus() + { + Date now = new Date(); + Long timeNow = now.getTime(); + Long creationTime = this.creationTime.getTime(); + + Long diff = timeNow - creationTime; + double hour = (((diff / 1000.0) / 3600.0)); + + for (int i = 0; i < timeBorder.length; i++) { + + if(hour < timeBorder[i]) + return states[i]; + } + + return states[timeBorder.length]; + } + + public String getEstimatedArrival() { + + return estimatedArrival; + } + + private Date addDays(Date date, int days) + { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.DATE, days); + return cal.getTime(); + } + + public UUID getUuid() { + return uuid; + } + + public String getName() { + return name; + } +} diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/DeliveryManager.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/DeliveryManager.java new file mode 100644 index 0000000..bd22a56 --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/DeliveryManager.java @@ -0,0 +1,34 @@ +package org.hso.ecommerce.supplier.data; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class DeliveryManager { + + private List deliveryList; + private static DeliveryManager deliveryManager; + + private DeliveryManager() + { + deliveryList = new ArrayList<>(); + } + + public static DeliveryManager getInstance () { + + if (DeliveryManager.deliveryManager == null) { + DeliveryManager.deliveryManager = new DeliveryManager(); + } + return DeliveryManager.deliveryManager; + } + + public boolean add(Delivery delivery) + { + return deliveryList.add(delivery); + } + + public Delivery getDeliveryByeID(String uuid) + { + return deliveryList.parallelStream().filter(d -> d.getUuid().equals(UUID.fromString(uuid))).findAny().get(); + } +} diff --git a/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java b/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java new file mode 100644 index 0000000..16eaae7 --- /dev/null +++ b/delivery/src/main/java/org/hso/ecommerce/supplier/data/ReturnStatus.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.supplier.data; + +public class ReturnStatus { + + private String status; + private String estimatedArrival; + + public ReturnStatus(String status, String estimatedArrival) { + this.status = status; + this.estimatedArrival = estimatedArrival; + } + + public String getStatus() { + return status; + } + + public String getEstimatedArrival() { + return estimatedArrival; + } +} diff --git a/delivery/src/main/resources/application.properties b/delivery/src/main/resources/application.properties new file mode 100644 index 0000000..eca0f47 --- /dev/null +++ b/delivery/src/main/resources/application.properties @@ -0,0 +1,2 @@ +server.address=::1 +server.port=8082 diff --git a/prototype/.gitignore b/prototype/.gitignore index 59529a7..52b353d 100644 --- a/prototype/.gitignore +++ b/prototype/.gitignore @@ -1,4 +1,4 @@ -./test.db +e-commerce.db ./build ./gradle ./out diff --git a/prototype/build.gradle b/prototype/build.gradle index 63716e3..8d138b9 100644 --- a/prototype/build.gradle +++ b/prototype/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.2.RELEASE") + classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.7.RELEASE") } } @@ -27,7 +27,7 @@ dependencies { // implementation 'org.springframework.session:spring-session-jdbc' implementation 'com.github.gwenn:sqlite-dialect:0.1.0' implementation 'org.springframework.boot:spring-boot-devtools' - implementation 'org.xerial:sqlite-jdbc:3.28.0' + implementation 'org.xerial:sqlite-jdbc:3.31.1' implementation 'org.yaml:snakeyaml:1.26' testCompile("org.springframework.boot:spring-boot-starter-test") } @@ -36,5 +36,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/gradle/wrapper/gradle-wrapper.jar b/prototype/gradle/wrapper/gradle-wrapper.jar index f6b961f..5c2d1cf 100644 Binary files a/prototype/gradle/wrapper/gradle-wrapper.jar and b/prototype/gradle/wrapper/gradle-wrapper.jar differ diff --git a/prototype/gradle/wrapper/gradle-wrapper.properties b/prototype/gradle/wrapper/gradle-wrapper.properties index 7c4388a..4c5803d 100644 --- a/prototype/gradle/wrapper/gradle-wrapper.properties +++ b/prototype/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/prototype/gradlew b/prototype/gradlew index cccdd3d..83f2acf 100755 --- a/prototype/gradlew +++ b/prototype/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` diff --git a/prototype/gradlew.bat b/prototype/gradlew.bat index e95643d..24467a1 100644 --- a/prototype/gradlew.bat +++ b/prototype/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/prototype/scripts/addarticles.sql b/prototype/scripts/addarticles.sql new file mode 100644 index 0000000..e81c174 --- /dev/null +++ b/prototype/scripts/addarticles.sql @@ -0,0 +1,15 @@ +/* +* add a supplier first +*/ + +INSERT INTO article_offers ("manufacturer", "article_number", "price_per_unit_net", "title", "vat_percent", "should_be_advertised", "cheapest_supplier_id") +VALUES ("McDonalds", "1", 4242, "McPizza", 7, 1, 1); + + + +/* +* There is no need for the add article, you can add one form the UI on the offerd article page +*/ +INSERT INTO articles ("related_id", "shop_price_per_unit_net_cent", "warehouse_units_per_slot", "should_reorder", "reorder_max_price", "title", "description", "image_id") +VALUES (1, 19.99, 10, 1, 15, "Huge Hamburger", "This huge Hamburger is awesome!", NULL); + diff --git a/prototype/scripts/addsupplier.sql b/prototype/scripts/addsupplier.sql new file mode 100644 index 0000000..a56f4ed --- /dev/null +++ b/prototype/scripts/addsupplier.sql @@ -0,0 +1,6 @@ + +INSERT INTO suppliers ("api_url", "name", "uuid") +VALUES ("https://api.com", "Conrad", "fdfdfg4gdfgdf4gfg"); + + + diff --git a/prototype/scripts/addsupplierorders.sql b/prototype/scripts/addsupplierorders.sql new file mode 100644 index 0000000..6c19af3 --- /dev/null +++ b/prototype/scripts/addsupplierorders.sql @@ -0,0 +1,3 @@ + +INSERT INTO supplier_orders ("created", "delivered", "number_of_units", "price_per_unit_net_cent", "total_price_net", "ordered_id", "supplier_id") +VALUES ('0', '0', '42', '42', '42', '1', '1'); 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..edde2cb --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/booking/CreateBookingAction.java @@ -0,0 +1,31 @@ +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.created = new java.sql.Timestamp(System.currentTimeMillis()); + 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/cronjob/ReadSupplierDataAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReadSupplierDataAction.java new file mode 100644 index 0000000..50930b8 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReadSupplierDataAction.java @@ -0,0 +1,83 @@ +package org.hso.ecommerce.action.cronjob; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +import org.hso.ecommerce.api.SupplierService; +import org.hso.ecommerce.api.data.Article; +import org.hso.ecommerce.api.data.Supplier; + +public class ReadSupplierDataAction { + private List suppliers; + + public static class ArticleIdentifier { + public final String manufacturer; + public final String articleNumber; + + public ArticleIdentifier(String manufacturer, String articleNumber) { + this.manufacturer = manufacturer; + this.articleNumber = articleNumber; + } + + @Override + public int hashCode() { + return Objects.hash(manufacturer, articleNumber); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ArticleIdentifier)) { + return false; + } + ArticleIdentifier otherId = (ArticleIdentifier) other; + return this.manufacturer.equals(otherId.manufacturer) && this.articleNumber.equals(otherId.articleNumber); + } + } + + public ReadSupplierDataAction(List suppliers) { + this.suppliers = suppliers; + } + + public static class Offer { + public final org.hso.ecommerce.entities.supplier.Supplier dbSupplier; + public final Supplier apiSupplier; + + public Offer(org.hso.ecommerce.entities.supplier.Supplier dbSupplier, Supplier apiSupplier) { + this.dbSupplier = dbSupplier; + this.apiSupplier = apiSupplier; + } + } + + public static class Result { + public final ArrayList supplierData; + public final HashMap cheapestOffer; + + public Result(ArrayList supplierData, HashMap cheapestOffer) { + this.supplierData = supplierData; + this.cheapestOffer = cheapestOffer; + } + } + + public Result finish() { + ArrayList suppliers = new ArrayList<>(); + HashMap price = new HashMap<>(); + HashMap cheapest = new HashMap<>(); + for (org.hso.ecommerce.entities.supplier.Supplier supplier : this.suppliers) { + SupplierService service = new SupplierService(supplier.apiUrl); + Supplier apiSupplier = service.getSupplier(); + suppliers.add(apiSupplier); + for (Article article : apiSupplier.articles) { + ArticleIdentifier identifier = new ArticleIdentifier(article.manufacturer, article.articleNumber); + Integer previousPrice = price.get(identifier); + if (previousPrice == null || article.pricePerUnitNet < previousPrice) { + price.put(identifier, article.pricePerUnitNet); + cheapest.put(identifier, new Offer(supplier, apiSupplier)); + } + } + } + + return new Result(suppliers, cheapest); + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java new file mode 100644 index 0000000..23599d8 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/ReorderAction.java @@ -0,0 +1,107 @@ +package org.hso.ecommerce.action.cronjob; + +import java.sql.Timestamp; +import java.util.HashMap; + +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer; +import org.hso.ecommerce.api.SupplierService; +import org.hso.ecommerce.api.data.Order; +import org.hso.ecommerce.api.data.OrderConfirmation; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ReorderAction { + private static final Logger log = LoggerFactory.getLogger(ReorderAction.class); + + private Article article; + private Integer[] orderedAmounts; + private Integer undeliveredReorders; + private int amountInStock; + private HashMap cheapestOffer; + private HashMap articleOffers; + + public ReorderAction( + Article article, Integer[] orderedAmounts, + Integer undeliveredReorders, + int amountInStock, + HashMap cheapestOffer, + HashMap articleOffers + ) { + this.article = article; + this.orderedAmounts = orderedAmounts; + this.undeliveredReorders = undeliveredReorders; + this.amountInStock = amountInStock; + this.cheapestOffer = cheapestOffer; + this.articleOffers = articleOffers; + } + + private int null_to_zero(Integer input) { + return input == null ? 0 : input; + } + + private int calculateAmountToReorder() { + // Algorithm as described in the documentation + int a = null_to_zero(orderedAmounts[0]); + int b = null_to_zero(orderedAmounts[1]); + int c = null_to_zero(orderedAmounts[2]); + + int x = Math.max(Math.max(a, b), c); + int y = Math.min(Math.min(a, b), c); + + int n = 6 * x - 2 * y; + if (n < 3) { + n = 3; + } + + int i = null_to_zero(undeliveredReorders); + int l = amountInStock; + + return n - i - l; + } + + public SupplierOrder finish() { + if (!article.shouldReorder) { + return null; + } + + int amount = calculateAmountToReorder(); + if (amount <= 0) { + return null; + } + + ArticleIdentifier identifier = new ArticleIdentifier(article.related.manufacturer, article.related.articleNumber); + Offer offer = cheapestOffer.get(identifier); + if (offer == null) { + log.info("Could not order \"" + article.title + "\" because there is no supplier delivering it."); + return null; + } + + ArticleOffer articleOffer = articleOffers.get(identifier); + org.hso.ecommerce.api.data.Article apiArticle = offer.apiSupplier.findArticle(identifier.manufacturer, + identifier.articleNumber); + if (apiArticle.pricePerUnitNet > article.reorderMaxPrice) { + log.info("Could not order \"" + article.title + "\" because it is currently too expensive."); + return null; + } + + Order order = new Order(); + order.manufacturer = articleOffer.manufacturer; + order.articleNumber = articleOffer.articleNumber; + order.quantity = amount; + order.maxTotalPriceCentNet = apiArticle.pricePerUnitNet * amount; + + OrderConfirmation confirm = new SupplierService(offer.dbSupplier.apiUrl).order(order); + SupplierOrder createdOrder = new SupplierOrder(); + createdOrder.created = new Timestamp(System.currentTimeMillis()); + createdOrder.supplier = offer.dbSupplier; + createdOrder.ordered = articleOffer; + createdOrder.numberOfUnits = confirm.quantity; + createdOrder.pricePerUnitNetCent = confirm.pricePerUnitNetCent; + createdOrder.totalPriceNet = confirm.totalPriceNetCharged; + return createdOrder; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java new file mode 100644 index 0000000..e21869e --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/cronjob/UpdateOffersAction.java @@ -0,0 +1,63 @@ +package org.hso.ecommerce.action.cronjob; + +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer; +import org.hso.ecommerce.api.data.Article; +import org.hso.ecommerce.entities.supplier.ArticleOffer; + +public class UpdateOffersAction { + private List offers; + private HashMap cheapestOffer; + + public UpdateOffersAction(List offers, HashMap cheapestOffer) { + this.offers = offers; + this.cheapestOffer = cheapestOffer; + } + + private HashMap mapOffers() { + HashMap map = new HashMap<>(); + for (ArticleOffer offer : offers) { + ArticleIdentifier identifier = new ArticleIdentifier(offer.manufacturer, offer.articleNumber); + map.put(identifier, offer); + } + return map; + } + + public List finish() { + HashMap availableOffers = mapOffers(); + + // Reset all advertise-flags and supplier relations first. They are set again below. + for (ArticleOffer offer : availableOffers.values()) { + offer.shouldBeAdvertised = false; + offer.cheapestSupplier = null; + } + + for (Entry cheapestOffer : cheapestOffer.entrySet()) { + String manufacturer = cheapestOffer.getKey().manufacturer; + String articleNumber = cheapestOffer.getKey().articleNumber; + ArticleOffer currentOffer = availableOffers.get(cheapestOffer.getKey()); + if (currentOffer == null) { + currentOffer = new ArticleOffer(); + currentOffer.manufacturer = manufacturer; + currentOffer.articleNumber = articleNumber; + offers.add(currentOffer); + } + Article currentOfferedArticle = cheapestOffer.getValue().apiSupplier.findArticle(manufacturer, + articleNumber); + currentOffer.title = currentOfferedArticle.title; + currentOffer.vatPercent = currentOfferedArticle.vatPercent; + currentOffer.cheapestSupplier = cheapestOffer.getValue().dbSupplier; + currentOffer.pricePerUnitNet = currentOfferedArticle.pricePerUnitNet; + + // Set advertise-flag if any supplier wants it to be set + if (currentOfferedArticle.shouldBeAdvertised) { + currentOffer.shouldBeAdvertised = true; + } + } + return offers; + } +} 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..854be3a --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/CreateOrderAction.java @@ -0,0 +1,201 @@ +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() throws ArticleNotInStockException { + CustomerOrder order = createOrder(); + CustomerPayment payment = createPayment(); + + List bookingList = new ArrayList<>(); + Booking purchaseBooking = new CreateBookingAction( + latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish(); + Booking paymentBooking = new CreateBookingAction( + null, purchaseBooking.source /* userAccount */, new BookingReason(payment), order.totalGrossCent).finish(); + Booking vatBooking = new CreateBookingAction( + purchaseBooking.destination /* mainAccount */, latestVatBooking, new BookingReason(order), order.totalVatCent).finish(); + bookingList.add(purchaseBooking); + bookingList.add(paymentBooking); + bookingList.add(vatBooking); + + WarehouseBooking warehouseBooking = createWarehouseBooking(order); + + return new Result( + order, + warehouseBooking, + bookingList + ); + } + + private WarehouseBooking createWarehouseBooking(CustomerOrder order) throws ArticleNotInStockException { + 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, item.article); + bookingPosition.booking = booking; + + booking.positions.add(bookingPosition); + + if (needed == 0) { + break; + } + } + + if (needed > 0) { + throw new ArticleNotInStockException(item.article); + } + } + + 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; + } + } + + public static class ArticleNotInStockException extends Exception { + private Article article; + + public ArticleNotInStockException(Article article) { + super("The quantity of article '" + article.title + "' is not in stock."); + this.article = article; + } + + public Article getArticle() { + return article; + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/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/shop/GetRandomArticlesAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java new file mode 100644 index 0000000..62c70c7 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/GetRandomArticlesAction.java @@ -0,0 +1,19 @@ +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
(); + int loopcount = Math.min(quantity, advertisedArticles.size()); + for (int i = 0; i < loopcount; i++) { + int index = (int) (Math.random() * advertisedArticles.size()); + randomisedArticles.add(advertisedArticles.remove(index)); + } + return randomisedArticles; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/shop/SearchByTermAction.java b/prototype/src/main/java/org/hso/ecommerce/action/shop/SearchByTermAction.java new file mode 100644 index 0000000..cfdfffe --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/shop/SearchByTermAction.java @@ -0,0 +1,40 @@ +package org.hso.ecommerce.action.shop; + +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class SearchByTermAction { + + public static List
searchByTerm(String sourceTerm, ArticleRepository repository) { + + List terms = Arrays.asList(sourceTerm.split(" ")); + List
resultArticles = new ArrayList<>(); + + terms.forEach(term -> { + List
titleArticles = repository.getArticlesByTermInTitle(term); //search in Title + titleArticles.forEach(article -> { + if(!resultArticles.contains(article)){ + resultArticles.add(article); + } + }); + + }); + + terms.forEach(term -> { + List
descArticles = repository.getArticlesByTermInDescription(term); //search by Term + descArticles.forEach(article -> { + if(!resultArticles.contains(article)){ + resultArticles.add(article); + } + }); + + }); + + return resultArticles; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/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/action/user/UpdateUserSettingsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java new file mode 100644 index 0000000..ba17181 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/user/UpdateUserSettingsAction.java @@ -0,0 +1,83 @@ +package org.hso.ecommerce.action.user; + +import org.hso.ecommerce.entities.booking.PaymentMethod; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.user.UserRepository; + +public class UpdateUserSettingsAction { + + private User user; + private UserRepository repository; + + public UpdateUserSettingsAction(User user, UserRepository repository) { + this.user = user; + this.repository = repository; + } + + public UpdateResult updateEmail(String newMail) { + UpdateResult result = new UpdateResult(false); + if (!newMail.contains("@")) { + result.errorString = "Ändern der Email-Addresse nicht möglich. Bitte versuchen Sie es erneut."; + } else { + this.user.email = newMail; + this.repository.save(this.user); + result.updated = true; + } + return result; + } + + public UpdateResult updatePassword(String oldPassword, String password1, String password2) { + UpdateResult result = new UpdateResult(false); + if (this.user.validatePassword(oldPassword)) { + if (password1.equals(password2)) { + if (!password1.equals(oldPassword)) { + this.user.setPassword(password1); + this.repository.save(this.user); + result.updated = true; + } else { + result.errorString = "Das neue Passwort entspricht dem alten Passwort."; + } + } else { + result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut."; + } + } else { + result.errorString = "Das eingegebene alte Passwort stimmt nicht mit dem momentan gespeicherten Passwort überein. Bitte versuchen Sie es erneut."; + } + return result; + } + + public UpdateResult updateShippingInfo(String salutation, String name, String address) { + this.user.salutation = salutation; + this.user.name = name; + this.user.defaultDeliveryAddress.addressString = address; + this.repository.save(this.user); + return new UpdateResult(true); + } + + public UpdateResult updatePaymentInfo(String creditCardNumber) { + UpdateResult result = new UpdateResult(false); + if (creditCardNumber.matches("[0-9]+")) { + this.user.defaultPayment = PaymentMethod.fromCreditCardNumber(creditCardNumber); + this.repository.save(this.user); + result.updated = true; + } else { + result.errorString = "Kreditkartennummer darf nur Zahlen enthalten. Bitte versuchen Sie es erneut."; + } + return result; + } + + public class UpdateResult { + public boolean updated; //if true worked, if false not worked + public String errorString; + + public UpdateResult(boolean updated, String errorString) { + this.updated = updated; + this.errorString = errorString; + } + + public UpdateResult(boolean updated) { + this.updated = updated; + this.errorString = ""; + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CalculateWarehouseStatsAction.java b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CalculateWarehouseStatsAction.java new file mode 100644 index 0000000..ca5c5dc --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CalculateWarehouseStatsAction.java @@ -0,0 +1,73 @@ +package org.hso.ecommerce.action.warehouse; + +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; + +import java.util.HashSet; +import java.util.List; + +public class CalculateWarehouseStatsAction { + + private List entryList; + + public CalculateWarehouseStatsAction(List everyCurrentEntry) { + this.entryList = everyCurrentEntry; + } + + public WarehouseStats finish() { + int numArticles = calculateNumArticles(); + double efficiency = calculateEfficiency(); + double ratioUsedSlots = calculateRatioSlotsUsed(); + + return new WarehouseStats(numArticles, efficiency, ratioUsedSlots); + } + + private double calculateRatioSlotsUsed() { + int used = 0; + + for (WarehouseBookingPositionSlotEntry entry : entryList) { + if (entry.newSumSlot > 0) { + used++; + } + } + + return ((double) used) / entryList.size(); + } + + private double calculateEfficiency() { + double e = 0; + + for (WarehouseBookingPositionSlotEntry entry : entryList) { + if (entry.newSumSlot > 0) { + e += entry.newSumSlot / (double) entry.article.warehouseUnitsPerSlot; + } else { + e += 0; + } + } + + return e / entryList.size(); + } + + private int calculateNumArticles() { + HashSet articleIds = new HashSet<>(); + + for (WarehouseBookingPositionSlotEntry entry : entryList) { + if (entry.newSumSlot > 0) { + articleIds.add(entry.article.id); + } + } + + return articleIds.size(); + } + + private static class WarehouseStats { + public int numArticles; + public double efficiency; + public double ratioUsedSlots; + + WarehouseStats(int numArticles, double efficiency, double ratioUsedSlots) { + this.numArticles = numArticles; + this.efficiency = efficiency; + this.ratioUsedSlots = ratioUsedSlots; + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CreateManuelBookingAction.java b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CreateManuelBookingAction.java new file mode 100644 index 0000000..0a148b2 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/CreateManuelBookingAction.java @@ -0,0 +1,92 @@ +package org.hso.ecommerce.action.warehouse; + +import org.hso.ecommerce.entities.booking.BookingReason; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; + +import java.sql.Timestamp; +import java.util.Date; +import java.util.Optional; + +public class CreateManuelBookingAction { + + private Article article; + private int amount; + private Optional source; + private Optional destination; + private String reason; + + public CreateManuelBookingAction(Article article, int amount, Optional source, Optional destination, String reason) { + this.article = article; + this.amount = amount; + this.source = source; + this.destination = destination; + this.reason = reason; + } + + public WarehouseBooking finish() throws ArticleSlotConstraintFailedException { + WarehouseBooking booking = new WarehouseBooking(); + booking.created = new Timestamp(new Date().getTime()); + booking.reason = new BookingReason(reason); + + if (source.isPresent()) { + + if (source.get().article.id != article.id) { + throw new ArticleSlotConstraintArticleTypeFailedException(); + } + + WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); + bookingPosition.booking = booking; + + bookingPosition.article = article; + bookingPosition.amount = -amount; + bookingPosition.slotEntry = source.get().copyAddAmount(-amount, article); + + if (bookingPosition.slotEntry.newSumSlot < 0 || bookingPosition.slotEntry.newSumSlot > article.warehouseUnitsPerSlot) { + throw new ArticleSlotConstraintFailedException("The quantity of article can only be set in bounds."); + } + + booking.positions.add(bookingPosition); + } + + if (destination.isPresent()) { + + if (destination.get().article.id != article.id && destination.get().newSumSlot > 0) { + throw new ArticleSlotConstraintArticleTypeFailedException(); + } + + WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); + bookingPosition.booking = booking; + + bookingPosition.article = article; + bookingPosition.amount = amount; + bookingPosition.slotEntry = destination.get().copyAddAmount(amount, article); + + if (bookingPosition.slotEntry.newSumSlot < 0 || bookingPosition.slotEntry.newSumSlot > article.warehouseUnitsPerSlot) { + throw new ArticleSlotConstraintFailedException("The quantity of article can only be set in bounds."); + } + + booking.positions.add(bookingPosition); + } + + return booking; + } + + public static class ArticleSlotConstraintFailedException extends Exception { + public ArticleSlotConstraintFailedException(String s) { + super(s); + } + + public ArticleSlotConstraintFailedException() { + super("The quantity of article can only be set in bounds."); + } + } + + public static class ArticleSlotConstraintArticleTypeFailedException extends ArticleSlotConstraintFailedException { + public ArticleSlotConstraintArticleTypeFailedException() { + super("The Article in the slot entry does not match."); + } + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/action/warehouse/SupplierOrderArrivedAction.java b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/SupplierOrderArrivedAction.java new file mode 100644 index 0000000..dc361b0 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/action/warehouse/SupplierOrderArrivedAction.java @@ -0,0 +1,103 @@ +package org.hso.ecommerce.action.warehouse; + +import org.hso.ecommerce.entities.booking.BookingReason; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.supplier.SupplierOrder; +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 SupplierOrderArrivedAction { + + private final Article article; + private final ArrayList warehouseCandidates; + private final SupplierOrder order; + + public SupplierOrderArrivedAction(List warehouseCandidates, SupplierOrder order, Article article) { + this.warehouseCandidates = new ArrayList<>(warehouseCandidates); + this.order = order; + this.article = article; + } + + public Result finish() throws NoSpaceInWarehouseException { + // Sort for most filled slot first; + warehouseCandidates.sort((b, a) -> Integer.compare(a.newSumSlot, b.newSumSlot)); + + int needed = order.numberOfUnits; + + WarehouseBooking booking = new WarehouseBooking(); + booking.created = new Timestamp(new Date().getTime()); + booking.reason = new BookingReason(order); + + for (WarehouseBookingPositionSlotEntry entry : warehouseCandidates) { + int canBeAdded = article.warehouseUnitsPerSlot - entry.newSumSlot; + + if (canBeAdded == 0) { + // this slot is full, skip + continue; + } + + int willBeAdded = Math.min(canBeAdded, needed); + needed -= willBeAdded; + + WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); + + bookingPosition.article = article; + bookingPosition.amount = willBeAdded; + bookingPosition.slotEntry = entry.copyAddAmount(willBeAdded, article); + bookingPosition.booking = booking; + + booking.positions.add(bookingPosition); + + if (needed == 0) { + break; + } + } + + if (needed > 0) { + throw new NoSpaceInWarehouseException(article); + } + + order.delivered = new Timestamp(new Date().getTime()); + return new Result(order, booking); + } + + public static class Result extends Exception { + + private final SupplierOrder order; + private final WarehouseBooking booking; + + public Result(SupplierOrder order, WarehouseBooking booking) { + this.order = order; + this.booking = booking; + } + + public SupplierOrder getOrder() { + return order; + } + + public WarehouseBooking getBooking() { + return booking; + } + } + + + public static class NoSpaceInWarehouseException extends Exception { + private Article article; + + public NoSpaceInWarehouseException(Article article) { + super("The quantity of article '" + article.title + "' does not fit in warehouse."); + this.article = article; + } + + public Article getArticle() { + return article; + } + } + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/api/SupplierService.java b/prototype/src/main/java/org/hso/ecommerce/api/SupplierService.java new file mode 100644 index 0000000..35b87c0 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/api/SupplierService.java @@ -0,0 +1,27 @@ +package org.hso.ecommerce.api; + +import org.hso.ecommerce.api.data.Order; +import org.hso.ecommerce.api.data.OrderConfirmation; +import org.hso.ecommerce.api.data.Supplier; +import org.springframework.web.client.RestTemplate; + +public class SupplierService { + + private final String url; + + public SupplierService(String url) { + this.url = url; + } + + public Supplier getSupplier() { + RestTemplate restTemplate = new RestTemplate(); + + return restTemplate.getForObject(url, Supplier.class); + } + + public OrderConfirmation order(Order order) { + RestTemplate restTemplate = new RestTemplate(); + + return restTemplate.postForObject(url + "/order", order, OrderConfirmation.class); + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/api/data/Article.java b/prototype/src/main/java/org/hso/ecommerce/api/data/Article.java new file mode 100644 index 0000000..ad2da1c --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/api/data/Article.java @@ -0,0 +1,11 @@ +package org.hso.ecommerce.api.data; + +public class Article { + public String title; + public String manufacturer; + public String articleNumber; + + public int vatPercent; + public int pricePerUnitNet; + public boolean shouldBeAdvertised; +} diff --git a/prototype/src/main/java/org/hso/ecommerce/api/data/Order.java b/prototype/src/main/java/org/hso/ecommerce/api/data/Order.java new file mode 100644 index 0000000..679d7a7 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/api/data/Order.java @@ -0,0 +1,9 @@ +package org.hso.ecommerce.api.data; + +public class Order { + public String manufacturer; + public String articleNumber; + + public int quantity; + public int maxTotalPriceCentNet; +} diff --git a/prototype/src/main/java/org/hso/ecommerce/api/data/OrderConfirmation.java b/prototype/src/main/java/org/hso/ecommerce/api/data/OrderConfirmation.java new file mode 100644 index 0000000..78c8285 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/api/data/OrderConfirmation.java @@ -0,0 +1,12 @@ +package org.hso.ecommerce.api.data; + +public class OrderConfirmation { + public String manufacturer; + public String articleNumber; + + public int quantity; + + public int pricePerUnitNetCent; + public int discountNetCent; + public int totalPriceNetCharged; +} diff --git a/prototype/src/main/java/org/hso/ecommerce/api/data/Supplier.java b/prototype/src/main/java/org/hso/ecommerce/api/data/Supplier.java new file mode 100644 index 0000000..211f353 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/api/data/Supplier.java @@ -0,0 +1,21 @@ +package org.hso.ecommerce.api.data; + +import java.util.List; + +public class Supplier { + + public String id; + public String name; + public SupplierDiscount discount; + public List
articles; + + public Article findArticle(String manufacturer, String articleNumber) { + for (Article a : articles) { + if (a.manufacturer.equals(manufacturer) && a.articleNumber.equals(articleNumber)) { + return a; + } + } + + return null; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/api/data/SupplierDiscount.java b/prototype/src/main/java/org/hso/ecommerce/api/data/SupplierDiscount.java new file mode 100644 index 0000000..9ccf250 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/api/data/SupplierDiscount.java @@ -0,0 +1,6 @@ +package org.hso.ecommerce.api.data; + +public class SupplierDiscount { + public int minimumDailySalesVolumeNetCent; + public int percentDiscount; +} 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/app/RequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java index 3602236..406e283 100644 --- a/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java +++ b/prototype/src/main/java/org/hso/ecommerce/app/RequestController.java @@ -1,15 +1,7 @@ package org.hso.ecommerce.app; -import org.hso.ecommerce.repos.user.UserRepository; -import org.hso.ecommerce.entities.user.User; -import org.springframework.beans.factory.annotation.Autowired; 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.Optional; +import org.springframework.web.bind.annotation.GetMapping; /** * TODO clean up this class @@ -17,260 +9,13 @@ import java.util.Optional; @Controller public class RequestController { - @Autowired - private final UserRepository userRepository = null; + @GetMapping("/intern/customerOrders/") + public String internCustomerOrder() { + return "intern/customerOrders/index"; + } - static int notSoRandom = 0; - - @GetMapping("/") - public String home() { - return "redirect:/shop/"; - } - - @GetMapping("/login") - public String login() { - return "login"; - } - - @PostMapping("/login") - public String loginPost( - HttpServletRequest request, - HttpServletResponse response, - @RequestParam("username") String username, - @RequestParam("password") String password, - HttpSession session - ) { - String gto = (String) session.getAttribute("afterLogin"); - - Optional user = userRepository.findByEmail(username); - if (user.isEmpty()) { - request.setAttribute("error", "Email Adresse falsch."); - response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); - return "login"; - } - - if (!user.get().validatePassword(password)) { - request.setAttribute("error", "Passwort falsch."); - response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); - return "login"; - } - - session.setAttribute("userId", user.get().getId()); - - if (gto != null && gto.startsWith("/")) { - return "redirect:" + gto; - } else { - return "redirect:/"; - } - } - - @PostMapping("/logout") - public String logoutPost(HttpServletResponse response, - HttpSession session - ) { - session.removeAttribute("userId"); - return "redirect:/"; - } - - @GetMapping("/register") - public String register() { - return "register"; - } - - @PostMapping("/register") - public String registerPost( - @RequestParam("username") String username, - @RequestParam("password") String password, - @RequestParam("password2") String password2, - @RequestParam("type") String type - ) { - - return "redirect:/"; - } - - @GetMapping("/shop/") - public String shop() { - return "shop/index"; - } - - @GetMapping("/shop/search") - public String shopSearch() { - return "shop/search"; - } - - @GetMapping("/shop/checkout") - public String shopCheckout(HttpSession session, HttpServletRequest request) { - session.setAttribute("afterLogin", request.getRequestURI()); - return "shop/checkout"; - } - - @PostMapping("/shop/checkoutFinish") - public String shopCheckoutFinish() { - return "shop/checkoutFinish"; - } - - @GetMapping("/shop/checkoutFinish") - public String shopCheckoutFinishGET() { - return "shop/checkoutFinish"; - } - - @GetMapping("/shop/articles/{id}") - public String shopArticlesById() { - return "shop/articles/id"; - } - - @PostMapping("/shop/articles/{id}") - public String shopArticlesByIdBuy(HttpSession session, - @RequestAttribute(value = "user", required = false) User customer, - @PathVariable("id") Integer id, - @RequestParam("fastcheckout") Boolean fastcheckout - ) { - if (customer != null) { - if (!fastcheckout) { - return "shop/articles/post_add"; - } else { - return "shop/checkout"; - } - } else { - session.setAttribute("afterLogin", "/shop/articles/" + id); - return "redirect:/login"; - } - } - - @GetMapping("/about") - public String about() { - return "about"; - } - - @GetMapping("/terms") - public String terms() { - return "terms"; - } - - @GetMapping("/privacy") - public String privacy() { - return "privacy"; - } - - - @GetMapping("/intern/") - public String intern() { - return "intern/index"; - } - - @GetMapping("/intern/listedArticles/") - public String internListedArticles() { - return "intern/listedArticles/index"; - } - - @GetMapping("/intern/listedArticles/{id}") - public String internListedArticlesId() { - return "intern/listedArticles/id"; - } - - - @GetMapping("/intern/articles/") - public String internArticles() { - return "intern/articles/index"; - } - - @GetMapping("/intern/articles/{id}") - public String internArticlesId() { - return "intern/articles/id"; - } - - @GetMapping("/intern/customers/") - public String internCustomers() { - return "intern/customers/index"; - } - - @GetMapping("/intern/customers/{id}") - public String internCustomersId() { - return "intern/customers/id"; - } - - @GetMapping("/intern/customerOrders/") - public String internCustomerOrder() { - return "intern/customerOrders/index"; - } - - @GetMapping("/intern/customerOrders/{id}") - public String internCustomerOrdersId() { - return "intern/customerOrders/id"; - } - - @GetMapping("/intern/suppliers/") - public String internSuppliers() { - return "intern/suppliers/index"; - } - - @GetMapping("/intern/suppliers/{id}") - public String internSuppliersId() { - return "intern/suppliers/id"; - } - - @GetMapping("/intern/supplierOrders/") - public String internSupplierOrders() { - return "intern/supplierOrders/index"; - } - - @GetMapping("/intern/supplierOrders/{id}") - public String internSupplierOrdersId() { - return "intern/supplierOrders/id"; - } - - @GetMapping("/intern/accounting/") - public String accounting() { - return "intern/accounting/index"; - } - - @GetMapping("/intern/accounting/vat") - public String accountingVat() { - return "intern/accounting/vat"; - } - - @GetMapping("/intern/accounting/main") - public String accountingIntern() { - return "intern/accounting/main"; - } - - @GetMapping("/intern/accounting/addManual") - public String accountingAddManual() { - return "intern/accounting/addManual"; - } - - @GetMapping("/intern/warehouse/") - public String accountingWarehouse() { - return "intern/warehouse/index"; - } - - @GetMapping("/intern/warehouse/todo") - public String accountingWarehouseTodo() { - return "intern/warehouse/todo"; - } - - @GetMapping("/intern/warehouse/addManual") - public String accountingWarehouseAddManual() { - return "intern/warehouse/addManual"; - } - - @PostMapping("/intern/warehouse/progress/{id}") - public String accountingWarehouseProgressIdPost(HttpServletResponse response) { - if ((notSoRandom++) % 2 == 1) { - return "redirect:/intern/warehouse/progress/450"; - } else { - response.setStatus(409); - return "intern/warehouse/error_progress_failed"; - } - } - - @GetMapping("/intern/warehouse/progress/{id}") - public String accountingWarehouseProgressId() { - return "intern/warehouse/id_progress"; - } - - @GetMapping("/intern/warehouse/slots/") - public String accountingWarehouseSlots() { - return "intern/warehouse/slots/index"; - } + @GetMapping("/intern/customerOrders/{id}") + public String internCustomerOrdersId() { + return "intern/customerOrders/id"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/app/UserRequestController.java b/prototype/src/main/java/org/hso/ecommerce/app/UserRequestController.java deleted file mode 100644 index c4f6301..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/app/UserRequestController.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.hso.ecommerce.app; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; - -@Controller -@RequestMapping("user") -public class UserRequestController { - - @GetMapping("/") - public String user() { - return "redirect:/user/settings"; - } - - @GetMapping("/settings") - public String userSettings() { - return "user/settings"; - } - - @GetMapping("/orders/") - public String userOrdeers() { - return "user/orders/index"; - } - - @GetMapping("/bonuspoints") - public String userBonuspoints() { - return "user/bonuspoints"; - } - - @GetMapping("/notifications/") - public String userNotifications() { - return "user/notifications/index"; - } -} - diff --git a/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java b/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java index 8e1fc1d..e537867 100644 --- a/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java +++ b/prototype/src/main/java/org/hso/ecommerce/components/LoginIntercepter.java @@ -24,6 +24,7 @@ public class LoginIntercepter implements HandlerInterceptor { HttpSession session = request.getSession(); Object userId = session.getAttribute("userId"); + Optional user = null; if (request.getRequestURI().startsWith("/user/")) { System.out.println("USER"); @@ -43,10 +44,24 @@ public class LoginIntercepter implements HandlerInterceptor { response.sendRedirect("/login"); return false; } + + user = userRepository.findById((Long) userId); + + if(user.isPresent() && !user.get().isEmployee) + { + session.setAttribute("afterLogin", request.getRequestURI()); + response.sendRedirect("/"); + return false; + } + } + + if (!request.getRequestURI().startsWith("/login")) { + session.removeAttribute("afterLogin"); } if (userId != null) { - Optional user = userRepository.findById((Long) userId); + if (user == null) + user = userRepository.findById((Long) userId); user.ifPresent(value -> request.setAttribute("user", value)); } diff --git a/prototype/src/main/java/org/hso/ecommerce/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/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/BookingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/BookingController.java deleted file mode 100644 index d35093e..0000000 --- a/prototype/src/main/java/org/hso/ecommerce/controller/BookingController.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.hso.ecommerce.controller; - -import org.springframework.stereotype.Controller; - -@Controller -//@RequestMapping("...") -public class BookingController { -} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java b/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java index 5f4ebad..94182ce 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/LoginController.java @@ -1,8 +1,69 @@ package org.hso.ecommerce.controller; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; @Controller -//@RequestMapping("...") +@RequestMapping("/") public class LoginController { + + @Autowired + private final UserRepository userRepository = null; + + @GetMapping("login") + public String login() { + return "login"; + } + + @PostMapping("login") + public String loginPost(HttpServletRequest request, HttpServletResponse response, + @RequestParam("username") String username, @RequestParam("password") String password, HttpSession session) { + + String gto = (String) session.getAttribute("afterLogin"); + + Optional user = userRepository.findByEmail(username); + if (!user.isPresent()) { + request.setAttribute("error", "Die Email Adresse falsch."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "login"; + } + + if (!user.get().validatePassword(password)) { + request.setAttribute("error", "Das Passwort ist falsch."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "login"; + } + + if (!user.get().isActive) { + request.setAttribute("error", "Dieses Konto ist deaktiviert.."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "login"; + } + + session.setAttribute("userId", user.get().getId()); + + if (gto != null && gto.startsWith("/")) { + return "redirect:" + gto; + } else { + return "redirect:/"; + } + } + + @PostMapping("logout") + public String logoutPost(HttpServletResponse response, HttpSession session) { + session.removeAttribute("userId"); + return "redirect:/"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java new file mode 100644 index 0000000..36fcdef --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/RegisterController.java @@ -0,0 +1,82 @@ +package org.hso.ecommerce.controller; + +import org.hso.ecommerce.entities.booking.PaymentMethod; +import org.hso.ecommerce.entities.shop.Address; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.util.Optional; + +@Controller +public class RegisterController { + + @Autowired + private final UserRepository userRepository = null; + + @PostMapping("/register") + public String registerPost(HttpServletRequest request, HttpServletResponse response, + @RequestParam("username") String username, @RequestParam("password") String password, + @RequestParam("password2") String password2, @RequestParam("salutation") String salutation, + @RequestParam("name") String name, @RequestParam("address") String address, + @RequestParam("type") String type, // TODO store + @RequestParam("ad") String ad, // TODO store + HttpSession session) { + Optional user = userRepository.findByEmail(username); + if (user.isPresent()) { + request.setAttribute("error", "Die Email Adresse existiert bereits."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "register"; + } + + if (!password.equals(password2)) { + request.setAttribute("error", "Die Passwörter stimmen nicht überein."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "register"; + } + + // set values for new user + User newUser = new User(); + newUser.email = username; + newUser.setPassword(password); + newUser.email = username; + newUser.isEmployee = false; + newUser.salutation = salutation; + newUser.defaultPayment = PaymentMethod.fromCreditCardNumber(""); + + newUser.isActive = true; + newUser.created = new java.sql.Timestamp(System.currentTimeMillis()); + + Address newAddress = new Address(); + newAddress.name = name; + newAddress.addressString = address; + newUser.defaultDeliveryAddress = newAddress; + + userRepository.save(newUser); // save newUser + + user = userRepository.findByEmail(username); + session.setAttribute("userId", user.get().getId()); + + String gto = (String) session.getAttribute("afterLogin"); + + //login after register + if (gto != null && gto.startsWith("/")) { + return "redirect:" + gto; + } else { + return "redirect:/"; + } + + } + + @GetMapping("/register") + public String register() { + return "register"; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java index 523bf2e..97c5bf6 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/UserController.java @@ -1,8 +1,131 @@ package org.hso.ecommerce.controller; +import org.hso.ecommerce.action.user.UpdateUserSettingsAction; +import org.hso.ecommerce.entities.shop.Address; +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.hso.ecommerce.entities.user.User; +import org.hso.ecommerce.repos.shop.CustomerOrderRepository; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.util.List; @Controller -//@RequestMapping("...") +@RequestMapping("/user") public class UserController { + + @Autowired + private final UserRepository userRepository = null; + + @Autowired + private final CustomerOrderRepository customerOrderRepository = null; + + @GetMapping("/") + public String user() { + return "redirect:/user/settings"; + } + + @GetMapping("/settings") + public String userSettings(Model model, + HttpSession session + ) { + long userId = (long) session.getAttribute("userId"); + User user = userRepository.findById(userId).get(); + if(user.defaultDeliveryAddress == null){ + user.defaultDeliveryAddress = new Address(); + } + model.addAttribute("user", user); + + return "user/settings"; + } + + @GetMapping("/orders/") + public String userOrdeers(HttpSession session, + Model model + ) { + List orders = customerOrderRepository.getOrdersByUserId((long) session.getAttribute("userId")); + model.addAttribute("orders", orders); + + return "user/orders/index"; + } + + @PostMapping("/settings/changeMail") + public String changeMail(HttpSession session, + @RequestParam("email") String email, + HttpServletRequest request + ) { + User user = userRepository.findById((long) session.getAttribute("userId")).get(); + + UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); + UpdateUserSettingsAction.UpdateResult result = cusa.updateEmail(email); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + return "user/settings"; + } + + return "redirect:/user/settings"; + } + + @PostMapping("/settings/changePwd") + public String changePwd(HttpSession session, + @RequestParam("old-password") String oldPassword, + @RequestParam("password1") String password1, + @RequestParam("password2") String password2, + HttpServletRequest request + ) { + User user = userRepository.findById((long) session.getAttribute("userId")).get(); + + UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); + UpdateUserSettingsAction.UpdateResult result = cusa.updatePassword(oldPassword, password1, password2); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + return "user/settings"; + } + + return "redirect:/user/settings"; + } + + @PostMapping("/settings/changeAddress") + public String changeAddress(HttpSession session, + @RequestParam("salutation") String salutation, + @RequestParam("name") String name, + @RequestParam("address") String address, + HttpServletRequest request + ) { + User user = userRepository.findById((long) session.getAttribute("userId")).get(); + + UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); + UpdateUserSettingsAction.UpdateResult result = cusa.updateShippingInfo(salutation, name, address); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + return "user/settings"; + } + + return "redirect:/user/settings"; + } + + @PostMapping("/settings/changePaymentInfo") + public String changePaymentInfo(HttpSession session, + @RequestParam("creditCardNumber") String creditCardNumber, + HttpServletRequest request + ) { + User user = userRepository.findById((long) session.getAttribute("userId")).get(); + + UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository); + UpdateUserSettingsAction.UpdateResult result = cusa.updatePaymentInfo(creditCardNumber); + if (result.updated == false) { + request.setAttribute("error", result.errorString); + return "user/settings"; + } + + return "redirect:/user/settings"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java new file mode 100644 index 0000000..0f54221 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/cronjob/CronjobController.java @@ -0,0 +1,331 @@ +package org.hso.ecommerce.controller.cronjob; + +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.PostConstruct; + +import org.hso.ecommerce.action.booking.CreateBookingAction; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction; +import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; +import org.hso.ecommerce.action.cronjob.ReorderAction; +import org.hso.ecommerce.action.cronjob.UpdateOffersAction; +import org.hso.ecommerce.entities.booking.Booking; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.BookingReason; +import org.hso.ecommerce.entities.cron.BackgroundJob; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.hso.ecommerce.entities.supplier.Supplier; +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; +import org.hso.ecommerce.repos.booking.BookingRepository; +import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.shop.CustomerOrderRepository; +import org.hso.ecommerce.repos.supplier.ArticleOfferRepository; +import org.hso.ecommerce.repos.supplier.SupplierOrderRepository; +import org.hso.ecommerce.repos.supplier.SupplierRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +interface ICronjob { + /** + * Calculate the earliest cronjob execution time that happens after the given reference time. + * + * @param reference Position in time to start searching. The implementor is allowed to modify the reference time. + * @return A new Calendar instance (or the same) containing the time for next execution. + */ + Calendar nextExecution(Calendar reference); + + /** + * Calculate the latest cronjob execution time that happens before or exactly at the given refernce time. + * + * @param reference Position in time to start searching. The implementor is allowed to modify the reference time. + * @return A new Calendar instance (or the same) containing the time of the last execution. + */ + Calendar previousExecution(Calendar reference); + + /** + * Execute this cronjob. + * + * @param time The point in time this execution was scheduled. In case of a missed cronjob, the actual time of + * this call might be much later. + * @param controller Back-reference that allows to use repositories. + */ + void executeAt(Calendar time, CronjobController controller); +} + +@Component +class Reorder implements ICronjob { + @Override + public Calendar nextExecution(Calendar reference) { + if (reference.get(Calendar.HOUR_OF_DAY) >= 8) { + reference.add(Calendar.DAY_OF_MONTH, 1); + } + reference.set(Calendar.HOUR_OF_DAY, 8); + reference.set(Calendar.MINUTE, 0); + reference.set(Calendar.SECOND, 0); + reference.set(Calendar.MILLISECOND, 0); + return reference; + } + + @Override + public Calendar previousExecution(Calendar reference) { + if (reference.get(Calendar.HOUR_OF_DAY) < 8) { + reference.add(Calendar.DAY_OF_MONTH, -1); + } + reference.set(Calendar.HOUR_OF_DAY, 8); + reference.set(Calendar.MINUTE, 0); + reference.set(Calendar.SECOND, 0); + reference.set(Calendar.MILLISECOND, 0); + return reference; + } + + /** + * Calculates the amount of ordered articles by customers for the given article type in the time between begin and + * end. + * + * @param article The article to search orders for. + * @param begin The start time for the search (included) + * @param end The end time for the search (excluded) + * @return The number of articles that were ordered by customers in the given range. + */ + private Integer getOrderedAmounts(Article article, Calendar begin, Calendar end, CronjobController controller) { + return controller.customerOrderRepository.countOrdersOfArticleInTimespan( + article.id, + new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis())); + } + + /** + * Calculates the amount of ordered articles by customers for the given article type in the three days before the + * given reference time. The return-array contains 3 fields: Index 0: Orders 72 to 48 hours ago; Index 1: Orders 48 + * to 24 hours ago; Index 2: Orders 24 to 0 hours ago. + * + * @param article The article for which the customer orders are checked. + * @param time The reference time to use for calculation of the last orders. + * @return A 3-element array containing the orders of the last three days. + */ + private Integer[] getOrderedAmounts(Article article, Calendar time, CronjobController controller) { + Calendar oneDayBefore = (Calendar) time.clone(); + oneDayBefore.add(Calendar.DAY_OF_MONTH, -1); + Calendar twoDaysBefore = (Calendar) time.clone(); + twoDaysBefore.add(Calendar.DAY_OF_MONTH, -2); + Calendar threeDaysBefore = (Calendar) time.clone(); + threeDaysBefore.add(Calendar.DAY_OF_MONTH, -3); + + return new Integer[] { // + getOrderedAmounts(article, threeDaysBefore, twoDaysBefore, controller), // + getOrderedAmounts(article, twoDaysBefore, oneDayBefore, controller), // + getOrderedAmounts(article, oneDayBefore, time, controller), // + }; + } + + private HashMap mapArticleOffers(List articleOffers) { + HashMap map = new HashMap<>(); + for (ArticleOffer articleOffer : articleOffers) { + ArticleIdentifier identifier = new ArticleIdentifier(articleOffer.manufacturer, articleOffer.articleNumber); + map.put(identifier, articleOffer); + } + return map; + } + + @Override + public void executeAt(Calendar time, CronjobController controller) { + List suppliers = controller.supplierRepository.findAll(); + ReadSupplierDataAction.Result supplierData = new ReadSupplierDataAction(suppliers).finish(); + + // Save the new offers in the database + List allOffers = controller.articleOfferRepository.findAll(); + allOffers = new UpdateOffersAction(allOffers, supplierData.cheapestOffer).finish(); + controller.articleOfferRepository.saveAll(allOffers); + + HashMap mappedOffers = mapArticleOffers(allOffers); + + // Reorder + List
allArticles = controller.articleRepository.findAll(); + for (Article article : allArticles) { + Integer[] orderedAmounts = getOrderedAmounts(article, time, controller); + + Integer undeliveredReorders = controller.supplierOrderRepository + .countUndeliveredReorders(article.related.articleNumber); + + int amountInStock = controller.warehouseBookingPositionSlotEntryRepository.getArticleStock(article.id) + .orElse(0); + + ReorderAction action = new ReorderAction(article, orderedAmounts, + undeliveredReorders, + amountInStock, + supplierData.cheapestOffer, mappedOffers); + SupplierOrder order = action.finish(); + if (order != null) { + controller.supplierOrderRepository.save(order); + + // Create bookings for this order + int netPrice = order.totalPriceNet; + int vatPercent = order.ordered.vatPercent; + int vatAmount = netPrice * vatPercent / 100; + int grossPrice = netPrice + vatAmount; + + // Obligation towards the supplier + BookingAccountEntry mainAccount = controller.bookingAccountEntryRepository.getByMain() + .orElseGet(BookingAccountEntry::newMain); + BookingAccountEntry supplierAccount = controller.bookingAccountEntryRepository + .getBySupplier(order.supplier.id) + .orElseGet(() -> BookingAccountEntry.newSupplier(order.supplier)); + BookingReason obligationReason = new BookingReason(order); + Booking obligationBooking = new CreateBookingAction(mainAccount, + supplierAccount, + obligationReason, + grossPrice).finish(); + controller.bookingRepository.save(obligationBooking); + + // Input Tax + BookingAccountEntry vatAccount = controller.bookingAccountEntryRepository.getByVat() + .orElseGet(BookingAccountEntry::newVat); + mainAccount = controller.bookingAccountEntryRepository.getByMain().get(); + BookingReason inputTaxReason = new BookingReason(order); + Booking inputTaxBooking = new CreateBookingAction(vatAccount, mainAccount, inputTaxReason, vatAmount) + .finish(); + controller.bookingRepository.save(inputTaxBooking); + } + } + } +} + +class ScheduledCronjob { + public final Calendar executionTime; + public final ICronjob cronjob; + public final BackgroundJob model; + + public ScheduledCronjob(Calendar executionTime, ICronjob cronjob, BackgroundJob model) { + this.executionTime = executionTime; + this.cronjob = cronjob; + this.model = model; + } +} + +@Component +class CronjobController { + private static final Logger log = LoggerFactory.getLogger(CronjobController.class); + + private static final Map cronjobs = getCronjobs(); + + @Autowired + private final BackgroundJobRepository cronjobRepository = null; + + @Autowired + final ArticleRepository articleRepository = null; + + @Autowired + final ArticleOfferRepository articleOfferRepository = null; + + @Autowired + final CustomerOrderRepository customerOrderRepository = null; + + @Autowired + final BookingRepository bookingRepository = null; + + @Autowired + final BookingAccountEntryRepository bookingAccountEntryRepository = null; + + @Autowired + final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + final SupplierRepository supplierRepository = null; + + @Autowired + final SupplierOrderRepository supplierOrderRepository = null; + + private static Map getCronjobs() { + HashMap map = new HashMap<>(); + + // Register all existing cronjobs + map.put(BackgroundJob.JOB_REORDER, new Reorder()); + + return Collections.unmodifiableMap(map); + } + + private ScheduledCronjob getNextCronjob() { + Calendar currentTime = new GregorianCalendar(); + Iterable jobs = cronjobRepository.findAll(); + HashMap alreadyExecuted = new HashMap<>(); + for (BackgroundJob job : jobs) { + alreadyExecuted.put(job.jobName, job); + } + ScheduledCronjob earliestJob = null; + for (Entry entry : cronjobs.entrySet()) { + ScheduledCronjob resultingJob; + BackgroundJob dbEntry = alreadyExecuted.get(entry.getKey()); + if (dbEntry != null) { + Calendar previousExecution = new GregorianCalendar(); + previousExecution.setTimeInMillis(dbEntry.lastExecution.getTime()); + Calendar followingSchedule = entry.getValue().nextExecution((Calendar) previousExecution.clone()); + Calendar lastSchedule = entry.getValue().previousExecution((Calendar) currentTime.clone()); + if (lastSchedule.getTimeInMillis() > followingSchedule.getTimeInMillis()) { + // This happens, if more than one execution was missed. + // In this case, run the job only once. + followingSchedule = lastSchedule; + } + resultingJob = new ScheduledCronjob(followingSchedule, entry.getValue(), dbEntry); + } else { + // This cronjob has never been executed before. + Calendar lastScheduleTime = entry.getValue().previousExecution((Calendar) currentTime.clone()); + BackgroundJob model = new BackgroundJob(); + model.jobName = entry.getKey(); + resultingJob = new ScheduledCronjob(lastScheduleTime, entry.getValue(), model); + } + + // Look for the job with earliest executionTime - it will run next + if (earliestJob == null + || resultingJob.executionTime.getTimeInMillis() < earliestJob.executionTime.getTimeInMillis()) { + earliestJob = resultingJob; + } + } + return earliestJob; + } + + private void runCronjobExecutionLoop() { + Thread.currentThread().setName("Cronjob"); + try { + while (true) { + ScheduledCronjob nextJob = getNextCronjob(); + if (nextJob == null) { + // In case there are no cronjobs + return; + } + long waitingTime = nextJob.executionTime.getTimeInMillis() - System.currentTimeMillis(); + if (waitingTime > 0) { + Thread.sleep(waitingTime); + } + + try { + nextJob.cronjob.executeAt(nextJob.executionTime, this); + } catch (Throwable t) { + log.error("Failed to execute cronjob " + nextJob.cronjob.getClass() + ":"); + t.printStackTrace(); + } + + nextJob.model.lastExecution = new Timestamp(nextJob.executionTime.getTimeInMillis()); + cronjobRepository.save(nextJob.model); + } + } catch (InterruptedException e) { + log.error("The cronjob execution thread has been interrupted"); + } + } + + @PostConstruct + public void onPostConstruct() { + new Thread(this::runCronjobExecutionLoop).start(); + } +} 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..e799210 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,272 @@ package org.hso.ecommerce.controller.intern; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.shop.Category; +import org.hso.ecommerce.entities.shop.Image; +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.shop.CategoryRepository; +import org.hso.ecommerce.repos.shop.ImageRepository; +import org.hso.ecommerce.repos.shop.OffersRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.DigestUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.view.RedirectView; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; @Controller -//@RequestMapping("...") +@RequestMapping("intern/articles") public class InternArticleController { + @Autowired + private final ArticleRepository articleRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseEntryRepository = null; + + @Autowired + private final CategoryRepository categoryRepository = null; + + @Autowired + private final OffersRepository offersRepository = null; + + @Autowired + private final ImageRepository imageRepository = null; + + @GetMapping("/") + public String internListedArticles(Model model) { + + List totals = new ArrayList(); + + for (Article article : articleRepository.findAll()) { + UImodelArticles tmp = new UImodelArticles(); + tmp.addListedArticle(article, warehouseEntryRepository.getArticleStock(article.id).orElse(0)); + totals.add(tmp); + } + + model.addAttribute("ListedArticles", totals); + return "intern/listedArticles/index"; + } + + @GetMapping("/{id}") + public String internListedArticlesId(Model model, @PathVariable String id) { + int articleid = Integer.parseInt(id); + UImodelArticle total = new UImodelArticle(); + total.addArticle(articleRepository.findArticleById(articleid), + warehouseEntryRepository.getArticleStock(articleid).orElse(0)); + + model.addAttribute("ArticleID", total); + return "intern/listedArticles/id"; + } + + @PostMapping("/{id}/saveChanges") + public RedirectView saveChanges(@PathVariable(required = true) int id, + @RequestParam(value = "title", required = true) String title, + @RequestParam(value = "description", required = true) String description, + @RequestParam(value = "units-per-slot", required = true) String warehouseUnitsPerSlot, + @RequestParam(value = "priceNet", required = true) String pricenetto, + @RequestParam(value = "reorderMaxPrice", required = true) String reorderMaxPrice, + @RequestParam(value = "autobuy", required = true) Boolean shouldReorder, + @RequestParam(value = "categorie", required = true) String categories, + @RequestParam(value = "img", required = true) MultipartFile imgFile) { + + Article tmpArticle = articleRepository.findArticleById(id); // get the old article + + String[] separatedCategories = categories.split("\n"); + + tmpArticle.categories.clear(); + + // loop through all categories strings and create a new category if a new one; + // also adds the categories to the article + for (String category : separatedCategories) { + tmpArticle.categories.add(categoryRepository.findCategoryByName(category.trim()) + .orElseGet(() -> new Category(category.trim()))); + } + + tmpArticle.shouldReorder = shouldReorder; + tmpArticle.reorderMaxPrice = (int) (Float.parseFloat(reorderMaxPrice) * 100); + tmpArticle.shopPricePerUnitNetCent = (int) (Float.parseFloat(pricenetto) * 100); + tmpArticle.warehouseUnitsPerSlot = Integer.parseInt(warehouseUnitsPerSlot); + tmpArticle.title = title; + updateImage(tmpArticle, imgFile); + tmpArticle.description = description; + + articleRepository.save(tmpArticle); // save updated article + return new RedirectView("../"); // return to overview page + + } + + @PostMapping("/addArticle/{id}") + public RedirectView addArticle(@PathVariable(required = true) String id) { + // article is not already listed, create new one + int offeredArticleID = Integer.parseInt(id); + + Article tmpArticle = new Article(); + + ArticleOffer offeredArticle = offersRepository.findOfferedArticleById(offeredArticleID); + + // set default values + tmpArticle.description = ""; + tmpArticle.reorderMaxPrice = 0; + tmpArticle.shopPricePerUnitNetCent = offeredArticle.pricePerUnitNet; + tmpArticle.shouldReorder = false; + tmpArticle.title = offeredArticle.title; + tmpArticle.warehouseUnitsPerSlot = 1; + setDefaultImage(tmpArticle); + tmpArticle.related = offeredArticle; + articleRepository.save(tmpArticle); // save new article + + // return to edit article page + return new RedirectView("../" + articleRepository.findArticleIDByRelatedID(offeredArticleID).get()); + } + + private void setDefaultImage(Article article) { + String defaultImagePath = "./data/img/no_product_img.jpg"; // path + name of default img + Optional imageID = imageRepository.findImageIDByPath(defaultImagePath); // get default img + + if (imageID.isPresent()) { + // default img is in DB + article.image = imageRepository.findImageById(imageID.get()); // set default img to new article + } else { + // default img is not in DB + File tmpFile = new File(defaultImagePath); + // test if default img file exits + if (!tmpFile.exists()) { + // fallback if the file not exists + // create new file + BufferedImage bufferedImage = new BufferedImage(422, 428, BufferedImage.TYPE_INT_RGB); + try { + ImageIO.write(bufferedImage, "jpg", new File(defaultImagePath)); // save new file on disk + } catch (IOException e) { + e.printStackTrace(); + } + } + Image defaultImage = new Image(); + defaultImage.path = defaultImagePath; // set new file to default img + imageRepository.save(defaultImage); // save default img + article.image = defaultImage; // set default img to new article + } + } + + private void updateImage(Article article, MultipartFile imgFile) { + // check if a file is present + if (imgFile.getSize() > 0) { + try { + // get file name based on MD5 hash + String fileName = DigestUtils.md5DigestAsHex(imgFile.getBytes()) + ".jpg"; + // check if img is already in system + Optional imageID = imageRepository.findImageIDByPath("./data/img/" + fileName); + if (imageID.isPresent()) { + article.image = imageRepository.findImageById(imageID.get()); // add existing img to article + } else { + // write new img file to disk + Files.newOutputStream(Paths.get("./data/img/", fileName)).write(imgFile.getBytes()); + // create new img + Image newImage = new Image(); + newImage.path = "./data/img/" + fileName; // set new file to new img + imageRepository.save(newImage); // save new img + article.image = newImage; // set new img to article + } + } catch (Exception e) { + setDefaultImage(article); // if upload failed, reset to default img + } + } + } + + public static class UImodelArticles { + + public String imgPath; + public String title; + public String price; + public String priceNet; + public String categorie; + public int stock; + public long offerID; + public long id; + + void addListedArticle(Article article, int stock) { + + if (article.image != null) { + this.imgPath = article.image.path; + } + this.title = article.title; + this.priceNet = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); + this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); + + StringBuilder result = new StringBuilder(); + + for (Category temp : article.categories) { + result.append(temp.name + " "); + } + this.categorie = result.toString(); + + this.stock = stock; + this.offerID = article.related.id; + this.id = article.id; + } + } + + public static class UImodelArticle { + + public String imgPath; + public String title; + public String price; + public String priceNet; + public String reorderMaxPrice; + public String categorie; + public int stock; + public long offerID; + public long id; + public boolean shouldReorder; + public String warehouseUnitsPerSlot; + public String description; + public int vatPercent; + + public String getCategorie() { + return categorie; + } + + public String getDescription() { + return description; + } + + void addArticle(Article article, int stock) { + if (article.image != null) { + this.imgPath = article.image.path; + } + this.title = article.title; + this.priceNet = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100)); + this.price = String.format("%.2f", ((float) article.getPriceGross() / 100)); + + StringBuilder result = new StringBuilder(); + + for (Category temp : article.categories) { + result.append(temp.name); + result.append("\n"); + } + this.categorie = result.toString(); + + this.stock = stock; + this.offerID = article.related.id; + this.id = article.id; + this.reorderMaxPrice = String.format("%.2f", ((float) article.reorderMaxPrice / 100)); + this.shouldReorder = article.shouldReorder; + this.warehouseUnitsPerSlot = String.valueOf(article.warehouseUnitsPerSlot); + this.description = article.description; + this.vatPercent = article.related.vatPercent; + } + } + } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java index 65b3dba..43fe2d9 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/InternIndexController.java @@ -1,8 +1,34 @@ package org.hso.ecommerce.controller.intern; +import java.util.Optional; + +import org.hso.ecommerce.controller.intern.accounting.AccountingController; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; @Controller -//@RequestMapping("...") +@RequestMapping("/intern") public class InternIndexController { + + @Autowired + private final BookingAccountEntryRepository bookingAccountEntryRepository = null; + + @GetMapping("/") + public String intern(Model model) { + Optional mainAccount = bookingAccountEntryRepository.getByMain(); + int mainAccountBalance = mainAccount.map(entry -> entry.newSumCent).orElse(0); + Optional vatAccount = bookingAccountEntryRepository.getByVat(); + int vatAccountBalance = vatAccount.map(entry -> entry.newSumCent).orElse(0); + + model.addAttribute("mainAccountBalance", AccountingController.fmtEuro(mainAccountBalance)); + model.addAttribute("vatAccountBalance", AccountingController.fmtEuro(vatAccountBalance)); + + return "intern/index"; + } + } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java index 9b618f7..4a0258e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/WarehouseController.java @@ -1,8 +1,27 @@ package org.hso.ecommerce.controller.intern; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/warehouse/") public class WarehouseController { + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @GetMapping("/") + public String accountingWarehouse( + Model model, + HttpServletRequest request + ) { + model.addAttribute("bookings", warehouseBookingRepository.findAll()); + return "intern/warehouse/index"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java new file mode 100644 index 0000000..c7f9b75 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/AccountingController.java @@ -0,0 +1,223 @@ +package org.hso.ecommerce.controller.intern.accounting; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +import org.hso.ecommerce.entities.booking.Booking; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.BookingReason; +import org.hso.ecommerce.repos.booking.BookingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class AccountingController { + + @Autowired + private BookingRepository bookingRepository = null; + + /** + * A description used to render the html template for the overview of all bookings + */ + static class TemplateBooking { + public String datetime; + public String amount; + public String source; + public String sourceAddr; + public String sourceBalance; + public String destination; + public String destinationAddr; + public String destinationBalance; + public String reason; + public String reference; + public String referenceAddr; + } + + /** + * A description used to render the html template for bookings on a specific account + */ + public static class ShortTemplateBooking { + public String datetime; + public String amount; + public String source; + public String sourceAddr; + public String balance; + public String reason; + public String reference; + public String referenceAddr; + } + + public static class ShortTemplateBookingResult { + public final String balance; + public final List bookings; + + public ShortTemplateBookingResult(String balance, List bookings) { + this.balance = balance; + this.bookings = bookings; + } + } + + /** + * Used to check which side of the booking (source or destination) is the account we are currently looking at. + */ + public interface AccountFilter { + boolean matches(BookingAccountEntry account); + } + + /** + * Renders template information for the bookings of one specific account. + * + * @param bookings The (already fetched) booking entities to display + * @param currentAccount A filter matching the account that is to be displayed + * @return A collection result containing the final balance and all booking entries. + */ + public ShortTemplateBookingResult buildShortTemplate(List bookings, AccountFilter currentAccount) { + List templateBookings = new ArrayList<>(); + for (Booking booking : bookings) { + ShortTemplateBooking tb = new ShortTemplateBooking(); + tb.datetime = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(booking.created); + tb.reason = booking.reason.comment; + tb.reference = describeReference(booking.reason); + tb.referenceAddr = linkToReference(booking.reason); + if (booking.destination != null && currentAccount.matches(booking.destination)) { + // Money was transferred onto the account we are looking at. + tb.amount = fmtEuro(booking.amountCent); + if (booking.source != null) { + tb.source = describeAccount(booking.source); + tb.sourceAddr = linkAddrOfAccount(booking.source); + } else { + tb.source = "-"; + tb.sourceAddr = null; + } + tb.balance = fmtEuro(booking.destination.newSumCent); + } else if (booking.source != null && currentAccount.matches(booking.source)) { + // Money was transferred away from the account we are looking at. + tb.amount = fmtEuro(-booking.amountCent); // Negative because of the transfer away + if (booking.destination != null) { + tb.source = describeAccount(booking.destination); // 'source' means 'the other account' in this case + tb.sourceAddr = linkAddrOfAccount(booking.destination); + } else { + tb.source = "-"; + tb.sourceAddr = null; + } + tb.balance = fmtEuro(booking.source.newSumCent); + } else { + throw new RuntimeException( + "This booking should not appear in this list, because neither source nor destination is the account we are looking at."); + } + templateBookings.add(tb); + } + + String balance = templateBookings.isEmpty() ? fmtEuro(0) : templateBookings.get(0).balance; + return new ShortTemplateBookingResult(balance, templateBookings); + } + + public static String fmtEuro(long amountCent) { + return String.format("%.2f EUR", amountCent / 100.0); + } + + private String describeAccount(BookingAccountEntry account) { + if (account.isMainAccount) { + return "Hauptkonto"; + } else if (account.isVATAccount) { + return "Mehrwertsteuer"; + } else if (account.supplierAccount != null) { + return "Lieferant " + account.supplierAccount.id; + } else if (account.userAccount != null) { + return "Kunde " + account.userAccount.id; + } else { + return "- unbekannt -"; + } + } + + private String linkAddrOfAccount(BookingAccountEntry account) { + if (account.isMainAccount) { + return "/intern/accounting/main"; + } else if (account.isVATAccount) { + return "/intern/accounting/vat"; + } else if (account.supplierAccount != null) { + return "/intern/suppliers/" + account.supplierAccount.id; + } else if (account.userAccount != null) { + return "/intern/customers/" + account.userAccount.id; + } else { + return null; + } + } + + private String describeReference(BookingReason reason) { + if (reason.customerOrder != null) { + return reason.customerOrder.id + ""; + } else if (reason.customerPayment != null) { + return "Bezahlung mit Kreditkarte " + reason.customerPayment.payment.creditCardNumber; + } else if (reason.supplierOrder != null) { + return "Lieferanten-Bestellung " + reason.supplierOrder.id; + } else { + return "-"; + } + } + + private String linkToReference(BookingReason reason) { + if (reason.customerOrder != null) { + return "/intern/customerOrders/" + reason.customerOrder.id; + } else { + return null; + } + } + + @GetMapping("/intern/accounting/") + public String accounting(Model model) { + List bookings = bookingRepository.allBookingsReverseChronologically(); + List templateBookings = new ArrayList<>(); + for (Booking booking : bookings) { + TemplateBooking tb = new TemplateBooking(); + tb.datetime = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(booking.created); + tb.amount = fmtEuro(booking.amountCent); + if (booking.source != null) { + tb.source = describeAccount(booking.source); + tb.sourceAddr = linkAddrOfAccount(booking.source); + tb.sourceBalance = fmtEuro(booking.source.newSumCent); + } else { + tb.source = "-"; + tb.sourceAddr = null; + tb.sourceBalance = "-"; + } + if (booking.destination != null) { + tb.destination = describeAccount(booking.destination); + tb.destinationAddr = linkAddrOfAccount(booking.destination); + tb.destinationBalance = fmtEuro(booking.destination.newSumCent); + } else { + tb.destination = "-"; + tb.destinationAddr = null; + tb.destinationBalance = "-"; + } + tb.reason = booking.reason.comment; + tb.reference = describeReference(booking.reason); + tb.referenceAddr = linkToReference(booking.reason); + templateBookings.add(tb); + } + + model.addAttribute("bookings", templateBookings); + return "intern/accounting/index"; + } + + @GetMapping("/intern/accounting/vat") + public String accountingVat(Model model) { + List bookings = bookingRepository.vatBookingsReverseChronologically(); + ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isVATAccount); + model.addAttribute("balance", result.balance); + model.addAttribute("bookings", result.bookings); + return "intern/accounting/vat"; + } + + @GetMapping("/intern/accounting/main") + public String accountingIntern(Model model) { + List bookings = bookingRepository.mainBookingsReverseChronologically(); + ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isMainAccount); + model.addAttribute("balance", result.balance); + model.addAttribute("bookings", result.bookings); + return "intern/accounting/main"; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java new file mode 100644 index 0000000..fd820b1 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/accounting/ManualAccountingController.java @@ -0,0 +1,270 @@ +package org.hso.ecommerce.controller.intern.accounting; + +import java.util.Optional; + +import org.hso.ecommerce.action.booking.CreateBookingAction; +import org.hso.ecommerce.entities.booking.Booking; +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.hso.ecommerce.entities.booking.BookingReason; +import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository; +import org.hso.ecommerce.repos.booking.BookingRepository; +import org.hso.ecommerce.repos.supplier.SupplierRepository; +import org.hso.ecommerce.repos.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class ManualAccountingController { + + @Autowired + final BookingRepository bookingRepository = null; + + @Autowired + final BookingAccountEntryRepository bookingAccountEntryRepository = null; + + @Autowired + final UserRepository userRepository = null; + + @Autowired + final SupplierRepository supplierRepository = null; + + /** + * Collection for the form values used to create a manual booking + */ + public static class ManualAccounting { + String amount; + String source; + String sourceCustomer; + String sourceSupplier; + String destination; + String destinationCustomer; + String destinationSupplier; + String reason; + String reasonText; + + /** + * Default constructor with default values for the form + */ + public ManualAccounting() { + amount = "0.00"; + reason = "Manual"; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getSourceCustomer() { + return sourceCustomer; + } + + public void setSourceCustomer(String sourceCustomer) { + this.sourceCustomer = sourceCustomer; + } + + public String getSourceSupplier() { + return sourceSupplier; + } + + public void setSourceSupplier(String sourceSupplier) { + this.sourceSupplier = sourceSupplier; + } + + public String getDestination() { + return destination; + } + + public void setDestination(String destination) { + this.destination = destination; + } + + public String getDestinationCustomer() { + return destinationCustomer; + } + + public void setDestinationCustomer(String destinationCustomer) { + this.destinationCustomer = destinationCustomer; + } + + public String getDestinationSupplier() { + return destinationSupplier; + } + + public void setDestinationSupplier(String destinationSupplier) { + this.destinationSupplier = destinationSupplier; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public String getReasonText() { + return reasonText; + } + + public void setReasonText(String reasonText) { + this.reasonText = reasonText; + } + } + + /** + * An exception to represent errors that can be shown to the user + */ + public static class InvalidFormDataException extends Exception { + public InvalidFormDataException(String msg) { + super(msg); + } + + private static final long serialVersionUID = 1L; + } + + private boolean sameAccountBothSides(ManualAccounting formData) { + // No need to check for NumberFormatException because the numbers were already parsed before. + if (!formData.getSource().equals(formData.getDestination())) { + return false; + } else if (formData.getSource().equals("Cust") + && Long.parseLong(formData.getSourceCustomer()) != Long.parseLong(formData.getDestinationCustomer())) { + return false; + } else if (formData.getSource().equals("Sup") + && Long.parseLong(formData.getSourceSupplier()) != Long.parseLong(formData.getDestinationSupplier())) { + return false; + } else { + return true; + } + } + + public Booking createBooking(ManualAccounting formData) throws InvalidFormDataException { + if (formData.getSource() == null) { + throw new InvalidFormDataException("Bitte wählen Sie ein Quellkonto aus."); + } else if (formData.getDestination() == null) { + throw new InvalidFormDataException("Bitte wählen Sie ein Zielkonto aus."); + } + BookingAccountEntry source = getAccountFromFormData(formData.getSource(), formData.getSourceCustomer(), + formData.getSourceSupplier()); + BookingAccountEntry destination = getAccountFromFormData(formData.getDestination(), + formData.getDestinationCustomer(), formData.getDestinationSupplier()); + + if (sameAccountBothSides(formData)) { + throw new InvalidFormDataException("Quell- und Zielkonto dürfen nicht das selbe sein."); + } + + double doubleAmount; + try { + doubleAmount = Double.parseDouble(formData.amount); + } catch (NumberFormatException e) { + throw new InvalidFormDataException("Der angegebene Betrag ist ungültig."); + } + int amountCent = (int) Math.round(doubleAmount * 100); + + BookingReason reason = new BookingReason(); + if (formData.getReason().equals("Start")) { + reason.isStartBooking = true; + } else if (formData.getReason().equals("Manual")) { + reason.isManuel = true; + } else { + throw new RuntimeException("Invalid form value for the booking reason: " + formData.getReason()); + } + reason.comment = formData.reasonText; + + CreateBookingAction action = new CreateBookingAction(source, destination, reason, amountCent); + return action.finish(); + } + + /** + * Retrieve the corresponding account based on user-input + * + * @param account The chosen value on the radio buttons + * @param customer The given customer id from the corresponding text field + * @param supplier the given supplier id from the corresponding text field + * @return The account entry that can be used to create the new booking + * @throws InvalidFormDataException If the user provided incorrect data in any way + */ + private BookingAccountEntry getAccountFromFormData(String account, String customer, String supplier) + throws InvalidFormDataException { + if (account.equals("None")) { + return null; + } else if (account.equals("Main")) { + return bookingAccountEntryRepository.getByMain().orElseGet(BookingAccountEntry::newMain); + } else if (account.equals("Vat")) { + return bookingAccountEntryRepository.getByVat().orElseGet(BookingAccountEntry::newVat); + } else if (account.equals("Cust")) { + long userId; + try { + userId = Long.parseLong(customer); + } catch (NumberFormatException e) { + throw new InvalidFormDataException("Die angegebene Kunden-Nr. ist ungültig."); + } + Optional bookingAccount = bookingAccountEntryRepository.getByUser(userId); + if (!bookingAccount.isPresent()) { + bookingAccount = userRepository.findById(userId).map((user) -> BookingAccountEntry.newUser(user)); + } + if (bookingAccount.isPresent()) { + return bookingAccount.get(); + } else { + throw new InvalidFormDataException("Der Kunde Nr. " + userId + " konnte nicht gefunden werden."); + } + } else if (account.equals("Sup")) { + long supplierId; + try { + supplierId = Long.parseLong(supplier); + } catch (NumberFormatException e) { + throw new InvalidFormDataException("Die angegebene Lieferanten-Nr. ist ungültig."); + } + Optional bookingAccount = bookingAccountEntryRepository.getBySupplier(supplierId); + if (!bookingAccount.isPresent()) { + bookingAccount = supplierRepository.findById(supplierId) + .map((sup) -> BookingAccountEntry.newSupplier(sup)); + } + if (bookingAccount.isPresent()) { + return bookingAccount.get(); + } else { + throw new InvalidFormDataException( + "Der Lieferant Nr. " + supplierId + " konnte nicht gefunden werden."); + } + } else { + throw new RuntimeException("Invalid form value for an account: " + account); + } + } + + @GetMapping("/intern/accounting/addManual") + public String accountingAddManual(Model model) { + model.addAttribute("form_vals", new ManualAccounting()); + return "intern/accounting/addManual"; + } + + @PostMapping("/intern/accounting/addManual") + public String accountingAddManualSubmit(Model model, @ModelAttribute ManualAccounting formData) { + Booking booking; + try { + booking = createBooking(formData); + bookingRepository.save(booking); + model.addAttribute("info", "Die Buchung wurde erfolgreich erstellt."); + model.addAttribute("form_vals", new ManualAccounting()); // Reply with empty form on success + } catch (InvalidFormDataException e) { + model.addAttribute("error", e.getMessage()); + model.addAttribute("form_vals", formData); + } + return "intern/accounting/addManual"; + } + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java index a15b927..3432770 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/customers/CustomersIndexController.java @@ -1,8 +1,120 @@ 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; +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.hso.ecommerce.entities.user.User; +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.HttpServletResponse; +import java.util.Optional; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/customers") public class CustomersIndexController { + + @Autowired + private BookingRepository bookingRepository = null; + + @Autowired + private final CustomerOrderRepository customerOrderRepository = null; + + @Autowired + private final UserRepository userRepository = null; + + @Autowired + private AccountingController accountingController = null; + + @GetMapping("") + public String internCustomers(Model model) { + List users = userRepository.findAll(); + + model.addAttribute("users", users); + + return "intern/customers/index"; + } + + @GetMapping("/{id}") + public String internCustomersId(Model model, + @PathVariable("id") Long id, + HttpServletResponse response, + HttpServletRequest request + ) { + Optional optUser = userRepository.findById(id); + if (!optUser.isPresent()) { + request.setAttribute("error", "Der User wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; + } + User user = optUser.get(); + model.addAttribute("user", user); + + List orders = customerOrderRepository.getOrdersByUserId(id); + model.addAttribute("orders", orders); + + List bookings = bookingRepository.customerBookingsReverseChronologically(id); + ShortTemplateBookingResult result = accountingController.buildShortTemplate( + bookings, + account -> account.userAccount != null && account.userAccount.id == id); + model.addAttribute("balance", result.balance); + model.addAttribute("bookings", result.bookings); + + return "intern/customers/id"; + } + + @PostMapping("/{id}/changeState") + public String changeState(@PathVariable("id") Long id, + @RequestParam(value = "active", required = false) String active, + @RequestParam(value = "ma", required = false) String ma + ) { + User user = userRepository.findById(id).get(); + + if (active == null) + user.isActive = false; + else + user.isActive = true; + + if (ma == null) + user.isEmployee = false; + else + user.isEmployee = true; + + userRepository.save(user); + + return "redirect:/intern/customers/" + id.toString(); + } + + @PostMapping("/{id}/resetPassword") + public String resetPassword(@PathVariable("id") Long id, + @RequestParam("password") String password, + @RequestParam("password2") String password2, + HttpServletRequest request + ) { + if (!password.equals(password2)) { + request.setAttribute("error", "Passwörter stimmen nicht überein!"); + 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"; + } + user.setPassword("12345"); + userRepository.save(user); + request.setAttribute("info", "Passwort wurde auf 12345 geändert!"); + + return "/intern/customers/id"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java index d6cefc0..e8881fd 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierIndexController.java @@ -1,8 +1,157 @@ package org.hso.ecommerce.controller.intern.suppliers; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.hso.ecommerce.controller.intern.accounting.AccountingController; +import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBooking; +import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult; +import org.hso.ecommerce.entities.booking.Booking; +import org.hso.ecommerce.entities.supplier.Supplier; +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.hso.ecommerce.repos.booking.BookingRepository; +import org.hso.ecommerce.repos.supplier.SupplierOrderRepository; +import org.hso.ecommerce.repos.supplier.SupplierRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/") public class SupplierIndexController { + + @Autowired + private final SupplierRepository supplierRepository = null; + + @Autowired + private final SupplierOrderRepository supplierOrderRepository = null; + + @Autowired + private final BookingRepository bookingRepository = null; + + @Autowired + private final AccountingController accountingController = null; + + @GetMapping("suppliers") + public String listSuppliers(Model model) { + + List totals = new ArrayList(); + + for (Supplier supplier : supplierRepository.findAll()) { + UImodelSuppliers tmp = new UImodelSuppliers(supplier.id, supplier.name); + totals.add(tmp); + } + + model.addAttribute("suppliers", totals); + return "intern/suppliers/index"; + } + + @GetMapping("/suppliers/{id}") + public String supplierDetail(Model model, @PathVariable String id) { + + long supplierId = Long.parseLong(id); + + // add orders from supplier to UImodel + List orders = new ArrayList(); + for (SupplierOrder supplierOrder : supplierOrderRepository.findOrderBySupplierID(supplierId)) { + orders.add(new UImodelSupplierDetailOrders(supplierOrder)); + } + + // Table of bookings + List bookings = bookingRepository.supplierBookingsReverseChronologically(supplierId); + ShortTemplateBookingResult bookingResult = accountingController.buildShortTemplate(bookings, + account -> account.supplierAccount != null && account.supplierAccount.id == supplierId); + + UImodelSupplierDetail total = new UImodelSupplierDetail(supplierRepository.findSupplierById(supplierId).name, + bookingResult.balance, orders, bookingResult.bookings); + + model.addAttribute("SupplierDetail", total); + + return "intern/suppliers/id"; + } + + public class UImodelSuppliers { + public long id; + public String name; + + public UImodelSuppliers(long id, String name) { + this.id = id; + this.name = name; + } + + } + + public class UImodelSupplierDetail { + + public String name; + public String balance; + public List orders; + public List bookings; + + public UImodelSupplierDetail(String name, String balance, List orders, + List bookings + ) { + this.name = name; + this.balance = balance; + this.orders = orders; + this.bookings = bookings; + } + + } + + public class UImodelSupplierDetailOrders { + public long id; + public String dateOrder; + public String articleName; + public long articleId; + public String priceNet; + public String quantity; + public String priceTotal; + public boolean arrived; + + public UImodelSupplierDetailOrders(SupplierOrder order) { + this.id = order.id; + this.articleName = order.ordered.title; + this.articleId = order.ordered.id; + this.priceNet = String.format("%.2f", ((float) order.pricePerUnitNetCent / 100)); + this.quantity = String.valueOf(order.numberOfUnits); + this.priceTotal = String.format("%.2f", ((float) order.totalPriceNet / 100)); + + Date date = new Date(); + date.setTime(order.created.getTime()); + this.dateOrder = new SimpleDateFormat("dd.MM.yyyy").format(date); + + if (order.delivered != null) { + arrived = true; + } else { + arrived = false; + } + } + } + + public class UImodelSupplierDetailBookings { + + public String dateBooking; + public String price; + public String srcName; + public String balance; + public String reason; + public long orderID; + + public UImodelSupplierDetailBookings(Booking booking) { + Date date = new Date(); + date.setTime(booking.reason.supplierOrder.created.getTime()); + this.dateBooking = new SimpleDateFormat("dd.MM.yyyy").format(date); + this.price = String.format("%.2f", ((float) booking.amountCent / 100)); + this.srcName = ((booking.source.isMainAccount) ? "Hauptkonto" : booking.source.supplierAccount.name); + this.balance = String.format("%.2f", ((float) booking.destination.newSumCent / 100)); + this.reason = booking.reason.comment; + this.orderID = booking.reason.supplierOrder.id; + } + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java index ec6dfbf..98a4c8b 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOfferController.java @@ -1,8 +1,77 @@ package org.hso.ecommerce.controller.intern.suppliers; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.shop.OffersRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/") public class SupplierOfferController { + + @Autowired + private final OffersRepository offersRepository = null; + + @Autowired + private final ArticleRepository articleRepository = null; + + @GetMapping("supplierOffers") + public String internListedArticles(Model model) { + + List totals = new ArrayList<>(); + + for (ArticleOffer article : offersRepository.findAll()) { + UImodelOfferedArticle tmp = new UImodelOfferedArticle(article, + articleRepository.findArticleIDByRelatedID(article.id)); + totals.add(tmp); + } + + model.addAttribute("OfferedArticles", totals); + return "intern/offeredArticles/index"; + } + + public class UImodelOfferedArticle { + + public long offerId; + public String title; + public String manufacturer; + public String articleNumber; + public String supplierName; + public String price; + public String ads; + public int listedArticleId; + public boolean offerIsListed; // true --> offered article is listed + + public UImodelOfferedArticle(ArticleOffer article, Optional listedArticleId) { + + this.offerId = article.id; + this.title = article.title; + this.manufacturer = article.manufacturer; + this.articleNumber = article.articleNumber; + if (article.cheapestSupplier != null) { + this.supplierName = article.cheapestSupplier.name; + } else { + this.supplierName = "-"; + } + this.price = String.format("%.2f", ((float) article.pricePerUnitNet / 100)); + this.ads = (article.shouldBeAdvertised) ? "Ja" : "Nein"; + + if (listedArticleId.isPresent()) { + // this offer is listed --> show link + this.listedArticleId = listedArticleId.get(); + offerIsListed = true; + } else { + // this offer is not listed + offerIsListed = false; + } + } + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java index ef3acb4..3259a17 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/suppliers/SupplierOrderController.java @@ -1,8 +1,135 @@ package org.hso.ecommerce.controller.intern.suppliers; +import org.hso.ecommerce.action.warehouse.SupplierOrderArrivedAction; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.supplier.SupplierOrderRepository; +import org.hso.ecommerce.repos.warehouse.SlotRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.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; + +import javax.servlet.http.HttpServletResponse; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; @Controller -//@RequestMapping("...") +@RequestMapping("/intern/") public class SupplierOrderController { + + @Autowired + private final SupplierOrderRepository supplierOrderRepository = null; + + @Autowired + private final ArticleRepository articleRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @Autowired + private final SlotRepository slotRepository = null; + + @GetMapping("supplierOrders") + public String listSuppliers(Model model) { + + List totals = new ArrayList(); + + for (SupplierOrder order : supplierOrderRepository.findAll()) { + final Article article = articleRepository.findArticleByArticleOffer(order.ordered).orElse(null); + totals.add(new UImodelSupplierOrder(order, article)); + } + + model.addAttribute("orders", totals); + + return "intern/supplierOrders/index"; + } + + @PostMapping("/supplierOrders/store/{id}") + public String storeOrder(@PathVariable("id") Long supplierOrderID, Model model, HttpServletResponse response) { + SupplierOrder order = supplierOrderRepository.findById(supplierOrderID).orElse(null); + if (order == null) { + model.addAttribute("error", "Die ausgewählte Lieferung konnte nicht gefunden werden."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return listSuppliers(model); + } + if (order.wasDelivered()) { + model.addAttribute("error", "Die ausgewählte Lieferung wurde schon zugestellt."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return listSuppliers(model); + } + + + final Article article = articleRepository.findArticleByArticleOffer(order.ordered).orElse(null); + if (article == null) { + model.addAttribute("error", "Der bestellte Artikel wurde nicht angelegt, er hätte nicht bestellt werden dürfen."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return listSuppliers(model); + } + + // Hard to do efficiently, this should be fine. + List candidates = slotRepository + .findAll() + .stream() + .map(slot -> + warehouseBookingPositionSlotEntryRepository.getBySlotNum(slot.slotNum).orElseGet(() -> + WarehouseBookingPositionSlotEntry.empty(article, slot) + ) + ) + .filter(entry -> entry.article.id == article.id || entry.newSumSlot == 0) + .collect(Collectors.toList()); + + SupplierOrderArrivedAction action = new SupplierOrderArrivedAction(candidates, order, article); + + try { + SupplierOrderArrivedAction.Result result = action.finish(); + supplierOrderRepository.save(result.getOrder()); + warehouseBookingRepository.save(result.getBooking()); + } catch (SupplierOrderArrivedAction.NoSpaceInWarehouseException e) { + e.printStackTrace(); + } + + return "redirect:/intern/warehouse/todo"; + } + + public class UImodelSupplierOrder { + public long id; + public String dateOrder; + public String supplierName; + public String articleName; + public long articleId; + public String priceNet; + public String quantity; + public String priceTotal; + public boolean arrived; + + public UImodelSupplierOrder(SupplierOrder order, Article article) { + this.id = order.id; + this.supplierName = order.supplier.name; + this.articleName = article != null ? article.title : "error"; + this.articleId = article != null ? article.id : 0; + this.priceNet = String.format("%.2f", ((float) order.pricePerUnitNetCent / 100)); + this.quantity = String.valueOf(order.numberOfUnits); + this.priceTotal = String.format("%.2f", ((float) order.totalPriceNet / 100)); + + Date date = new Date(); + date.setTime(order.created.getTime()); + this.dateOrder = new SimpleDateFormat("dd.MM.yyyy").format(date); + + arrived = order.delivered != null; + } + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/ManuelBookingController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/ManuelBookingController.java new file mode 100644 index 0000000..7ef8e0e --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/ManuelBookingController.java @@ -0,0 +1,140 @@ +package org.hso.ecommerce.controller.intern.warehouse; + +import org.hso.ecommerce.action.warehouse.CreateManuelBookingAction; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.warehouse.Slot; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.warehouse.SlotRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Optional; + +@Controller +@RequestMapping("/intern/warehouse/") +public class ManuelBookingController { + + @Autowired + private final ArticleRepository articleRepository = null; + + @Autowired + private final SlotRepository slotRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @GetMapping("addManual") + public String warehouseAddManual( + Model model + ) { + + model.addAttribute("articles", articleRepository.findAll()); + model.addAttribute("slots", slotRepository.findAll()); + + return "intern/warehouse/addManual"; + } + + @PostMapping("addManual") + public String warehouseAddMaualPost( + Model model, + HttpServletRequest request, + HttpServletResponse response, + @RequestParam("articleId") String articleIdText, + @RequestParam("amount") Integer amount, + @RequestParam("reason") String reason, + @RequestParam("sourceIsSlot") Boolean sourceIsSlot, + @RequestParam("sourceSlot") Integer sourceSlotNum, + @RequestParam("destinationIsSlot") Boolean destinationIsSlot, + @RequestParam("destinationSlot") Integer destinationSlotNum + ) { + + // The suggestions for articleId in the UI show articles names, seperated by a " - ". + // The Number must be extracted first. + long articleId = -1; + try { + articleId = Long.parseLong(articleIdText.split(" - ", 2)[0].trim()); + } catch (NumberFormatException e) { + model.addAttribute("error", "Die Artikel Id konnte nicht erkannt werden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "intern/warehouse/addManual"; + } + + + Optional
optionalArticle = articleRepository.findById(articleId); + Article article = null; + if (!optionalArticle.isPresent()) { + model.addAttribute("error", "Der Artikel konnte nicht gefunden werden."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } else { + article = optionalArticle.get(); + } + + if (amount <= 0) { + model.addAttribute("error", "Eine Anzahl <= 0 kann nicht verbucht werden."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + if (sourceIsSlot == false && destinationIsSlot == false) { + model.addAttribute("error", "Jede Buchung benötigt ein Ziel oder eine Quelle."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + Optional sourceSlot = Optional.empty(); + if (sourceIsSlot == true) { + sourceSlot = warehouseBookingPositionSlotEntryRepository.getBySlotNum(sourceSlotNum); + if (!sourceSlot.isPresent()) { + request.setAttribute("error", "Quelllagerplatz wurde nicht gefunden oder ist leer."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + } + + Optional destinationSlot = Optional.empty(); + if (destinationIsSlot == true) { + Optional slot = slotRepository.findBySlotNum(destinationSlotNum); + if (!slot.isPresent()) { + request.setAttribute("error", "Ziellagerplatz wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + Article finalArticle = article; + destinationSlot = Optional.of(warehouseBookingPositionSlotEntryRepository.getBySlotNum(destinationSlotNum).orElseGet( + () -> WarehouseBookingPositionSlotEntry.empty(finalArticle, slot.get()) + )); + } + + try { + warehouseBookingRepository.save( + new CreateManuelBookingAction(article, amount, sourceSlot, destinationSlot, reason).finish() + ); + } catch (CreateManuelBookingAction.ArticleSlotConstraintArticleTypeFailedException e) { + model.addAttribute("error", "Es befindet sich der falsche Artikeltyp in Quell- oder Ziellagerplatz. "); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } catch (CreateManuelBookingAction.ArticleSlotConstraintFailedException e) { + model.addAttribute("error", "Die maximale Anzahl an lagerbaren Artikeln im Ziellagerplatz wurde überschritten."); + response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); + return "intern/warehouse/addManual"; + } + + return "redirect:/intern/warehouse/todo"; + } + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/SlotsController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/SlotsController.java new file mode 100644 index 0000000..8cc5721 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/SlotsController.java @@ -0,0 +1,46 @@ +package org.hso.ecommerce.controller.intern.warehouse; + +import org.hso.ecommerce.action.warehouse.CalculateWarehouseStatsAction; +import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; +import org.hso.ecommerce.repos.warehouse.SlotRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.stream.Collectors; + +@Controller +@RequestMapping("/intern/warehouse/") +public class SlotsController { + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + private final SlotRepository slotRepository = null; + + @GetMapping("slots/") + public String accountingWarehouseSlots( + Model model, + HttpServletRequest request + ) { + + // Doing this in a single would be hard and error prone. + // Writing native queries should be minimized. + // Therefore this method was prefered + List entries = slotRepository.findAll().stream().map( + s -> warehouseBookingPositionSlotEntryRepository + .getBySlotNum(s.slotNum) + .orElseGet(() -> WarehouseBookingPositionSlotEntry.empty(null, s)) + ).collect(Collectors.toList()); + model.addAttribute("entries", entries); + + model.addAttribute("stats", new CalculateWarehouseStatsAction(entries).finish()); + return "intern/warehouse/slots/index"; + } +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/TodoController.java b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/TodoController.java new file mode 100644 index 0000000..4bed5cd --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/controller/intern/warehouse/TodoController.java @@ -0,0 +1,112 @@ +package org.hso.ecommerce.controller.intern.warehouse; + +import org.hso.ecommerce.action.shop.EnableTrackingAction; +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.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; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Optional; + +@Controller +@RequestMapping("/intern/warehouse/") +public class TodoController { + + @Autowired + private final WarehouseBookingRepository warehouseBookingRepository = null; + + @GetMapping("todo") + public String accountingWarehouseTodo( + Model model + ) { + model.addAttribute("bookings", warehouseBookingRepository.findNotDone()); + return "intern/warehouse/todo"; + } + + + @PostMapping("progress/{id}") + public String postProgressId( + Model model, + HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id + ) { + Optional booking = warehouseBookingRepository.findById(id); + if (!booking.isPresent()) { + model.addAttribute("error", "Die Buchung wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "error/404"; + } + + if (booking.get().isInProgress) { + response.setStatus(409); + return "intern/warehouse/error_progress_failed"; + } + + booking.get().isInProgress = true; + warehouseBookingRepository.save(booking.get()); + + return "redirect:/intern/warehouse/progress/" + id; + } + + @PostMapping("progress/{id}/finish") + public String postProgressIdFinish( + Model model, + HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id + ) { + Optional booking = warehouseBookingRepository.findById(id); + if (!booking.isPresent()) { + model.addAttribute("error", "Die Buchung wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "error/404"; + } + + booking.get().isInProgress = true; + booking.get().isDone = true; + + // Update Delivery Date + if (booking.get().reason.customerOrder != null) { + EnableTrackingAction.addTrackingInfo(booking.get().reason.customerOrder); + } + + warehouseBookingRepository.save(booking.get()); + + return "redirect:/intern/warehouse/todo"; + } + + @GetMapping("progress/{id}") + public String getProgressId(Model model, + HttpServletRequest request, + HttpServletResponse response, + @PathVariable("id") Long id) { + Optional booking = warehouseBookingRepository.findById(id); + if (!booking.isPresent()) { + model.addAttribute("error", "Die Buchung wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return "error/404"; + } + + if (booking.get().isDone) { + model.addAttribute("info", "Die Buchung wurde schon abgeschlossen."); + } + + if (!booking.get().isInProgress) { + // Only reachable if path is manipulated. + model.addAttribute("error", "Die Buchung wurde noch nicht zugewiesen!"); + } + + model.addAttribute("booking", booking.get()); + return "intern/warehouse/id_progress"; + } + + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java index 27ccf63..b1df109 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopArticleController.java @@ -1,8 +1,118 @@ 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; +import org.hso.ecommerce.repos.shop.CategoryRepository; +import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; +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.*; + +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; +import java.util.List; @Controller -//@RequestMapping("...") +@RequestMapping("/shop/articles") public class ShopArticleController { -} + + @Autowired + private final ArticleRepository articleRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @Autowired + private final CategoryRepository categoryRepository = null; + + @GetMapping("/{id}") + public String shopArticlesById(Model model, + @PathVariable("id") Long id, + HttpServletRequest request, + HttpServletResponse response + ) { + model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar + + Article article = articleRepository.findArticleById(id); + + if (article == null) { + request.setAttribute("error", "Der Artikel wurde nicht gefunden."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; + } + model.addAttribute("article", article); + + if (warehouseBookingPositionSlotEntryRepository.getByArticle(id).get(0).newSumSlot > 0) { //check if in Stock + model.addAttribute("inStock", true); + } else { + model.addAttribute("inStock", false); + } + + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(3, articleRepository.getAdvertisedArticles()); //get 3 advertised Articles + model.addAttribute("commercialArticles", commercialArticles); + + return "shop/articles/id"; + } + + @PostMapping("/{id}") + public String shopArticlesByIdBuy(HttpServletRequest request, + HttpServletResponse response, + HttpSession session, + @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 + ) { + 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"; + } + + if (setAmount != null && setAmount) { + shoppingCart.setArticleCount(article, quantity); + } else { + shoppingCart.addArticle(article, quantity); + } + + if (!fastcheckout) { + return "shop/articles/post_add"; + } else { + 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); + + if(article.image != null) { + 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."); + } + } + } +} \ No newline at end of file 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..8b0b1ba 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,158 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.action.shop.CreateOrderAction; +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.CustomerOrderRepository; +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.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.util.ArrayList; +import java.util.TreeMap; @Controller -//@RequestMapping("...") +@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 CustomerOrderRepository customerOderRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository wbeseRepo = null; + + @GetMapping("/checkout") + 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( + HttpSession session, + HttpServletRequest request, + HttpServletResponse response, + @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 + ) { + + 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(); + + CreateOrderAction action = new CreateOrderAction( + user, + expectedPrice, + Address.fromString(address), + PaymentMethod.fromCreditCardNumber(cardnumber), + 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.id)); + } + + CreateOrderAction.Result result = null; + try { + result = action.finish(); + + customerOderRepository.save(result.customerOrder); + bookingRepository.saveAll(result.bookings); + warehouseBookingRepository.save(result.warehouseBooking); + + shoppingCart.clear(); + + } catch (CreateOrderAction.ArticleNotInStockException e) { + request.setAttribute("error", "Der Artikel '" + e.getArticle().title + "' ist leider nicht mehr in ausreichender Menge verfügbar. Bitte passen Sie die Artikelmenge an."); + return shopCheckout(session, request, shoppingCart); + } + + + return "shop/checkoutFinish"; + } + + @GetMapping("/checkoutFinish") + public String shopCheckoutFinishGET() { + return "shop/checkoutFinish"; + } } 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..71ecf70 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,72 @@ 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.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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpSession; +import java.util.List; @Controller -//@RequestMapping("...") +@RequestMapping("/") public class ShopIndexController { + + @Autowired + private final ArticleRepository articleRepository = null; + + @Autowired + private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; + + @GetMapping("/") + public String home() { + return "redirect:/shop/"; + } + + @GetMapping("/shop/") + public String shop(Model model, HttpSession session) { + + List
commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); //get random advertised Articles + model.addAttribute("commercialArticles", commercialArticles); + + boolean isLoggedIn = false; + boolean hasOrders = false; + + if (session != null && session.getAttribute("userId") != null) { //check if logged in + long userId = (long) session.getAttribute("userId"); + isLoggedIn = true; + + List
suggestedArticles = articleRepository.getOrderedArticles(userId); + suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0, 4) : suggestedArticles; //only latest 4 ordered articles + if (suggestedArticles.size() > 0) { + model.addAttribute("suggestedArticles", suggestedArticles); + hasOrders = true; + } + } + + model.addAttribute("isLoggedIn", isLoggedIn); + model.addAttribute("hasOrders", hasOrders); + + return "shop/index"; + } + + @GetMapping("/about") + public String about() { + return "about"; + } + + @GetMapping("/terms") + public String terms() { + return "terms"; + } + + @GetMapping("/privacy") + public String privacy() { + return "privacy"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java index 05a795f..47b8acf 100644 --- a/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java +++ b/prototype/src/main/java/org/hso/ecommerce/controller/shop/ShopSearchController.java @@ -1,8 +1,50 @@ package org.hso.ecommerce.controller.shop; +import org.hso.ecommerce.action.shop.SearchByTermAction; +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.repos.shop.ArticleRepository; +import org.hso.ecommerce.repos.shop.CategoryRepository; +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; @Controller -//@RequestMapping("...") +@RequestMapping("/shop/search") public class ShopSearchController { -} + + @Autowired + private final ArticleRepository articleRepository = null; + + @Autowired + private final CategoryRepository categoryRepository = null; + + @GetMapping("") + public String searchArticles(@RequestParam(required = false, value = "term") String term, + @RequestParam(required = false, value = "category") String category, + Model model, + HttpServletRequest request, + HttpServletResponse response + ) { + model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar + + if (term != null) { //if search by Term + term = term.trim(); + List
articles = SearchByTermAction.searchByTerm(term, articleRepository); + model.addAttribute("articles", articles); + } else if (category != null) { //if search by Category + List
articles = articleRepository.getArticlesByCategory(category); //search by Category + model.addAttribute("articles", articles); + } else { + request.setAttribute("error", "Es wurden keine Suchparameter angegeben."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return "error/404"; + } + + return "/shop/search"; + } +} \ No newline at end of file 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..64ef1cd 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 @@ -1,6 +1,7 @@ package org.hso.ecommerce.entities.booking; import javax.persistence.*; +import javax.validation.constraints.NotNull; @Entity @Table(name = "bookings") @@ -14,12 +15,15 @@ public class Booking { // always >= 0 public int amountCent; - @ManyToOne(optional = true) + @NotNull + public java.sql.Timestamp created; + + @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..64f2931 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingAccountEntry.java @@ -16,13 +16,57 @@ public class BookingAccountEntry { public int newSumCent; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.MERGE) public User userAccount; - @ManyToOne(optional = true) + @ManyToOne(optional = true, cascade = CascadeType.MERGE) 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 newSupplier(Supplier supplier) { + BookingAccountEntry e = new BookingAccountEntry(); + e.supplierAccount = supplier; + e.newSumCent = 0; + + return e; + } + + public static BookingAccountEntry newMain() { + BookingAccountEntry e = new BookingAccountEntry(); + e.isMainAccount = true; + 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..8d6572f 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/BookingReason.java @@ -22,9 +22,30 @@ 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; + + // Default Constructor is needed for construction by ORM + public BookingReason() { + } + + public BookingReason(CustomerOrder order) { + this.customerOrder = order; + } + + public BookingReason(CustomerPayment customerPayment) { + this.customerPayment = customerPayment; + } + + public BookingReason(SupplierOrder supplierOrder) { + this.supplierOrder = supplierOrder; + } + + public BookingReason(String comment) { + this.isManuel = true; + this.comment = comment; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java index 6cb5307..fa9fe62 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/booking/PaymentMethod.java @@ -1,10 +1,16 @@ package org.hso.ecommerce.entities.booking; import javax.persistence.Embeddable; -import javax.validation.constraints.NotNull; @Embeddable public class PaymentMethod { - @NotNull + public String creditCardNumber; + + public static PaymentMethod fromCreditCardNumber(String cardnumber) { + PaymentMethod m = new PaymentMethod(); + m.creditCardNumber = cardnumber; + + return m; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/cron/BackgroundJob.java b/prototype/src/main/java/org/hso/ecommerce/entities/cron/BackgroundJob.java index 14a4ae3..7b0e09e 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/cron/BackgroundJob.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/cron/BackgroundJob.java @@ -7,8 +7,8 @@ import javax.validation.constraints.NotNull; @Table(name = "background_jobs") public class BackgroundJob { - public final String JOB_DASHBOARD = "Dashboard"; - public final String JOB_REORDER = "SupplierOrder"; + public static final String JOB_DASHBOARD = "Dashboard"; + public static final String JOB_REORDER = "SupplierOrder"; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) 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/Article.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Article.java index 6cd198e..d9fe69a 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 @@ -16,7 +16,7 @@ public class Article { @Basic public long id; - @ManyToOne(optional = false) + @OneToOne(optional = false) public ArticleOffer related; public int shopPricePerUnitNetCent; @@ -32,9 +32,18 @@ public class Article { public String description; @OneToOne(optional = true) + @Basic(fetch = FetchType.LAZY) public Image image; - @ManyToMany + @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "article_categories_bindings") public Set categories = new HashSet<>(); -} + + public int getVat() { + return (shopPricePerUnitNetCent * related.vatPercent) / 100; + } + + public int getPriceGross() { + return shopPricePerUnitNetCent + getVat(); + } +} \ No newline at end of file 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..d552869 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; @@ -22,4 +20,15 @@ public class Category { @ManyToMany(mappedBy = "categories") public Set
articles = new HashSet<>(); + + + public Category() { + + } + + + public Category (String name) { + this.name = name; + } + } 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..853b885 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrder.java @@ -4,6 +4,7 @@ import org.hso.ecommerce.entities.user.User; import javax.persistence.*; import javax.validation.constraints.NotNull; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; @@ -24,14 +25,14 @@ public class CustomerOrder { @OneToMany( targetEntity = CustomerOrderPosition.class, - mappedBy = "order" + mappedBy = "order", cascade = CascadeType.ALL ) public List positions = new ArrayList<>(); @NotNull public java.sql.Timestamp created; - @NotNull + @Column(nullable = true) public String trackingId; @Column(nullable = true) @@ -43,4 +44,21 @@ public class CustomerOrder { public int totalNetCent; public int totalGrossCent; public int totalVatCent; + + public String formatInDeliverySince(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(inDeliverySince); + } + + public String formatCreated(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(created); + } + + public String formatDeliveredAt(){ + return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt); + } + + public String getEstimatedArrival() { + //TODO: get estimated arrival from api + return "TODO TODO TODO"; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java index dbe53f7..2569158 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/CustomerOrderPosition.java @@ -19,4 +19,8 @@ public class CustomerOrderPosition { public int pricePerUnit; public int quantity; + + public int getSumPrice(){ + return article.getPriceGross() * quantity; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java index 33eebdb..6609853 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/Image.java @@ -11,7 +11,5 @@ public class Image { @Basic public long id; - @Lob - @Column(name = "data", columnDefinition = "BLOB", nullable = false) - private byte[] data; -} + public String path; +} \ No newline at end of file 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..d059f15 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/entities/shop/ShoppingCart.java @@ -0,0 +1,103 @@ +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 { + + private final static int MAX_ITEMS = 10; + + private int revision; + private ArrayList items; + + public ShoppingCart() { + clear(); + } + + public void clear() { + items = new ArrayList<>(); + revision = (int) Math.round(Math.random() * 0xFFFF); + } + + public List getItems() { + return items; + } + + 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)); + } + + 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; + + 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; + if (this.amount > MAX_ITEMS) { + this.amount = MAX_ITEMS; + } + } + + public long getArticleId() { + return articleId; + } + + 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/supplier/ArticleOffer.java b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java index 005b0b9..41296ba 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/supplier/ArticleOffer.java @@ -14,9 +14,20 @@ public class ArticleOffer { @NotNull public String manufacturer; + + @NotNull + public String title; + + @NotNull + public int pricePerUnitNet; @NotNull public String articleNumber; public int vatPercent; + + public boolean shouldBeAdvertised; + + @ManyToOne(optional = true) + public Supplier cheapestSupplier; } 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..e08c2ee 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/user/User.java @@ -24,16 +24,21 @@ public class User { @Column(unique = true) public String email; + @Column(insertable = false, updatable = false) + public String name; + + public String salutation; + public String passwordHash; public boolean isActive; public boolean isEmployee; @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/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/WarehouseBooking.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java index 97a2805..08eaa40 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBooking.java @@ -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,11 @@ public class WarehouseBooking { public boolean isDone; @OneToMany( - mappedBy = "booking" + mappedBy = "booking", cascade = CascadeType.ALL ) public List positions = new ArrayList<>(); + + // TODO FIX ME + @OneToOne(optional = false, cascade = CascadeType.ALL) + public BookingReason reason; } diff --git a/prototype/src/main/java/org/hso/ecommerce/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..15233df 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,11 +14,43 @@ public class WarehouseBookingPositionSlotEntry { @Basic public long id; + @NotNull @ManyToOne public Article article; - public int newSumArticles; - public int newSumWarehousePosition; + @NotNull + public int newSumSlot; - public int slot; + @NotNull + @ManyToOne + public Slot slot; + + public WarehouseBookingPositionSlotEntry copyAddAmount(int amount, Article article) { + // Article can be changed if newSumSlot == 0. + if (this.newSumSlot != 0 && this.article.id != article.id) { + throw new IllegalArgumentException("Article does not match."); + } + + 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; + } + + public static WarehouseBookingPositionSlotEntry empty(Article article, Slot slot) { + WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry(); + + e.article = article; + e.slot = slot; + e.newSumSlot = 0; + + return e; + } } diff --git a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java index ec60c4d..e287141 100644 --- a/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java +++ b/prototype/src/main/java/org/hso/ecommerce/entities/warehouse/WarehouseBookingReason.java @@ -23,4 +23,12 @@ public class WarehouseBookingReason { public CustomerOrder customerOrder; public boolean isManuel; + + // Default Constructor is needed for construction by ORM + 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..53f0d62 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingAccountEntryRepository.java @@ -0,0 +1,28 @@ +package org.hso.ecommerce.repos.booking; + +import java.util.Optional; + +import org.hso.ecommerce.entities.booking.BookingAccountEntry; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface BookingAccountEntryRepository extends JpaRepository { + + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.user_account_id = :user ORDER BY e.id DESC LIMIT 1", nativeQuery = true) + Optional getByUser(Long user); + + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.supplier_account_id = :supplier ORDER BY e.id DESC LIMIT 1", nativeQuery = true) + Optional getBySupplier(Long supplier); + + @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.is_main_account = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true) + Optional getByMain(); + + @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/booking/BookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java new file mode 100644 index 0000000..e72ae6a --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/booking/BookingRepository.java @@ -0,0 +1,28 @@ +package org.hso.ecommerce.repos.booking; + +import java.util.List; + +import org.hso.ecommerce.entities.booking.Booking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface BookingRepository extends JpaRepository { + + @Query("SELECT b FROM Booking b ORDER BY b.id DESC") + List allBookingsReverseChronologically(); + + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.isMainAccount = 1 OR d.isMainAccount = 1 ORDER BY b.id DESC") + List mainBookingsReverseChronologically(); + + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.isVATAccount = 1 OR d.isVATAccount = 1 ORDER BY b.id DESC") + List vatBookingsReverseChronologically(); + + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.userAccount.id = :customerId OR d.userAccount.id = :customerId ORDER BY b.id DESC") + List customerBookingsReverseChronologically(long customerId); + + @Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.supplierAccount.id = :supplierId OR d.supplierAccount.id = :supplierId ORDER BY b.id DESC") + List supplierBookingsReverseChronologically(long supplierId); + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/cronjob/BackgroundJobRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/cronjob/BackgroundJobRepository.java new file mode 100644 index 0000000..41163d0 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/cronjob/BackgroundJobRepository.java @@ -0,0 +1,10 @@ +package org.hso.ecommerce.repos.cronjob; + +import org.hso.ecommerce.entities.cron.BackgroundJob; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BackgroundJobRepository extends JpaRepository { + +} 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..0925ea0 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ArticleRepository.java @@ -0,0 +1,53 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.Article; +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface ArticleRepository extends JpaRepository { + + /*** + * use findById instead. + */ + @Deprecated + @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(value = "Select a.* from articles as a, article_offers as ao, warehouse_booking_position_entries as wbpe where a.related_id = ao.id and wbpe.article_id = a.id and ao.should_be_advertised = true group by wbpe.slot_id having max(wbpe.id) and wbpe.new_sum_slot != 0", nativeQuery = 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 CustomerOrderPosition cop JOIN cop.order co JOIN co.customer c JOIN cop.article a WHERE c.id = :customerId ORDER BY co.id DESC") + List
getOrderedArticles(long customerId); + + /*** + * use type safe findArticleByArticleOffer instead. + */ + @Deprecated + @Query(value = "SELECT a.id FROM articles a WHERE a.related_id = :relatedId", nativeQuery = true) + Optional findArticleIDByRelatedID(@Param("relatedId") long relatedId); + + @Query(value = "SELECT a FROM Article a Where a.related = :related") + Optional
findArticleByArticleOffer(ArticleOffer related); + + @Query(value = "Select a.* from articles as a, warehouse_booking_position_entries as wbpe where wbpe.article_id = a.id and a.title LIKE %:term% group by wbpe.slot_id having max(wbpe.id) and wbpe.new_sum_slot != 0", nativeQuery = true) + List
getArticlesByTermInTitle(String term); + + @Query(value = "Select a.* from articles as a, warehouse_booking_position_entries as wbpe where wbpe.article_id = a.id and a.description LIKE %:term% group by wbpe.slot_id having max(wbpe.id) and wbpe.new_sum_slot != 0", nativeQuery = true) + List
getArticlesByTermInDescription(String term); + + @Query(value = "Select a.* from articles as a, categories as c, article_categories_bindings as acb, warehouse_booking_position_entries as wbpe where wbpe.article_id = a.id and acb.articles_id = a.id and acb.categories_id = c.id and c.name = :category group by wbpe.slot_id having max(wbpe.id) and wbpe.new_sum_slot != 0", nativeQuery = true) + List
getArticlesByCategory(String category); +} \ No newline at end of file diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CategoryRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CategoryRepository.java new file mode 100644 index 0000000..4f4133a --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CategoryRepository.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.Category; +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 CategoryRepository extends JpaRepository { + + @Query("SELECT a FROM Category a WHERE a.name = :name") + Optional findCategoryByName(@Param("name") String name); + + @Query("SELECT c FROM Category c") + List getCategories(); +} \ No newline at end of file diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java new file mode 100644 index 0000000..86c88e6 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/CustomerOrderRepository.java @@ -0,0 +1,21 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.CustomerOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CustomerOrderRepository extends JpaRepository { + + @Query("SELECT SUM(cop.quantity) FROM CustomerOrderPosition cop JOIN cop.order co WHERE cop.article.id = :articleId AND co.created >= :begin AND co.created < :end") + Integer countOrdersOfArticleInTimespan( + long articleId, java.sql.Timestamp begin, java.sql.Timestamp end + ); + + @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId ORDER BY co.id DESC") + List getOrdersByUserId(long userId); + +} \ No newline at end of file diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java new file mode 100644 index 0000000..d0f3997 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/ImageRepository.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.repos.shop; + +import org.hso.ecommerce.entities.shop.Image; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface ImageRepository extends JpaRepository { + + @Query("SELECT i.id FROM Image i WHERE i.path = :path") + Optional findImageIDByPath(@Param("path") String path); + + @Query("SELECT i FROM Image i WHERE i.id = :imageId") + Image findImageById(@Param("imageId") long imageId); + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java new file mode 100644 index 0000000..20a3f90 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/shop/OffersRepository.java @@ -0,0 +1,20 @@ +package org.hso.ecommerce.repos.shop; + +import java.util.List; + +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface OffersRepository extends JpaRepository { + + @Query("SELECT a FROM ArticleOffer a") + List findAll(); + + @Query("SELECT a FROM ArticleOffer a WHERE a.id = :offeredarticleId") + ArticleOffer findOfferedArticleById(@Param("offeredarticleId") long offeredarticleId); + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/ArticleOfferRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/ArticleOfferRepository.java new file mode 100644 index 0000000..41263ff --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/ArticleOfferRepository.java @@ -0,0 +1,8 @@ +package org.hso.ecommerce.repos.supplier; + +import org.hso.ecommerce.entities.supplier.ArticleOffer; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ArticleOfferRepository extends JpaRepository { + +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java new file mode 100644 index 0000000..06ad249 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierOrderRepository.java @@ -0,0 +1,22 @@ +package org.hso.ecommerce.repos.supplier; + +import java.util.List; + +import org.hso.ecommerce.entities.supplier.SupplierOrder; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface SupplierOrderRepository extends JpaRepository { + + @Query("SELECT SUM(so.numberOfUnits) FROM SupplierOrder so JOIN so.ordered ao WHERE ao.articleNumber = :articleNumber AND so.delivered IS NULL") + Integer countUndeliveredReorders(String articleNumber); + + @Query(value = "SELECT * FROM supplier_orders as a WHERE a.supplier_id = :supplierId", nativeQuery = true) + List findOrderBySupplierID(@Param("supplierId") long supplierId); + + @Query("SELECT a FROM SupplierOrder a") + List findAll(); +} diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java new file mode 100644 index 0000000..14b3a75 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/supplier/SupplierRepository.java @@ -0,0 +1,19 @@ +package org.hso.ecommerce.repos.supplier; + +import java.util.List; + +import org.hso.ecommerce.entities.supplier.Supplier; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface SupplierRepository extends JpaRepository { + + @Query("SELECT a FROM Supplier a") + List findAll(); + + @Query("SELECT a FROM Supplier a WHERE a.id = :supplierId") + Supplier findSupplierById(@Param("supplierId") long supplierId); +} \ No newline at end of file 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 new file mode 100644 index 0000000..804e8b7 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingPositionSlotEntryRepository.java @@ -0,0 +1,24 @@ +package org.hso.ecommerce.repos.warehouse; + +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; +import java.util.Optional; + +@Repository +public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository { + + @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) + List getByArticle(long article); + + @Query(value = "SELECT SUM(w.new_sum_slot) FROM warehouse_booking_position_entries as w WHERE w.article_id = :articleid", nativeQuery = true) + Optional getArticleStock(long articleid); + + @Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND s.slot_num = :slotnum GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) + Optional getBySlotNum(long slotnum); + +} + diff --git a/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java new file mode 100644 index 0000000..08434c4 --- /dev/null +++ b/prototype/src/main/java/org/hso/ecommerce/repos/warehouse/WarehouseBookingRepository.java @@ -0,0 +1,17 @@ +package org.hso.ecommerce.repos.warehouse; + +import org.hso.ecommerce.entities.warehouse.WarehouseBooking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface WarehouseBookingRepository extends JpaRepository { + + @Query("Select b FROM WarehouseBooking b WHERE b.isDone = 0") + List findNotDone(); + +} + diff --git a/prototype/src/main/resources/application.properties b/prototype/src/main/resources/application.properties index b62e80f..aee1c7d 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 @@ -16,6 +16,8 @@ spring.jpa.show-sql=true #server.servlet.session.persistent=true # ---------------------------------------- # WEB PROPERTIES +spring.servlet.multipart.max-file-size=10MB +spring.servlet.multipart.max-request-size=10MB # ---------------------------------------- # EMBEDDED SERVER CONFIGURATION (ServerProperties) server.address=::1 diff --git a/prototype/src/main/resources/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/static/css/ecom.css b/prototype/src/main/resources/static/css/ecom.css index 1d8f125..2a696d2 100644 --- a/prototype/src/main/resources/static/css/ecom.css +++ b/prototype/src/main/resources/static/css/ecom.css @@ -278,7 +278,7 @@ img.s { } img.m { - width: var(--u8); + width: 20rem; } /* diff --git a/prototype/src/main/resources/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/static/img/product-1.jpg b/prototype/src/main/resources/static/img/product-1.jpg deleted file mode 100644 index 8ea7598..0000000 Binary files a/prototype/src/main/resources/static/img/product-1.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/img/product-2.jpg b/prototype/src/main/resources/static/img/product-2.jpg deleted file mode 100644 index 427b2f0..0000000 Binary files a/prototype/src/main/resources/static/img/product-2.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/img/product-3.jpg b/prototype/src/main/resources/static/img/product-3.jpg deleted file mode 100644 index dbc1b2e..0000000 Binary files a/prototype/src/main/resources/static/img/product-3.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/img/product-4.jpg b/prototype/src/main/resources/static/img/product-4.jpg deleted file mode 100644 index 8f2d857..0000000 Binary files a/prototype/src/main/resources/static/img/product-4.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/img/product-5.jpg b/prototype/src/main/resources/static/img/product-5.jpg deleted file mode 100644 index 5e0a406..0000000 Binary files a/prototype/src/main/resources/static/img/product-5.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/img/product-6.jpg b/prototype/src/main/resources/static/img/product-6.jpg deleted file mode 100644 index 05d78c0..0000000 Binary files a/prototype/src/main/resources/static/img/product-6.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/img/product-7.jpg b/prototype/src/main/resources/static/img/product-7.jpg deleted file mode 100644 index 220140c..0000000 Binary files a/prototype/src/main/resources/static/img/product-7.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/img/product-8.jpg b/prototype/src/main/resources/static/img/product-8.jpg deleted file mode 100644 index cdb829d..0000000 Binary files a/prototype/src/main/resources/static/img/product-8.jpg and /dev/null differ diff --git a/prototype/src/main/resources/static/js/editViewUpdateTax.js b/prototype/src/main/resources/static/js/editViewUpdateTax.js new file mode 100644 index 0000000..0724150 --- /dev/null +++ b/prototype/src/main/resources/static/js/editViewUpdateTax.js @@ -0,0 +1,15 @@ +document.addEventListener("DOMContentLoaded", function() { + let priceElm = document.getElementById("price"); + let priceGrossElm = document.getElementById("priceGross"); + let vatPercent = parseInt(document.getElementById("vatPercent").value); + + let updateFn = () => { + let net = Math.floor(priceElm.value*100); + let vat = Math.floor((net * vatPercent) / 100); + let gross = net + vat; + + priceGrossElm.innerText = (gross / 100.0).toFixed(2).replace("\\.", ","); + }; + + priceElm.onchange = updateFn; +}); 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.

-
diff --git a/prototype/src/main/resources/templates/fragments/customer.html b/prototype/src/main/resources/templates/fragments/customer.html index d47a06a..f8ba1ff 100644 --- a/prototype/src/main/resources/templates/fragments/customer.html +++ b/prototype/src/main/resources/templates/fragments/customer.html @@ -12,8 +12,6 @@ diff --git a/prototype/src/main/resources/templates/fragments/header.html b/prototype/src/main/resources/templates/fragments/header.html index b0b52bb..349f72c 100644 --- a/prototype/src/main/resources/templates/fragments/header.html +++ b/prototype/src/main/resources/templates/fragments/header.html @@ -11,14 +11,13 @@
@@ -38,7 +37,6 @@ function toggle(id) { document.getElementById(id).classList.toggle("invisible"); } - X
@@ -54,7 +52,6 @@ function toggle(id) { document.getElementById(id).classList.toggle("invisible"); } - X
@@ -62,5 +59,4 @@ - diff --git a/prototype/src/main/resources/templates/fragments/intern.html b/prototype/src/main/resources/templates/fragments/intern.html index 79d5f7e..22de534 100644 --- a/prototype/src/main/resources/templates/fragments/intern.html +++ b/prototype/src/main/resources/templates/fragments/intern.html @@ -13,11 +13,7 @@
  • Dashboard
  • -
  • Gelistete Artikel - +
  • Gelistete Artikel
  • Alle Buchungen
      @@ -38,7 +34,7 @@
    • Lieferanten
    • diff --git a/prototype/src/main/resources/templates/fragments/shop.html b/prototype/src/main/resources/templates/fragments/shop.html index 7a87073..d6dea6a 100644 --- a/prototype/src/main/resources/templates/fragments/shop.html +++ b/prototype/src/main/resources/templates/fragments/shop.html @@ -7,15 +7,10 @@ -