Merge branch 'master' into feature/user
This commit is contained in:
commit
dfa528484c
|
@ -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/
|
|
@ -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()
|
||||||
|
}
|
Binary file not shown.
|
@ -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
|
|
@ -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" "$@"
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = 'delivery'
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
server.address=::1
|
||||||
|
server.port=8082
|
|
@ -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,59 @@
|
||||||
|
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 first. They are set again below.
|
||||||
|
for (ArticleOffer offer : availableOffers.values()) {
|
||||||
|
offer.shouldBeAdvertised = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.vatPercent = currentOfferedArticle.vatPercent;
|
||||||
|
|
||||||
|
// Set advertise-flag if any supplier wants it to be set
|
||||||
|
if (currentOfferedArticle.shouldBeAdvertised) {
|
||||||
|
currentOffer.shouldBeAdvertised = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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.CustomerOderRepository;
|
||||||
|
import org.hso.ecommerce.repos.supplier.ArticleOfferRepository;
|
||||||
|
import org.hso.ecommerce.repos.supplier.SupplierOrderRepository;
|
||||||
|
import org.hso.ecommerce.repos.supplier.SupplierRepository;
|
||||||
|
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
interface ICronjob {
|
||||||
|
/**
|
||||||
|
* Calculate the earliest cronjob execution time that happens after the given reference time.
|
||||||
|
*
|
||||||
|
* @param reference Position in time to start searching. The implementor is allowed to modify the reference time.
|
||||||
|
* @return A new Calendar instance (or the same) containing the time for next execution.
|
||||||
|
*/
|
||||||
|
Calendar nextExecution(Calendar reference);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the latest cronjob execution time that happens before or exactly at the given refernce time.
|
||||||
|
*
|
||||||
|
* @param reference Position in time to start searching. The implementor is allowed to modify the reference time.
|
||||||
|
* @return A new Calendar instance (or the same) containing the time of the last execution.
|
||||||
|
*/
|
||||||
|
Calendar previousExecution(Calendar reference);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute this cronjob.
|
||||||
|
*
|
||||||
|
* @param time The point in time this execution was scheduled. In case of a missed cronjob, the actual time of
|
||||||
|
* this call might be much later.
|
||||||
|
* @param controller Back-reference that allows to use repositories.
|
||||||
|
*/
|
||||||
|
void executeAt(Calendar time, CronjobController controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class Reorder implements ICronjob {
|
||||||
|
@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 CustomerOderRepository customerOrderRepository = null;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
final BookingRepository bookingRepository = null;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
final BookingAccountEntryRepository bookingAccountEntryRepository = null;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
final SupplierRepository supplierRepository = null;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
final SupplierOrderRepository supplierOrderRepository = null;
|
||||||
|
|
||||||
|
private static Map<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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,7 +83,7 @@ public class InternArticleController {
|
||||||
@RequestParam(value = "price_netto", required = true) String pricenetto,
|
@RequestParam(value = "price_netto", required = true) String pricenetto,
|
||||||
@RequestParam(value = "reorderMaxPrice", required = true) String reorderMaxPrice,
|
@RequestParam(value = "reorderMaxPrice", required = true) String reorderMaxPrice,
|
||||||
@RequestParam(value = "autobuy", required = true) Boolean shouldReorder,
|
@RequestParam(value = "autobuy", required = true) Boolean shouldReorder,
|
||||||
@RequestParam(value = "categories", required = true) String categories,
|
@RequestParam(value = "categorie", required = true) String categories,
|
||||||
@RequestParam(value = "img", required = true) MultipartFile imgFile) {
|
@RequestParam(value = "img", required = true) MultipartFile imgFile) {
|
||||||
|
|
||||||
Article tmpArticle = articleRepository.findArticleById(id); // get the old article
|
Article tmpArticle = articleRepository.findArticleById(id); // get the old article
|
||||||
|
@ -93,7 +93,7 @@ public class InternArticleController {
|
||||||
tmpArticle.categories.clear();
|
tmpArticle.categories.clear();
|
||||||
|
|
||||||
// loop through all categories strings and create a new category if a new one;
|
// loop through all categories strings and create a new category if a new one;
|
||||||
// also adds the categorys to the article
|
// also adds the categories to the article
|
||||||
for (String category : separatedCategories) {
|
for (String category : separatedCategories) {
|
||||||
tmpArticle.categories.add(categoryRepository.findCategoryByName(category.trim())
|
tmpArticle.categories.add(categoryRepository.findCategoryByName(category.trim())
|
||||||
.orElseGet(() -> new Category(category.trim())));
|
.orElseGet(() -> new Category(category.trim())));
|
||||||
|
|
|
@ -16,10 +16,10 @@ public class BookingAccountEntry {
|
||||||
|
|
||||||
public int newSumCent;
|
public int newSumCent;
|
||||||
|
|
||||||
@ManyToOne(optional = true, cascade = CascadeType.ALL)
|
@ManyToOne(optional = true, cascade = CascadeType.MERGE)
|
||||||
public User userAccount;
|
public User userAccount;
|
||||||
|
|
||||||
@ManyToOne(optional = true, cascade = CascadeType.ALL)
|
@ManyToOne(optional = true, cascade = CascadeType.MERGE)
|
||||||
public Supplier supplierAccount;
|
public Supplier supplierAccount;
|
||||||
|
|
||||||
public boolean isMainAccount;
|
public boolean isMainAccount;
|
||||||
|
@ -46,6 +46,14 @@ public class BookingAccountEntry {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BookingAccountEntry newSupplier(Supplier supplier) {
|
||||||
|
BookingAccountEntry e = new BookingAccountEntry();
|
||||||
|
e.supplierAccount = supplier;
|
||||||
|
e.newSumCent = 0;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
public static BookingAccountEntry newMain() {
|
public static BookingAccountEntry newMain() {
|
||||||
BookingAccountEntry e = new BookingAccountEntry();
|
BookingAccountEntry e = new BookingAccountEntry();
|
||||||
e.isMainAccount = true;
|
e.isMainAccount = true;
|
||||||
|
|
|
@ -39,4 +39,8 @@ public class BookingReason {
|
||||||
public BookingReason(CustomerPayment customerPayment) {
|
public BookingReason(CustomerPayment customerPayment) {
|
||||||
this.customerPayment = customerPayment;
|
this.customerPayment = customerPayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BookingReason(SupplierOrder supplierOrder) {
|
||||||
|
this.supplierOrder = supplierOrder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import javax.validation.constraints.NotNull;
|
||||||
@Table(name = "background_jobs")
|
@Table(name = "background_jobs")
|
||||||
public class BackgroundJob {
|
public class BackgroundJob {
|
||||||
|
|
||||||
public final String JOB_DASHBOARD = "Dashboard";
|
public static final String JOB_DASHBOARD = "Dashboard";
|
||||||
public final String JOB_REORDER = "SupplierOrder";
|
public static final String JOB_REORDER = "SupplierOrder";
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
package org.hso.ecommerce.repos.booking;
|
package org.hso.ecommerce.repos.booking;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
|
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface BookingAccountEntryRepository extends JpaRepository<BookingAccountEntry, Long> {
|
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)
|
@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);
|
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)
|
@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();
|
Optional<BookingAccountEntry> getByMain();
|
||||||
|
|
||||||
|
|
|
@ -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,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,14 @@
|
||||||
|
package org.hso.ecommerce.repos.supplier;
|
||||||
|
|
||||||
|
import org.hso.ecommerce.entities.supplier.SupplierOrder;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface SupplierOrderRepository extends JpaRepository<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);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.hso.ecommerce.repos.supplier;
|
||||||
|
|
||||||
|
import org.hso.ecommerce.entities.supplier.Supplier;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface SupplierRepository extends JpaRepository<Supplier, Long> {
|
||||||
|
|
||||||
|
}
|
|
@ -24,21 +24,21 @@
|
||||||
<form class="detailgrid" action="#" th:action="@{/intern/articles/{id}/saveChanges(id = ${ArticleID.id})}" th:object="${ArticleID}" method="POST" enctype="multipart/form-data">
|
<form class="detailgrid" action="#" th:action="@{/intern/articles/{id}/saveChanges(id = ${ArticleID.id})}" th:object="${ArticleID}" method="POST" enctype="multipart/form-data">
|
||||||
<p class="m">
|
<p class="m">
|
||||||
<label for="title">Titel</label>
|
<label for="title">Titel</label>
|
||||||
<input class=" full-width" type="text" id="title" name="title" required="required" pattern="[A-Za-z0-9]{1,20}" th:value="${ArticleID.title}"/>
|
<input class=" full-width" type="text" id="title" name="title" required="required" pattern=".+" th:value="${ArticleID.title}"/>
|
||||||
</p>
|
</p>
|
||||||
<p class="s">
|
<p class="s">
|
||||||
<label for="ref-article">Refernzierter Artikel</label>
|
<label for="ref_disabled">Refernzierter Artikel</label>
|
||||||
<input class="" type="text" id="ref_disabled" th:value="${ArticleID.offer_id}" disabled/>
|
<input class="" type="text" id="ref_disabled" th:value="${ArticleID.offer_id}" disabled/>
|
||||||
|
|
||||||
<input type="hidden" id="ref_hidden" th:value="${ArticleID.offer_id}" name="ref-article" />
|
<input type="hidden" id="ref_hidden" th:value="${ArticleID.offer_id}" name="ref-article" />
|
||||||
|
|
||||||
<td><a th:href="${'/intern/supplierOffers/#q=' + {ArticleID.id}}">Details</a></td>
|
<a th:href="${'/intern/supplierOffers/#q=' + ArticleID.id}">Details</a>
|
||||||
</p>
|
</p>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<div class="m">
|
<div class="m">
|
||||||
<p>
|
<p>
|
||||||
<label for="img">Bild Hochladen</label>
|
<label for="img">Bild Hochladen</label>
|
||||||
<input class="full-width" type="file" id="image" name="img"/>
|
<input class="full-width" type="file" id="img" name="img"/>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<img th:src="@{/shop/articles/{id}/image.jpg(id=${ArticleID.id})}" class="m"/>
|
<img th:src="@{/shop/articles/{id}/image.jpg(id=${ArticleID.id})}" class="m"/>
|
||||||
|
@ -47,14 +47,14 @@
|
||||||
<div class="s">
|
<div class="s">
|
||||||
<p>
|
<p>
|
||||||
<label for="price">Preis (Netto)</label>
|
<label for="price">Preis (Netto)</label>
|
||||||
<input class="" type="number" step="0.01" name="price_netto" th:value="${ArticleID.price_netto}"/> EUR <br/>
|
<input class="" type="number" id="price" step="0.01" name="price_netto" required th:value="${ArticleID.price_netto}"/> EUR <br/>
|
||||||
(19% Mwst.)
|
(19% Mwst.)
|
||||||
<!-- Info von article ref--> <br/>
|
<!-- Info von article ref--> <br/>
|
||||||
= <span th:text="${ArticleID.price}"></span> EUR Brutto
|
= <span th:text="${ArticleID.price}"></span> EUR Brutto
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<label for="max-price-buy">Maximaler Einkaufspreis (Netto)</label>
|
<label for="reorderMaxPrice">Maximaler Einkaufspreis (Netto)</label>
|
||||||
<input class="" type="number" id="reorderMaxPrice" step="0.01" name="reorderMaxPrice" th:value="${ArticleID.reorderMaxPrice}"/> EUR
|
<input class="" type="number" id="reorderMaxPrice" step="0.01" required name="reorderMaxPrice" th:value="${ArticleID.reorderMaxPrice}"/> EUR
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
@ -66,18 +66,18 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="m">
|
<div class="m">
|
||||||
<label for="tags">Kategorien</label>
|
<label for="categorie">Kategorien</label>
|
||||||
<p>
|
<p>
|
||||||
Bitte jede Kategorien in eine eigene Zeile
|
Bitte jede Kategorien in eine eigene Zeile
|
||||||
</p>
|
</p>
|
||||||
<textarea name="categories" id="categories" class="full-width" rows="6"th:inline="text">[[${ArticleID.categorie}]]
|
<textarea name="categorie" id="categorie" class="full-width" required rows="6" th:inline="text" th:field="${ArticleID.categorie}">
|
||||||
|
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="s">
|
<div class="s">
|
||||||
<p>
|
<p>
|
||||||
<label for="price">Einheiten pro Lagerplatz</label>
|
<label for="units-per-slot">Einheiten pro Lagerplatz</label>
|
||||||
<input class="" type="number" id="units-per-slot" name="units-per-slot" th:value="${ArticleID.warehouseUnitsPerSlot}"/>
|
<input class="" type="number" id="units-per-slot" required name="units-per-slot" th:value="${ArticleID.warehouseUnitsPerSlot}"/>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Lagerbestand: <span th:text="${ArticleID.stock}"></span></b>
|
<b>Lagerbestand: <span th:text="${ArticleID.stock}"></span></b>
|
||||||
|
@ -88,12 +88,12 @@
|
||||||
<!-- TODO: set link g-->
|
<!-- TODO: set link g-->
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="/todo" class="button smaller">Lagerbuchung</a>
|
<a href="/intern/warehouse/" class="button smaller">Lagerbuchung</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="l">
|
<p class="l">
|
||||||
<label for="description">Beschreibung</label>
|
<label for="description">Beschreibung</label>
|
||||||
<textarea name="description" id="description" class="full-width" rows="15" th:inline="text">[[${ArticleID.description}]]
|
<textarea name="description" id="description" class="full-width" required th:field="${ArticleID.description}" rows="15" th:inline="text">
|
||||||
</textarea>
|
</textarea>
|
||||||
</p>
|
</p>
|
||||||
<div class="l">
|
<div class="l">
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="9">
|
<th colspan="9">
|
||||||
<input type="text" placeholder="Filtern" class="smaller jsFilterTable full-width"
|
<input type="text" placeholder="Filtern" class="smaller jsFilterTable full-width"
|
||||||
data-target-id="main-table"></input>
|
data-target-id="main-table"/>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -53,10 +53,10 @@
|
||||||
<td><span th:text="${article.price_netto}"></span> €</td>
|
<td><span th:text="${article.price_netto}"></span> €</td>
|
||||||
<td><span th:text="${article.categorie}"></span></td>
|
<td><span th:text="${article.categorie}"></span></td>
|
||||||
<td><span th:text="${article.stock}"></span></td>
|
<td><span th:text="${article.stock}"></span></td>
|
||||||
<td><a th:href="${'/intern/supplierOffers/#q=' + {article.title}}" th:text="${article.offer_id}"></a></td>
|
<td><a th:href="${'/intern/supplierOffers/#q=' + article.title}" th:text="${article.offer_id}"></a></td>
|
||||||
<td><a th:href="@{/intern/articles/{id}(id = ${article.id})}" th:text="${article.id}"></a></td>
|
<td><a th:href="@{/intern/articles/{id}(id = ${article.id})}" th:text="${article.id}"></a></td>
|
||||||
<td>
|
<td>
|
||||||
<form th:action="@{/intern/articles/{id}(id = ${article.id})}"><input type="submit" value="Bearbeiten" /></form>
|
<form th:action="@{/intern/articles/{id}(id = ${article.id})}"><input class="button smaller" type="submit" value="Bearbeiten" /></form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
<!-- ELSE -->
|
<!-- ELSE -->
|
||||||
<div th:unless="${article.offerIsListed}">
|
<div th:unless="${article.offerIsListed}">
|
||||||
<form class="detailgrid" action="#" th:action="@{/intern/articles/addArticle/{id}(id = ${article.offer_id})}" method="POST">
|
<form class="detailgrid" action="#" th:action="@{/intern/articles/addArticle/{id}(id = ${article.offer_id})}" method="POST">
|
||||||
<input type="submit" value="Hinzufügen" />
|
<input class="button smaller" type="submit" value="Hinzufügen" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
Binary file not shown.
|
@ -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/
|
|
@ -0,0 +1,29 @@
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"id" : "bank",
|
||||||
|
"name" : "Bank of Cheese",
|
||||||
|
"discount" : {
|
||||||
|
"minimumDailySalesVolumeNetCent": 100,
|
||||||
|
"percentDiscount": 1
|
||||||
|
},
|
||||||
|
"articles": [
|
||||||
|
{
|
||||||
|
"title": "Big Mac",
|
||||||
|
"manufacturer": "Mc Donalds",
|
||||||
|
"articleNumber": "0x1 BIGMAC",
|
||||||
|
"vatPercent": 7,
|
||||||
|
"pricePerUnitNet": 700,
|
||||||
|
"shouldBeAdvertised": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "500£ Schein",
|
||||||
|
"manufacturer": "Bank",
|
||||||
|
"articleNumber": "500",
|
||||||
|
"vatPercent": 0,
|
||||||
|
"pricePerUnitNet": 50000,
|
||||||
|
"shouldBeAdvertised": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"id" : "hans",
|
||||||
|
"name" : "Hans and more",
|
||||||
|
"discount" : {
|
||||||
|
"minimumDailySalesVolumeNetCent": 100000,
|
||||||
|
"percentDiscount": 2
|
||||||
|
},
|
||||||
|
"articles": [
|
||||||
|
{
|
||||||
|
"title": "Big Mac",
|
||||||
|
"manufacturer": "Mc Donalds",
|
||||||
|
"articleNumber": "0x1 BIGMAC",
|
||||||
|
"vatPercent": 7,
|
||||||
|
"pricePerUnitNet": 700,
|
||||||
|
"shouldBeAdvertised": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Pommes",
|
||||||
|
"manufacturer": "Mc Donalds",
|
||||||
|
"articleNumber": "0x1 POmes",
|
||||||
|
"vatPercent": 7,
|
||||||
|
"pricePerUnitNet": 100,
|
||||||
|
"shouldBeAdvertised": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Milchshake Premium 19%",
|
||||||
|
"manufacturer": "Mc Donalds",
|
||||||
|
"articleNumber": "0x2",
|
||||||
|
"vatPercent": 19,
|
||||||
|
"pricePerUnitNet": 50,
|
||||||
|
"shouldBeAdvertised": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,185 @@
|
||||||
|
#!/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" "$@"
|
|
@ -0,0 +1,104 @@
|
||||||
|
@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 Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@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
|
|
@ -0,0 +1 @@
|
||||||
|
rootProject.name = 'supplier'
|
|
@ -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,45 @@
|
||||||
|
package org.hso.ecommerce.supplier;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.hso.ecommerce.supplier.data.Supplier;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ConfigurationReader {
|
||||||
|
|
||||||
|
public static List<Supplier> read() throws IOException {
|
||||||
|
File dir = new File("./config/");
|
||||||
|
|
||||||
|
System.out.println("Loading Config in " + dir.getAbsolutePath());
|
||||||
|
ArrayList<Supplier> ret = Files.list(dir.toPath()).map(path -> {
|
||||||
|
|
||||||
|
System.out.println("Iterating over; " + path);
|
||||||
|
if (path.toString().endsWith(".json")) {
|
||||||
|
try {
|
||||||
|
String jsonData = Files.readString(path, StandardCharsets.UTF_8);
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
Supplier sup = objectMapper.readValue(jsonData, Supplier.class);
|
||||||
|
System.out.println("Loaded " + sup.id);
|
||||||
|
return sup;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("Skipping because of file extension.");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
|
||||||
|
ret.removeIf(Objects::isNull);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
package org.hso.ecommerce.supplier;
|
||||||
|
|
||||||
|
import org.hso.ecommerce.supplier.data.Article;
|
||||||
|
import org.hso.ecommerce.supplier.data.Order;
|
||||||
|
import org.hso.ecommerce.supplier.data.OrderConfirmation;
|
||||||
|
import org.hso.ecommerce.supplier.data.Supplier;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class RequestController {
|
||||||
|
|
||||||
|
private final HashMap<String, Integer> dailySalesVolumeCent = new HashMap<>();
|
||||||
|
private final HashMap<String, Supplier> knownSuppliers = new HashMap<>();
|
||||||
|
private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() throws IOException {
|
||||||
|
for (Supplier s : ConfigurationReader.read()) {
|
||||||
|
knownSuppliers.put(s.id, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public List<Supplier> index() {
|
||||||
|
return new ArrayList<>(knownSuppliers.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{supplier}/")
|
||||||
|
public Supplier supplier(HttpServletResponse res, @PathVariable("supplier") String supplierName) {
|
||||||
|
Supplier s = knownSuppliers.get(supplierName);
|
||||||
|
if(s == null) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{supplier}/order")
|
||||||
|
public OrderConfirmation order(HttpServletResponse res, @PathVariable("supplier") String supplierName, @RequestBody Order order) {
|
||||||
|
Supplier s = knownSuppliers.get(supplierName);
|
||||||
|
if(s == null) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String dateKey = simpleDateFormat.format(new Date());
|
||||||
|
int dailyVolume = dailySalesVolumeCent.getOrDefault(dateKey,0);
|
||||||
|
|
||||||
|
Article a = s.findArticle(order.manufacturer, order.articleNumber);
|
||||||
|
if(a == null) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int priceNet = a.pricePerUnitNet * order.quantity;
|
||||||
|
int discount = 0;
|
||||||
|
if(dailyVolume >= s.discount.minimumDailySalesVolumeNetCent) {
|
||||||
|
discount = (priceNet * s.discount.percentDiscount) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderConfirmation confirmation = new OrderConfirmation();
|
||||||
|
confirmation.articleNumber = order.articleNumber;
|
||||||
|
confirmation.discountNetCent = discount;
|
||||||
|
confirmation.pricePerUnitNetCent = a.pricePerUnitNet;
|
||||||
|
confirmation.manufacturer = a.manufacturer;
|
||||||
|
confirmation.quantity = order.quantity;
|
||||||
|
confirmation.totalPriceNetCharged = priceNet - discount;
|
||||||
|
|
||||||
|
if (confirmation.totalPriceNetCharged > order.maxTotalPriceCentNet) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dailyVolume += confirmation.totalPriceNetCharged;
|
||||||
|
dailySalesVolumeCent.put(dateKey, dailyVolume);
|
||||||
|
|
||||||
|
return confirmation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.hso.ecommerce.supplier.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.supplier.data;
|
||||||
|
|
||||||
|
public class Order {
|
||||||
|
public String manufacturer;
|
||||||
|
public String articleNumber;
|
||||||
|
|
||||||
|
public int quantity;
|
||||||
|
public int maxTotalPriceCentNet;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.hso.ecommerce.supplier.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.supplier.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.supplier.data;
|
||||||
|
|
||||||
|
public class SupplierDiscount {
|
||||||
|
public int minimumDailySalesVolumeNetCent;
|
||||||
|
public int percentDiscount;
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
server.address=::1
|
||||||
|
server.port=8081
|
Reference in New Issue