Merge remote-tracking branch 'origin/master' into feature/config
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -89,3 +89,7 @@ local.properties | ||||
|  | ||||
| # SQLite | ||||
| prototype/*.db | ||||
|  | ||||
| prototype/images | ||||
| prototype/data | ||||
|  | ||||
|  | ||||
							
								
								
									
										32
									
								
								delivery/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								delivery/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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/ | ||||
							
								
								
									
										30
									
								
								delivery/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								delivery/build.gradle
									
									
									
									
									
										Normal file
									
								
							| @ -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() | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								delivery/gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								delivery/gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6
									
								
								delivery/gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								delivery/gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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 | ||||
							
								
								
									
										183
									
								
								delivery/gradlew
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								delivery/gradlew
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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" "$@" | ||||
							
								
								
									
										100
									
								
								delivery/gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								delivery/gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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 | ||||
							
								
								
									
										1
									
								
								delivery/settings.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								delivery/settings.gradle
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| rootProject.name = 'delivery' | ||||
							
								
								
									
										11
									
								
								delivery/src/main/java/org/hso/ecommerce/supplier/App.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								delivery/src/main/java/org/hso/ecommerce/supplier/App.java
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
|     } | ||||
| } | ||||
| @ -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()); | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
| @ -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<Delivery> 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(); | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										2
									
								
								delivery/src/main/resources/application.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								delivery/src/main/resources/application.properties
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| server.address=::1 | ||||
| server.port=8082 | ||||
							
								
								
									
										2
									
								
								prototype/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								prototype/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| ./test.db | ||||
| e-commerce.db | ||||
| ./build | ||||
| ./gradle | ||||
| ./out | ||||
|  | ||||
| @ -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"] | ||||
| } | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								prototype/gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								prototype/gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -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 | ||||
|  | ||||
							
								
								
									
										22
									
								
								prototype/gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								prototype/gradlew
									
									
									
									
										vendored
									
									
								
							| @ -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"` | ||||
|  | ||||
							
								
								
									
										18
									
								
								prototype/gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								prototype/gradlew.bat
									
									
									
									
										vendored
									
									
								
							| @ -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 | ||||
|  | ||||
							
								
								
									
										15
									
								
								prototype/scripts/addarticles.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								prototype/scripts/addarticles.sql
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
|  | ||||
							
								
								
									
										6
									
								
								prototype/scripts/addsupplier.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								prototype/scripts/addsupplier.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
|  | ||||
| INSERT INTO suppliers ("api_url", "name", "uuid") | ||||
| VALUES ("https://api.com", "Conrad", "fdfdfg4gdfgdf4gfg"); | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										3
									
								
								prototype/scripts/addsupplierorders.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								prototype/scripts/addsupplierorders.sql
									
									
									
									
									
										Normal file
									
								
							| @ -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'); | ||||
| @ -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"); | ||||
| 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"); | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
| @ -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<org.hso.ecommerce.entities.supplier.Supplier> 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<org.hso.ecommerce.entities.supplier.Supplier> 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<Supplier> supplierData; | ||||
|         public final HashMap<ArticleIdentifier, Offer> cheapestOffer; | ||||
|  | ||||
|         public Result(ArrayList<Supplier> supplierData, HashMap<ArticleIdentifier, Offer> cheapestOffer) { | ||||
|             this.supplierData = supplierData; | ||||
|             this.cheapestOffer = cheapestOffer; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Result finish() { | ||||
|         ArrayList<Supplier> suppliers = new ArrayList<>(); | ||||
|         HashMap<ArticleIdentifier, Integer> price = new HashMap<>(); | ||||
|         HashMap<ArticleIdentifier, Offer> 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); | ||||
|     } | ||||
| } | ||||
| @ -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<ArticleIdentifier, Offer> cheapestOffer; | ||||
|     private HashMap<ArticleIdentifier, ArticleOffer> articleOffers; | ||||
|  | ||||
|     public ReorderAction( | ||||
|             Article article, Integer[] orderedAmounts, | ||||
|             Integer undeliveredReorders, | ||||
|             int amountInStock, | ||||
|             HashMap<ArticleIdentifier, Offer> cheapestOffer, | ||||
|             HashMap<ArticleIdentifier, ArticleOffer> 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; | ||||
|     } | ||||
| } | ||||
| @ -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<ArticleOffer> offers; | ||||
|     private HashMap<ArticleIdentifier, Offer> cheapestOffer; | ||||
|  | ||||
|     public UpdateOffersAction(List<ArticleOffer> offers, HashMap<ArticleIdentifier, Offer> cheapestOffer) { | ||||
|         this.offers = offers; | ||||
|         this.cheapestOffer = cheapestOffer; | ||||
|     } | ||||
|  | ||||
|     private HashMap<ArticleIdentifier, ArticleOffer> mapOffers() { | ||||
|         HashMap<ArticleIdentifier, ArticleOffer> map = new HashMap<>(); | ||||
|         for (ArticleOffer offer : offers) { | ||||
|             ArticleIdentifier identifier = new ArticleIdentifier(offer.manufacturer, offer.articleNumber); | ||||
|             map.put(identifier, offer); | ||||
|         } | ||||
|         return map; | ||||
|     } | ||||
|  | ||||
|     public List<ArticleOffer> finish() { | ||||
|         HashMap<ArticleIdentifier, ArticleOffer> 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<ArticleIdentifier, Offer> 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; | ||||
|     } | ||||
| } | ||||
| @ -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<OrderItem> 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<WarehouseBookingPositionSlotEntry> 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<Booking> 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<Booking> bookings; | ||||
|  | ||||
|         Result(CustomerOrder customerOrder, WarehouseBooking warehouseBooking, List<Booking> bookings) { | ||||
|             this.customerOrder = customerOrder; | ||||
|             this.warehouseBooking = warehouseBooking; | ||||
|             this.bookings = bookings; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static class OrderItem { | ||||
|         List<WarehouseBookingPositionSlotEntry> availableSlots; | ||||
|         int quantity; | ||||
|         Article article; | ||||
|  | ||||
|         public OrderItem(Article article, List<WarehouseBookingPositionSlotEntry> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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"; | ||||
|     } | ||||
| } | ||||
| @ -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<Article> getRandomArticles(int quantity, List<Article> advertisedArticles) { | ||||
|         List<Article> randomisedArticles = new ArrayList<Article>(); | ||||
|         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; | ||||
|     } | ||||
| } | ||||
| @ -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<Article> searchByTerm(String sourceTerm, ArticleRepository repository) { | ||||
|  | ||||
|         List<String> terms = Arrays.asList(sourceTerm.split(" ")); | ||||
|         List<Article> resultArticles = new ArrayList<>(); | ||||
|  | ||||
|         terms.forEach(term -> { | ||||
|             List<Article> titleArticles = repository.getArticlesByTermInTitle(term); //search in Title | ||||
|             titleArticles.forEach(article -> { | ||||
|                 if(!resultArticles.contains(article)){ | ||||
|                     resultArticles.add(article); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|  | ||||
|         terms.forEach(term -> { | ||||
|             List<Article> descArticles = repository.getArticlesByTermInDescription(term); //search by Term | ||||
|             descArticles.forEach(article -> { | ||||
|                 if(!resultArticles.contains(article)){ | ||||
|                     resultArticles.add(article); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|  | ||||
|         return resultArticles; | ||||
|     } | ||||
| } | ||||
| @ -1,7 +0,0 @@ | ||||
| package org.hso.ecommerce.action.somepackage; | ||||
|  | ||||
| public class DemoAction { | ||||
|     // TODO: remove me. | ||||
|     // mksubpackage | ||||
|  | ||||
| } | ||||
| @ -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 = ""; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<WarehouseBookingPositionSlotEntry> entryList; | ||||
|  | ||||
|     public CalculateWarehouseStatsAction(List<WarehouseBookingPositionSlotEntry> 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<Long> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<WarehouseBookingPositionSlotEntry> source; | ||||
|     private Optional<WarehouseBookingPositionSlotEntry> destination; | ||||
|     private String reason; | ||||
|  | ||||
|     public CreateManuelBookingAction(Article article, int amount, Optional<WarehouseBookingPositionSlotEntry> source, Optional<WarehouseBookingPositionSlotEntry> 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."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<WarehouseBookingPositionSlotEntry> warehouseCandidates; | ||||
|     private final SupplierOrder order; | ||||
|  | ||||
|     public SupplierOrderArrivedAction(List<WarehouseBookingPositionSlotEntry> 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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
| @ -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<Article> 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; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,6 @@ | ||||
| package org.hso.ecommerce.api.data; | ||||
|  | ||||
| public class SupplierDiscount { | ||||
|     public int minimumDailySalesVolumeNetCent; | ||||
|     public int percentDiscount; | ||||
| } | ||||
| @ -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()); | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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> 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"; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -24,6 +24,7 @@ public class LoginIntercepter implements HandlerInterceptor { | ||||
|  | ||||
|         HttpSession session = request.getSession(); | ||||
|         Object userId = session.getAttribute("userId"); | ||||
|         Optional<User> 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> user = userRepository.findById((Long) userId); | ||||
|             if (user == null) | ||||
|                 user = userRepository.findById((Long) userId); | ||||
|             user.ifPresent(value -> request.setAttribute("user", value)); | ||||
|         } | ||||
|  | ||||
|  | ||||
| @ -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 { | ||||
|     } | ||||
| } | ||||
| @ -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"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,8 +0,0 @@ | ||||
| package org.hso.ecommerce.controller; | ||||
|  | ||||
| import org.springframework.stereotype.Controller; | ||||
|  | ||||
| @Controller | ||||
| //@RequestMapping("...") | ||||
| public class BookingController { | ||||
| } | ||||
| @ -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> 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:/"; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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> 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"; | ||||
| 	} | ||||
| } | ||||
| @ -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<CustomerOrder> 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"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<ArticleIdentifier, ArticleOffer> mapArticleOffers(List<ArticleOffer> articleOffers) { | ||||
|         HashMap<ArticleIdentifier, ArticleOffer> 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<Supplier> suppliers = controller.supplierRepository.findAll(); | ||||
|         ReadSupplierDataAction.Result supplierData = new ReadSupplierDataAction(suppliers).finish(); | ||||
|  | ||||
|         // Save the new offers in the database | ||||
|         List<ArticleOffer> allOffers = controller.articleOfferRepository.findAll(); | ||||
|         allOffers = new UpdateOffersAction(allOffers, supplierData.cheapestOffer).finish(); | ||||
|         controller.articleOfferRepository.saveAll(allOffers); | ||||
|  | ||||
|         HashMap<ArticleIdentifier, ArticleOffer> mappedOffers = mapArticleOffers(allOffers); | ||||
|  | ||||
|         // Reorder | ||||
|         List<Article> 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<String, ICronjob> 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<String, ICronjob> getCronjobs() { | ||||
|         HashMap<String, ICronjob> 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<BackgroundJob> jobs = cronjobRepository.findAll(); | ||||
|         HashMap<String, BackgroundJob> alreadyExecuted = new HashMap<>(); | ||||
|         for (BackgroundJob job : jobs) { | ||||
|             alreadyExecuted.put(job.jobName, job); | ||||
|         } | ||||
|         ScheduledCronjob earliestJob = null; | ||||
|         for (Entry<String, ICronjob> 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(); | ||||
|     } | ||||
| } | ||||
| @ -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<UImodelArticles> totals = new ArrayList<UImodelArticles>(); | ||||
|  | ||||
| 		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<Integer> 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<Integer> 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; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -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<BookingAccountEntry> mainAccount = bookingAccountEntryRepository.getByMain(); | ||||
|         int mainAccountBalance = mainAccount.map(entry -> entry.newSumCent).orElse(0); | ||||
|         Optional<BookingAccountEntry> 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"; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -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"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<ShortTemplateBooking> bookings; | ||||
|  | ||||
|         public ShortTemplateBookingResult(String balance, List<ShortTemplateBooking> 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<Booking> bookings, AccountFilter currentAccount) { | ||||
|         List<ShortTemplateBooking> 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<Booking> bookings = bookingRepository.allBookingsReverseChronologically(); | ||||
|         List<TemplateBooking> 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<Booking> 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<Booking> bookings = bookingRepository.mainBookingsReverseChronologically(); | ||||
|         ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isMainAccount); | ||||
|         model.addAttribute("balance", result.balance); | ||||
|         model.addAttribute("bookings", result.bookings); | ||||
|         return "intern/accounting/main"; | ||||
|     } | ||||
| } | ||||
| @ -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<BookingAccountEntry> 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<BookingAccountEntry> 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"; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -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<User> 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<User> 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<CustomerOrder> orders = customerOrderRepository.getOrdersByUserId(id); | ||||
|         model.addAttribute("orders", orders); | ||||
|  | ||||
|         List<Booking> 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"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<UImodelSuppliers> totals = new ArrayList<UImodelSuppliers>(); | ||||
|  | ||||
| 		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<UImodelSupplierDetailOrders> orders = new ArrayList<UImodelSupplierDetailOrders>(); | ||||
| 		for (SupplierOrder supplierOrder : supplierOrderRepository.findOrderBySupplierID(supplierId)) { | ||||
| 			orders.add(new UImodelSupplierDetailOrders(supplierOrder)); | ||||
| 		} | ||||
|  | ||||
| 		// Table of bookings | ||||
| 		List<Booking> 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<UImodelSupplierDetailOrders> orders; | ||||
| 		public List<ShortTemplateBooking> bookings; | ||||
|  | ||||
| 		public UImodelSupplierDetail(String name, String balance, List<UImodelSupplierDetailOrders> orders, | ||||
| 				List<ShortTemplateBooking> 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; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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<UImodelOfferedArticle> 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<Integer> 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; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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<UImodelSupplierOrder> totals = new ArrayList<UImodelSupplierOrder>(); | ||||
|  | ||||
| 		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<WarehouseBookingPositionSlotEntry> 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; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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<Article> 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<WarehouseBookingPositionSlotEntry> 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<WarehouseBookingPositionSlotEntry> destinationSlot = Optional.empty(); | ||||
|         if (destinationIsSlot == true) { | ||||
|             Optional<Slot> 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"; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -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<WarehouseBookingPositionSlotEntry> 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"; | ||||
|     } | ||||
| } | ||||
| @ -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<WarehouseBooking> 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<WarehouseBooking> 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<WarehouseBooking> 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"; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @ -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<Article> 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."); | ||||
|             } | ||||
|         }         | ||||
|     } | ||||
| } | ||||
| @ -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<CheckoutListItem> 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<Integer, Integer> 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"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<Article> 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<Article> 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"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<Article> articles = SearchByTermAction.searchByTerm(term, articleRepository); | ||||
|             model.addAttribute("articles", articles); | ||||
|         } else if (category != null) {  //if search by Category | ||||
|             List<Article> 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"; | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<Category> categories = new HashSet<>(); | ||||
| } | ||||
|  | ||||
|     public int getVat() { | ||||
|         return (shopPricePerUnitNetCent * related.vatPercent) / 100; | ||||
|     } | ||||
|  | ||||
|     public int getPriceGross() { | ||||
|         return shopPricePerUnitNetCent + getVat(); | ||||
|     } | ||||
| } | ||||
| @ -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<Article> articles = new HashSet<>(); | ||||
|      | ||||
|      | ||||
|     public Category() { | ||||
|     	 | ||||
|     } | ||||
|      | ||||
|      | ||||
|     public Category (String name) { | ||||
|     	this.name = name; | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -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<CustomerOrderPosition> 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"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -19,4 +19,8 @@ public class CustomerOrderPosition { | ||||
|  | ||||
|     public int pricePerUnit; | ||||
|     public int quantity; | ||||
|  | ||||
|     public int getSumPrice(){ | ||||
|         return article.getPriceGross() * quantity; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| @ -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<ShoppingCartItem> items; | ||||
|  | ||||
|     public ShoppingCart() { | ||||
|         clear(); | ||||
|     } | ||||
|  | ||||
|     public void clear() { | ||||
|         items = new ArrayList<>(); | ||||
|         revision = (int) Math.round(Math.random() * 0xFFFF); | ||||
|     } | ||||
|  | ||||
|     public List<ShoppingCartItem> 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; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
| @ -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<WarehouseBookingPosition> positions = new ArrayList<>(); | ||||
|  | ||||
|     // TODO FIX ME | ||||
|     @OneToOne(optional = false, cascade = CascadeType.ALL) | ||||
|     public BookingReason reason; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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<BookingAccountEntry, Long> { | ||||
|  | ||||
|     @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<BookingAccountEntry> 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<BookingAccountEntry> 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<BookingAccountEntry> getByMain(); | ||||
|  | ||||
|     @Query(value = "SELECT * FROM booking_account_entries as e WHERE e.isvataccount = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true) | ||||
|     Optional<BookingAccountEntry> getByVat(); | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -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<Booking, Long> { | ||||
|  | ||||
|     @Query("SELECT b FROM Booking b ORDER BY b.id DESC") | ||||
|     List<Booking> 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<Booking> 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<Booking> 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<Booking> 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<Booking> supplierBookingsReverseChronologically(long supplierId); | ||||
|  | ||||
| } | ||||
| @ -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<BackgroundJob, Long> { | ||||
|  | ||||
| } | ||||
| @ -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<Article, Long> { | ||||
|  | ||||
|     /*** | ||||
|      * 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<Article> 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<Article> 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<Article> 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<Article> 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<Integer> findArticleIDByRelatedID(@Param("relatedId") long relatedId); | ||||
|  | ||||
|     @Query(value = "SELECT a FROM Article a Where a.related = :related") | ||||
|     Optional<Article> 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<Article> 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<Article> 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<Article> getArticlesByCategory(String category); | ||||
| } | ||||
| @ -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<Category, Long> { | ||||
|  | ||||
| 	@Query("SELECT a FROM Category a WHERE a.name = :name") | ||||
| 	Optional<Category> findCategoryByName(@Param("name") String name); | ||||
|  | ||||
|     @Query("SELECT c FROM Category c") | ||||
|     List<Category> getCategories(); | ||||
| } | ||||
| @ -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<CustomerOrder, Long> { | ||||
|  | ||||
|     @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<CustomerOrder> getOrdersByUserId(long userId); | ||||
|  | ||||
| } | ||||
| @ -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<Image, Long> { | ||||
|  | ||||
| 	@Query("SELECT i.id FROM Image i WHERE i.path = :path") | ||||
| 	Optional<Integer> findImageIDByPath(@Param("path") String path); | ||||
|  | ||||
| 	@Query("SELECT i FROM Image i WHERE i.id = :imageId") | ||||
| 	Image findImageById(@Param("imageId") long imageId); | ||||
|  | ||||
| } | ||||
| @ -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<ArticleOffer, Long> { | ||||
|  | ||||
| 	@Query("SELECT a FROM ArticleOffer a") | ||||
| 	List<ArticleOffer> findAll(); | ||||
| 	 | ||||
| 	@Query("SELECT a FROM ArticleOffer a WHERE a.id = :offeredarticleId") | ||||
| 	ArticleOffer findOfferedArticleById(@Param("offeredarticleId") long offeredarticleId); | ||||
|  | ||||
| } | ||||
| @ -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<ArticleOffer, Long> { | ||||
|  | ||||
| } | ||||
| @ -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<SupplierOrder, Long> { | ||||
|  | ||||
|     @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<SupplierOrder> findOrderBySupplierID(@Param("supplierId") long supplierId); | ||||
|      | ||||
| 	@Query("SELECT a FROM SupplierOrder a") | ||||
| 	List<SupplierOrder> findAll(); | ||||
| } | ||||
| @ -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<Supplier, Long> { | ||||
| 	 | ||||
| 	@Query("SELECT a FROM Supplier a") | ||||
| 	List<Supplier> findAll(); | ||||
| 	 | ||||
| 	@Query("SELECT a FROM Supplier a WHERE a.id = :supplierId") | ||||
| 	Supplier findSupplierById(@Param("supplierId") long supplierId); | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user