98 Commits

Author SHA1 Message Date
b993700ae4 Merge pull request 'feature/employee_login_defaults' (#124) from feature/employee_login_defaults into master
Reviewed-on: #124
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 19:00:36 +02:00
5d506a1526 Merge pull request 'Fix display order in various locations. closes #120' (#121) from feateure/display_order_intern into master
Reviewed-on: #121
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:59:14 +02:00
e87d0d8cdd Merge pull request 'feature/optimize_create_order_whbookings' (#117) from feature/optimize_create_order_whbookings into master
Reviewed-on: #117
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:57:45 +02:00
fb222c9213 Merge branch 'master' into feature/employee_login_defaults 2020-06-24 18:57:05 +02:00
cf9ab66e68 Hide /intern/ link if non employee. closes #76 2020-06-24 18:40:57 +02:00
cdc372d574 Redirect to intern if employee. 2020-06-24 18:40:22 +02:00
2290f01a8f Merge pull request 'feature/fix_delivery_api' (#116) from feature/fix_delivery_api into master
Reviewed-on: #116
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:38:03 +02:00
3bc0996c73 Merge pull request 'Add & generate tracking information to (Supplier-)OrderConfirmation. Fixes #110' (#114) from feature/supplier_tracking into master
Reviewed-on: #114
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:37:15 +02:00
758c6810ab Merge pull request 'Clean Shopping Cart on Logout' (#123) from feature/clean_shopping_on_logout into master
Reviewed-on: #123
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:27:24 +02:00
577a4f72f0 Clean Shopping Cart on Logout for real 2020-06-24 18:25:42 +02:00
a2aca7a8ca Clean Shopping Cart on Logout 2020-06-24 18:23:01 +02:00
3b7317a831 Fix typo 7 2020-06-24 18:19:14 +02:00
32921a3f87 Merge pull request 'Remove WarehouseBookingReason it was not used.' (#115) from feature/remove_unused_warehouse_booking_reason into master
Reviewed-on: #115
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 15:03:47 +02:00
63e9bc2336 Merge pull request 'Fix in stock calculation. closes #118' (#119) from feature/display_article_shop_in_stock into master
Reviewed-on: #119
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 15:03:04 +02:00
31bff55a20 Merge pull request 'Show advertisements distinct.' (#122) from feature/show_ads_distinct into master
Reviewed-on: #122
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 14:59:50 +02:00
5861b61565 Merge branch 'master' into feature/supplier_tracking 2020-06-24 10:44:38 +02:00
f0c9823e45 Clearify Inventory view with text. 2020-06-24 09:53:19 +02:00
6fbb16fea2 Show advertisements distinct. 2020-06-24 02:20:24 +02:00
0d547c8903 Fix display order in various locations. closes #120 2020-06-24 02:15:31 +02:00
5162e3fc19 Fix in stock calculation. closes #118 2020-06-24 02:05:25 +02:00
8bfe8b37f5 Maybe Fix warehouse booking repo 2020-06-24 02:00:37 +02:00
2e07d03908 Fix warehouse bookings on CreateOrderAction 2020-06-24 01:48:33 +02:00
b9e87f5bd4 Add additional infos for order history in /user/... fixes #109 2020-06-24 01:19:37 +02:00
cd641d6cc4 Fix #113 by fixing initialization of RestServiceForDelivery. 2020-06-24 01:18:16 +02:00
4e9f7800be Remove WarehouseBookingReason it was not used. 2020-06-24 00:42:55 +02:00
8d27c486d5 Add & generate tracking information to (Supplier-)OrderConfirmation. Fixes #110 2020-06-24 00:37:58 +02:00
714ac4f1de Fix issues with class loading in Cronjobs 2020-06-24 00:35:18 +02:00
186db31b17 Merge pull request 'Fix showing CustomerOrder all CustomerOrders in my orders. fix displaying orders in random order' (#108) from feature/fix_show_orders into master
reviewed by Lukas
2020-06-21 22:22:53 +02:00
fb53c68be3 Merge pull request 'implement Config for Supplier and Delivery Service' (#107) from feature/reading_config into master
reviewed by Lukas
2020-06-21 22:22:43 +02:00
19cf6ccfc5 Merge pull request 'Implement Supplier AutoSupplierPayment. closes #100' (#105) from feature/supplier_auto_payment into master 2020-06-21 22:13:41 +02:00
7fafd14715 Fix variable name 2020-06-21 19:01:25 +02:00
d5181f149a Fix showing CustomerOrder all CustomerOrders in my orders. fix displaying orders in random order 2020-06-21 18:58:15 +02:00
a950ecb2b9 Make delivery/gradlew executable 2020-06-21 18:42:33 +02:00
f42a50d24d fix CustomerOrder find by order 2020-06-21 18:32:04 +02:00
33118ddb2f Mark lost delivery as delivered. #spaghetticode 2020-06-21 18:25:33 +02:00
a6a2adc7db implement Config for Supplier and Delivery Service 2020-06-21 16:49:35 +02:00
62df2346c3 fix template crashing when defaultAddr == null 2020-06-21 16:28:00 +02:00
c1a0ecdcf3 fix delivery server crashing when tracking id not found 2020-06-21 16:13:48 +02:00
a1abbf5329 Implement Supplier AutoSupplierPayment. closes #100 2020-06-21 15:50:27 +02:00
deaf283a98 remove db blob 2020-06-21 01:08:58 +02:00
ffe9af0633 Prevent duplicate Articles from ArticleOffer 2020-06-21 01:07:04 +02:00
2989b54593 Merge pull request 'fix_change_user_name' (#104) from fix_change_user_name into master 2020-06-21 00:15:57 +02:00
b846b919b7 code clean up 2020-06-21 00:14:00 +02:00
1f83a82f17 removed user.name and use user.defaultDeliveryAddress.name instead 2020-06-21 00:13:10 +02:00
cd1a8568e6 show only one entry per article suggestions fix #96 2020-06-21 00:06:15 +02:00
53cdb20091 Merge pull request 'Fix the warehouse diagrams on the dashboard' (#103) from fix/dashboard_warehouse_statistics into master 2020-06-21 00:02:50 +02:00
9389675b5c Do not show dupliactes in category search. fixes #85 2020-06-21 00:01:28 +02:00
97a7f3a8ad Fix the warehouse diagrams on the dashboard
- Show the correct utilization
- Display values as correct percentages

Closes #99
2020-06-20 23:54:33 +02:00
aa2f42a10a Add password restrictions fixes #91 2020-06-20 23:47:53 +02:00
f128a8b9ed Make reason for manuel booking required. fixes #90 2020-06-20 23:35:56 +02:00
ffea25c082 Merge pull request 'Fix bad query for article slot sum' (#102) from feature/89_fix_lagerstand into master 2020-06-20 23:30:12 +02:00
d77b8c046a Fix bad query for article slot sum 2020-06-20 23:24:06 +02:00
8fdfe9cc69 Catch failing delivery server 2020-06-20 22:51:18 +02:00
e5332a3348 Fix String -> UUIDs -> String in method causing crash 2020-06-20 22:25:52 +02:00
c3eaf5ac11 Remove unused db fields 2020-06-20 22:10:53 +02:00
fa75beb9c4 Fix 6284a4d7bf altering template 2020-06-20 22:09:25 +02:00
4bef17dc0a Merge pull request 'feature/dashboard' (#83) from feature/dashboard into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-19 14:12:44 +02:00
79e4986880 delete blank line 2020-06-19 14:11:32 +02:00
181a09e16b Merge pull request 'feature/config' (#78) from feature/config into master
Reviewed-by: CodeSteak <codesteak@shellf.art>
2020-06-18 14:09:46 +02:00
f4aae9c581 ignore config.yml 2020-06-18 14:07:59 +02:00
ea0c8c45d6 rename eCommerce_config.yml to config.yml
* if no config file is present, create a default config file
2020-06-18 14:04:54 +02:00
8416783e0a merged 2020-06-18 01:13:03 +02:00
bcbebe8e8d master merged into feature/customer_orders 2020-06-18 01:11:11 +02:00
a570696795 feature/customer_orders merged into master 2020-06-18 01:07:11 +02:00
187656814e String Error -> Enum ErrorHandling Logic removed from model class 2020-06-18 00:50:43 +02:00
8c0652b26b exit if the settings file has not been found
* use a final variable for the settings file name
2020-06-17 22:32:48 +02:00
ef07447abc fix json-file 2020-06-17 20:42:08 +02:00
bb7b35dc28 Increase the amount of commits 2020-06-17 19:00:49 +02:00
93cfbb4a27 Merge remote-tracking branch 'origin/master' 2020-06-17 18:59:10 +02:00
68cc35cec3 Fix typo 6 2020-06-17 18:57:42 +02:00
b2ee048f25 Fix typo 5 2020-06-17 18:57:23 +02:00
020a30474e Fix typo 4 2020-06-17 18:56:32 +02:00
4bfc8a2cc6 Fix typo 3 2020-06-17 18:56:02 +02:00
0ff873baaa Fix typo 2 2020-06-17 18:55:50 +02:00
1812c89994 Fix typo 2020-06-17 18:55:29 +02:00
2442a477c8 add missing "|" 2020-06-17 18:43:06 +02:00
ebb3c8c235 removes unused Imports and fomattet 2020-06-17 17:23:29 +02:00
e6c068e71a Secretly fix number 0 showing on 'Warenkorb' 2020-06-17 00:23:05 +02:00
86e3ced19b Secretly fix searchbar not showing term when searching 2020-06-17 00:17:50 +02:00
44df9e4b4f Secretly add Favicon 2020-06-17 00:11:55 +02:00
18cbfbf148 Fix template strings with a starting "/" 2020-06-17 00:02:46 +02:00
5a0b303601 Merge branch 'master' into feature/config 2020-06-16 20:44:10 +02:00
d5825ba7a0 use @Autowired to get AppSettings Object 2020-06-16 20:42:40 +02:00
774e62f892 add suppliers to YAMLData, store data in AppSettings() 2020-06-16 19:19:58 +02:00
e1c00eca8f fix ApplicationSettings.readConfig() 2020-06-16 18:52:33 +02:00
e904eba7a4 Merge remote-tracking branch 'origin/feature/manual_cronjobs' into feature/dashboard
Butfixes
2020-06-15 21:41:54 +02:00
6284a4d7bf Dashboard working 2020-06-15 17:47:55 +02:00
d121532fba Merge remote-tracking branch 'origin/master' into feature/config 2020-06-14 21:15:37 +02:00
0ab185f143 impl trackingID for the UserController 2020-06-13 21:59:18 +02:00
1dd4b5cfa5 del update query and impl error for trackigID 2020-06-13 20:05:22 +02:00
266bba095a merged feature/customer_orders with master 2020-06-13 19:58:30 +02:00
fa7bdf2f7e impl error handling 2020-06-13 19:37:34 +02:00
ebeba72278 impl tracking ID 2020-06-10 17:03:59 +02:00
f671baf7f0 Error with package path 2020-06-10 14:51:12 +02:00
3775f96f3f implement getCustomerOrderDetail 2020-06-01 11:53:29 +02:00
2394157be8 implement getCustomerOrder 2020-06-01 11:31:12 +02:00
bba4173269 fixed inputstream once more 2020-05-10 22:19:58 +02:00
e1d7becc2e added yaml dump&parse class 2020-05-10 21:23:44 +02:00
82 changed files with 1694 additions and 542 deletions

0
delivery/gradlew vendored Normal file → Executable file
View File

View File

@ -4,9 +4,9 @@ package org.hso.ecommerce.supplier;
import org.hso.ecommerce.supplier.data.Delivery; import org.hso.ecommerce.supplier.data.Delivery;
import org.hso.ecommerce.supplier.data.DeliveryManager; import org.hso.ecommerce.supplier.data.DeliveryManager;
import org.hso.ecommerce.supplier.data.ReturnStatus; import org.hso.ecommerce.supplier.data.ReturnStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -19,14 +19,19 @@ public class RequestController {
public String supplier(HttpServletResponse response, HttpServletRequest request, @RequestBody Delivery delivery) { public String supplier(HttpServletResponse response, HttpServletRequest request, @RequestBody Delivery delivery) {
DeliveryManager.getInstance().add(delivery); DeliveryManager.getInstance().add(delivery);
return delivery.getUuid().toString(); return delivery.getUuid();
} }
@GetMapping("/status") @GetMapping(value = "/status", produces = MediaType.APPLICATION_JSON_VALUE)
public ReturnStatus searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) { public ReturnStatus searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) {
Delivery delivery = DeliveryManager.getInstance().getDeliveryByeID(trackingID); Delivery delivery = DeliveryManager.getInstance().getDeliveryByID(trackingID);
if (delivery == null) {
Delivery lostDelivery = Delivery.lostDelivery(trackingID);
DeliveryManager.getInstance().add(lostDelivery);
delivery = lostDelivery;
}
return new ReturnStatus(delivery.getStatus(),delivery.getEstimatedArrival()); return new ReturnStatus(delivery.getStatus(), delivery.getEstimatedArrival());
} }
} }

View File

@ -8,38 +8,48 @@ import java.util.UUID;
public class Delivery { public class Delivery {
private String[] states = {"Bestellung eingegangen","Bestellung auf dem Weg","Lieferung erfolgreich"}; private String[] states = {"Bestellung eingegangen", "Bestellung auf dem Weg", "Lieferung erfolgreich"};
private int[] timeBorder = {4,24}; private int[] timeBorder = {4, 24};
private String name; private String name;
private String address; private String address;
private String estimatedArrival;
private Date creationTime;
private UUID uuid;
public Delivery(String name, String address) // Why is this a string and creationTime a Date?!
{ private String estimatedArrival;
private Date creationTime;
private String uuid;
public Delivery(String name, String address) {
this.name = name; this.name = name;
this.address = address; this.address = address;
this.uuid = UUID.randomUUID(); this.uuid = UUID.randomUUID().toString();
this.creationTime = new Date(); this.creationTime = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
this.estimatedArrival = formatter.format(addDays((Date)this.creationTime.clone(),1)); this.estimatedArrival = formatter.format(addDays((Date) this.creationTime.clone(), 1));
} }
public static Delivery lostDelivery(String uuid) {
Delivery delivery = new Delivery("", "");
delivery.uuid = uuid;
delivery.creationTime = addDays(new Date(), -1);
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
delivery.estimatedArrival = formatter.format(addDays((Date) delivery.creationTime.clone(), 1));
return delivery;
}
public String getStatus() public String getStatus() {
{
Date now = new Date(); Date now = new Date();
Long timeNow = now.getTime(); long timeNow = now.getTime();
Long creationTime = this.creationTime.getTime(); long creationTime = this.creationTime.getTime();
Long diff = timeNow - creationTime; // Wow, that's how calculate date diffs.
long diff = timeNow - creationTime;
double hour = (((diff / 1000.0) / 3600.0)); double hour = (((diff / 1000.0) / 3600.0));
for (int i = 0; i < timeBorder.length; i++) { for (int i = 0; i < timeBorder.length; i++) {
if(hour < timeBorder[i]) if (hour < timeBorder[i])
return states[i]; return states[i];
} }
@ -51,15 +61,14 @@ public class Delivery {
return estimatedArrival; return estimatedArrival;
} }
private Date addDays(Date date, int days) private static Date addDays(Date date, int days) {
{
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(date); cal.setTime(date);
cal.add(Calendar.DATE, days); cal.add(Calendar.DATE, days);
return cal.getTime(); return cal.getTime();
} }
public UUID getUuid() { public String getUuid() {
return uuid; return uuid;
} }

View File

@ -1,20 +1,18 @@
package org.hso.ecommerce.supplier.data; package org.hso.ecommerce.supplier.data;
import java.util.ArrayList; import java.util.HashMap;
import java.util.List;
import java.util.UUID;
public class DeliveryManager { public class DeliveryManager {
private List<Delivery> deliveryList; private HashMap<String, Delivery> deliveryList;
private static DeliveryManager deliveryManager; private static DeliveryManager deliveryManager;
private DeliveryManager() private DeliveryManager()
{ {
deliveryList = new ArrayList<>(); deliveryList = new HashMap<>();
} }
public static DeliveryManager getInstance () { public static DeliveryManager getInstance() {
if (DeliveryManager.deliveryManager == null) { if (DeliveryManager.deliveryManager == null) {
DeliveryManager.deliveryManager = new DeliveryManager(); DeliveryManager.deliveryManager = new DeliveryManager();
@ -22,13 +20,11 @@ public class DeliveryManager {
return DeliveryManager.deliveryManager; return DeliveryManager.deliveryManager;
} }
public boolean add(Delivery delivery) public void add(Delivery delivery) {
{ deliveryList.put(delivery.getUuid(), delivery);
return deliveryList.add(delivery);
} }
public Delivery getDeliveryByeID(String uuid) public Delivery getDeliveryByID(String uuid) {
{ return deliveryList.getOrDefault(uuid, null);
return deliveryList.parallelStream().filter(d -> d.getUuid().equals(UUID.fromString(uuid))).findAny().get();
} }
} }

View File

@ -5,3 +5,5 @@ e-commerce.db
./e-commerce.iml ./e-commerce.iml
./e-commerce.ipr ./e-commerce.ipr
./e-commerce.iws ./e-commerce.iws
config.yml

View File

@ -28,6 +28,7 @@ dependencies {
implementation 'com.github.gwenn:sqlite-dialect:0.1.0' implementation 'com.github.gwenn:sqlite-dialect:0.1.0'
implementation 'org.springframework.boot:spring-boot-devtools' implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'org.xerial:sqlite-jdbc:3.31.1' implementation 'org.xerial:sqlite-jdbc:3.31.1'
implementation 'org.yaml:snakeyaml:1.26'
testCompile("org.springframework.boot:spring-boot-starter-test") testCompile("org.springframework.boot:spring-boot-starter-test")
} }

View File

@ -1,8 +1,5 @@
package org.hso.ecommerce.action.cronjob; 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.ArticleIdentifier;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer; import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer;
import org.hso.ecommerce.api.SupplierService; import org.hso.ecommerce.api.SupplierService;
@ -14,6 +11,9 @@ import org.hso.ecommerce.entities.supplier.SupplierOrder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.sql.Timestamp;
import java.util.HashMap;
public class ReorderAction { public class ReorderAction {
private static final Logger log = LoggerFactory.getLogger(ReorderAction.class); private static final Logger log = LoggerFactory.getLogger(ReorderAction.class);
@ -102,6 +102,10 @@ public class ReorderAction {
createdOrder.numberOfUnits = confirm.quantity; createdOrder.numberOfUnits = confirm.quantity;
createdOrder.pricePerUnitNetCent = confirm.pricePerUnitNetCent; createdOrder.pricePerUnitNetCent = confirm.pricePerUnitNetCent;
createdOrder.totalPriceNet = confirm.totalPriceNetCharged; createdOrder.totalPriceNet = confirm.totalPriceNetCharged;
createdOrder.carrier = confirm.carrier;
createdOrder.trackingId = confirm.trackingId;
createdOrder.estimatedArrival = Timestamp.valueOf(confirm.estimatedArrival);
return createdOrder; return createdOrder;
} }
} }

View File

@ -13,6 +13,7 @@ import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -101,7 +102,14 @@ public class CreateOrderAction {
for (OrderItem item : orderItems) { for (OrderItem item : orderItems) {
int needed = item.quantity; int needed = item.quantity;
// Sort for most empty slot first;
item.availableSlots.sort(Comparator.comparingInt(a -> a.newSumSlot));
for (WarehouseBookingPositionSlotEntry slot : item.availableSlots) { for (WarehouseBookingPositionSlotEntry slot : item.availableSlots) {
if (slot.newSumSlot == 0) {
continue;
}
int remove = Math.min(slot.newSumSlot, needed); int remove = Math.min(slot.newSumSlot, needed);
needed -= remove; needed -= remove;

View File

@ -1,11 +1,16 @@
package org.hso.ecommerce.action.shop; package org.hso.ecommerce.action.shop;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.springframework.web.client.ResourceAccessException;
import java.sql.Timestamp;
import java.util.Date;
public class EnableTrackingAction { public class EnableTrackingAction {
public static void addTrackingInfo(CustomerOrder customerOrder) { public static void addTrackingInfo(RestServiceForDelivery deliveryService, CustomerOrder customerOrder) throws ResourceAccessException {
// TODO: customerOrder.inDeliverySince = new Timestamp(new Date().getTime());
customerOrder.trackingId = "555-NASE"; customerOrder.trackingId = deliveryService.getDeliveryID(customerOrder);
} }
} }

View File

@ -0,0 +1,48 @@
package org.hso.ecommerce.action.user;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.hso.ecommerce.uimodel.DeliveryDataEnum;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class CreateDeliveryData {
public static DeliveryData getDeliveryDataFromCustomerOrder(CustomerOrder customerOrder, CustomerOrderRepository customerOrderRepository, RestServiceForDelivery restServiceForDelivery)
{
if(customerOrder.trackingId == null)
return new DeliveryData("", "", DeliveryDataEnum.NO_TRACKING_ID);
if(customerOrder.deliveredAt == null)
{
DeliveryData deliveryData = restServiceForDelivery.getDeliveryData(customerOrder.trackingId);
if(deliveryData.isDelivered())
{
Calendar calendar = Calendar.getInstance();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
try {
calendar.setTime(simpleDateFormat.parse(deliveryData.getEstimatedArrival()));
} catch (ParseException e) {
e.printStackTrace();
}
customerOrder.deliveredAt = new Timestamp(calendar.getTimeInMillis());
customerOrderRepository.save(customerOrder);
}
return deliveryData;
}
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
return new DeliveryData("Lieferung erfolgreich", formatter.format(customerOrder.deliveredAt), DeliveryDataEnum.OK);
}
}

View File

@ -48,7 +48,7 @@ public class UpdateUserSettingsAction {
public UpdateResult updateShippingInfo(String salutation, String name, String address) { public UpdateResult updateShippingInfo(String salutation, String name, String address) {
this.user.salutation = salutation; this.user.salutation = salutation;
this.user.name = name; this.user.defaultDeliveryAddress.name = name;
this.user.defaultDeliveryAddress.addressString = address; this.user.defaultDeliveryAddress.addressString = address;
this.repository.save(this.user); this.repository.save(this.user);
return new UpdateResult(true); return new UpdateResult(true);

View File

@ -59,7 +59,7 @@ public class CalculateWarehouseStatsAction {
return articleIds.size(); return articleIds.size();
} }
private static class WarehouseStats { public static class WarehouseStats {
public int numArticles; public int numArticles;
public double efficiency; public double efficiency;
public double ratioUsedSlots; public double ratioUsedSlots;

View File

@ -0,0 +1,83 @@
package org.hso.ecommerce.api;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.hso.ecommerce.uimodel.DeliveryDataEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.HashMap;
import java.util.Map;
@Service
public class RestServiceForDelivery {
@Autowired
private final RestTemplateBuilder restTemplateBuilder = null;
@Autowired
private final AppSettings settings = null;
private String getDeliveryEndpoint() {
return settings.getParcelServiceApiURL();
}
public String getDeliveryID(CustomerOrder customerOrder) throws ResourceAccessException {
String url = getDeliveryEndpoint() + "/newDelivery";
RestTemplate restTemplate = restTemplateBuilder.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Map<String, String> requestBody = new HashMap<>();
requestBody.put("name", customerOrder.destination.name);
requestBody.put("address", customerOrder.destination.addressString);
HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
return response.getBody();
} else {
throw new ResourceAccessException("Http Status wrong");
}
}
public DeliveryData getDeliveryData(String trackingID) {
String url = getDeliveryEndpoint() + "/status";
RestTemplate restTemplate = restTemplateBuilder.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).queryParam("trackingID", trackingID);
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<DeliveryData> response;
try {
response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity, DeliveryData.class);
if (response.getStatusCode() == HttpStatus.OK) {
return response.getBody();
} else {
return new DeliveryData("", "", DeliveryDataEnum.NO_DATA);
}
} catch (Exception e) {
return new DeliveryData("", "", DeliveryDataEnum.NO_DATA);
}
}
}

View File

@ -1,5 +1,7 @@
package org.hso.ecommerce.api.data; package org.hso.ecommerce.api.data;
import java.time.LocalDateTime;
public class OrderConfirmation { public class OrderConfirmation {
public String manufacturer; public String manufacturer;
public String articleNumber; public String articleNumber;
@ -9,4 +11,8 @@ public class OrderConfirmation {
public int pricePerUnitNetCent; public int pricePerUnitNetCent;
public int discountNetCent; public int discountNetCent;
public int totalPriceNetCharged; public int totalPriceNetCharged;
public String carrier;
public String trackingId;
public LocalDateTime estimatedArrival;
} }

View File

@ -13,6 +13,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
public class Application { public class Application {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(Application.class, args); SpringApplication.run(Application.class, args);
} }

View File

@ -1,21 +0,0 @@
package org.hso.ecommerce.app;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* TODO clean up this class
*/
@Controller
public class RequestController {
@GetMapping("/intern/customerOrders/")
public String internCustomerOrder() {
return "intern/customerOrders/index";
}
@GetMapping("/intern/customerOrders/{id}")
public String internCustomerOrdersId() {
return "intern/customerOrders/id";
}
}

View File

@ -0,0 +1,161 @@
package org.hso.ecommerce.app.config;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.hso.ecommerce.app.config.YAMLData.Address;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import javax.annotation.PostConstruct;
@Component("appSettings")
public class AppSettings {
private YAMLData data;
private final String configFile = "config.yml";
private String installationName;
private String companyName;
private Address companyAddress;
private int numberOfStorageSpaces;
private List<YAMLData.Supplier> suppliers;
private String parcelServiceName;
private String parcelServiceApiURL;
/**
* on initialization read the config and store the data in static objects
*/
@PostConstruct
public void init() {
data = readConfig();
installationName = data.getInstallationName();
companyName = data.getCompanyName();
companyAddress = data.getCompanyAddress();
numberOfStorageSpaces = data.getNumberOfStorageSpaces();
suppliers = data.getSuppliers();
parcelServiceName = data.getParcelServiceName();
parcelServiceApiURL = data.getParcelServiceApiURL();
System.out.println("Initialised Settings!");
}
/**
* write the default config file
*/
public void writeDefaultConfig() {
YAMLData data = new YAMLData();
data.setInstallationName("eCommerce");
data.setCompanyName("eCommerce Shop UG");
data.setCompanyAddress(new Address(
"Musterstraße",
"1",
"12345",
"Musterstadt",
"Germany"
));
data.setNumberOfStorageSpaces(128);
List<YAMLData.Supplier> suppliers = new ArrayList<>();
suppliers.add(new YAMLData.Supplier(
"Bank of Chees",
"d41d8cd98f00b204e9800998ecf8427e",
"http://[::1]:8081/bank/",
4,
new Address(
"Musterstraße",
"2",
"12345",
"Musterstadt",
"Germany"
)
));
suppliers.add(new YAMLData.Supplier(
"MDA",
"18a17da5bac1cf00551b08c3e98720f5",
"http://[::1]:8081/mda/",
5,
new Address(
"Musterstraße",
"3",
"12345",
"Musterstadt",
"Germany"
)
));
data.setSuppliers(suppliers);
data.setParcelServiceName("Parcel Service");
data.setParcelServiceApiURL("http://[::1]:8082/");
try (FileWriter writer = new FileWriter("./" + configFile)) {
Yaml yaml = new Yaml();
yaml.dump(data, writer);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* read a config file named "eCommerce_config.yml" from the applications root directory
* @return the settings as YAMLData object
*/
public YAMLData readConfig() {
YAMLData data = new YAMLData();
File file = new File("./" + configFile);
if (!file.exists()) {
writeDefaultConfig();
}
try (InputStream inputStream = new FileInputStream("./" + configFile)) {
Yaml yaml = new Yaml(new Constructor(YAMLData.class));
data = yaml.load(inputStream);
} catch (FileNotFoundException e) {
System.err.println("The file \"" + configFile + "\" has not been found, please create a valid Configuration file.");
e.printStackTrace();
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
return data;
}
public YAMLData getData() {
return data;
}
public String getInstallationName() {
return installationName;
}
public String getCompanyName() {
return companyName;
}
public Address getCompanyAddress() {
return companyAddress;
}
public int getNumberOfStorageSpaces() {
return numberOfStorageSpaces;
}
public List<YAMLData.Supplier> getSuppliers() {
return suppliers;
}
public String getParcelServiceName() {
return parcelServiceName;
}
public String getParcelServiceApiURL() {
return parcelServiceApiURL;
}
}

View File

@ -0,0 +1,183 @@
package org.hso.ecommerce.app.config;
import java.util.List;
public class YAMLData {
private String installationName;
private String companyName;
private Address companyAddress;
private int numberOfStorageSpaces;
private List<Supplier> suppliers;
private String parcelServiceName;
private String parcelServiceApiURL;
public String getInstallationName() {
return installationName;
}
public void setInstallationName(String installationName) {
this.installationName = installationName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Address getCompanyAddress() {
return companyAddress;
}
public void setCompanyAddress(Address companyAddress) {
this.companyAddress = companyAddress;
}
public int getNumberOfStorageSpaces() {
return numberOfStorageSpaces;
}
public void setNumberOfStorageSpaces(int numberOfStorageSpaces) {
this.numberOfStorageSpaces = numberOfStorageSpaces;
}
public List<Supplier> getSuppliers() {
return suppliers;
}
public void setSuppliers(List<Supplier> suppliers) {
this.suppliers = suppliers;
}
public String getParcelServiceName() {
return parcelServiceName;
}
public void setParcelServiceName(String parcelServiceName) {
this.parcelServiceName = parcelServiceName;
}
public String getParcelServiceApiURL() {
return parcelServiceApiURL;
}
public void setParcelServiceApiURL(String parcelServiceApiURL) {
this.parcelServiceApiURL = parcelServiceApiURL;
}
public static class Address {
public String streetName;
public String houseNumber;
public String zipCode;
public String cityName;
public String countryName;
public Address() {
// needed by snakeyaml
}
public Address(String streetName, String houseNumber, String zipCode, String cityName, String countryName) {
this.streetName = streetName;
this.houseNumber = houseNumber;
this.zipCode = zipCode;
this.cityName = cityName;
this.countryName = countryName;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getHouseNumber() {
return houseNumber;
}
public void setHouseNumber(String houseNumber) {
this.houseNumber = houseNumber;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
public static class Supplier {
public String name;
public String id;
public String apiURL;
public int deliveryTime;
public Address companyAddress;
public Supplier() {
// needed by snakeyaml
}
public Supplier(String name, String id, String apiURL, int deliveryTime, Address companyAddress) {
this.name = name;
this.id = id;
this.apiURL = apiURL;
this.deliveryTime = deliveryTime;
this.companyAddress = companyAddress;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getApiURL() {
return apiURL;
}
public void setApiURL(String apiURL) {
this.apiURL = apiURL;
}
public int getDeliveryTime() {
return deliveryTime;
}
public void setDeliveryTime(int deliveryTime) {
this.deliveryTime = deliveryTime;
}
public Address getCompanyAddress() {
return companyAddress;
}
public void setCompanyAddress(Address companyAddress) {
this.companyAddress = companyAddress;
}
}
}

View File

@ -1,16 +1,16 @@
package org.hso.ecommerce.components; package org.hso.ecommerce.components;
import org.hso.ecommerce.entities.booking.PaymentMethod; 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.entities.user.User;
import org.hso.ecommerce.repos.user.UserRepository; import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Optional; import java.util.Optional;
import javax.annotation.PostConstruct;
@Component @Component
public class AdminInitializer { public class AdminInitializer {
@ -24,7 +24,8 @@ public class AdminInitializer {
// create first admin user // create first admin user
User firstAdmin = new User(); User firstAdmin = new User();
firstAdmin.created = new Timestamp(System.currentTimeMillis()); firstAdmin.created = new Timestamp(System.currentTimeMillis());
firstAdmin.name = "admin"; firstAdmin.defaultDeliveryAddress = new Address();
firstAdmin.defaultDeliveryAddress.name = "admin";
firstAdmin.defaultPayment = new PaymentMethod(); firstAdmin.defaultPayment = new PaymentMethod();
firstAdmin.defaultPayment.creditCardNumber = ""; //set empty number firstAdmin.defaultPayment.creditCardNumber = ""; //set empty number
firstAdmin.email = "admin"; firstAdmin.email = "admin";

View File

@ -1,5 +1,6 @@
package org.hso.ecommerce.components; package org.hso.ecommerce.components;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.entities.warehouse.Slot; import org.hso.ecommerce.entities.warehouse.Slot;
import org.hso.ecommerce.repos.warehouse.SlotRepository; import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -13,18 +14,22 @@ public class SlotInitializer {
@Autowired @Autowired
private final SlotRepository slotRepository = null; private final SlotRepository slotRepository = null;
// TODO: use values form cfg. @Autowired
private final int NUM_SLOTS = 50; private final AppSettings appSettings = null;
@PostConstruct
public void init() {
int NUM_SLOTS = appSettings.getNumberOfStorageSpaces();
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");
}
}
}
@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");
}
}
}
} }

View File

@ -0,0 +1,42 @@
package org.hso.ecommerce.components;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.app.config.YAMLData;
import org.hso.ecommerce.entities.supplier.Supplier;
import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Optional;
@Component
public class SupplierInitializer {
@Autowired
private final SupplierRepository supplierRepository = null;
@Autowired
private final AppSettings appSettings = null;
@PostConstruct
public void init() {
for (YAMLData.Supplier cfg : appSettings.getSuppliers()) {
Optional<Supplier> sup = supplierRepository.findByUuid(cfg.id);
Supplier supplier;
if (sup.isPresent()) {
supplier = sup.get();
supplier.name = cfg.name;
supplier.apiUrl = cfg.apiURL;
} else {
supplier = new Supplier();
supplier.uuid = cfg.id;
supplier.apiUrl = cfg.apiURL;
supplier.name = cfg.name;
}
supplierRepository.save(supplier);
}
}
}

View File

@ -1,19 +1,16 @@
package org.hso.ecommerce.controller; package org.hso.ecommerce.controller;
import java.util.Optional; import org.hso.ecommerce.entities.shop.ShoppingCart;
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.entities.user.User;
import org.hso.ecommerce.repos.user.UserRepository; import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Optional;
@Controller @Controller
@RequestMapping("/") @RequestMapping("/")
@ -56,14 +53,18 @@ public class LoginController {
if (gto != null && gto.startsWith("/")) { if (gto != null && gto.startsWith("/")) {
return "redirect:" + gto; return "redirect:" + gto;
} else if (user.get().isEmployee) {
return "redirect:/intern/";
} else { } else {
return "redirect:/"; return "redirect:/";
} }
} }
@PostMapping("logout") @PostMapping("logout")
public String logoutPost(HttpServletResponse response, HttpSession session) { public String logoutPost(@RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, HttpSession session) {
session.removeAttribute("userId"); session.removeAttribute("userId");
shoppingCart.clear();
return "redirect:/"; return "redirect:/";
} }
} }

View File

@ -1,22 +1,23 @@
package org.hso.ecommerce.controller; package org.hso.ecommerce.controller;
import org.hso.ecommerce.action.user.CreateDeliveryData;
import org.hso.ecommerce.action.user.UpdateUserSettingsAction; import org.hso.ecommerce.action.user.UpdateUserSettingsAction;
import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.user.UserRepository; import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
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.HttpServletRequest;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Controller @Controller
@RequestMapping("/user") @RequestMapping("/user")
@ -28,6 +29,12 @@ public class UserController {
@Autowired @Autowired
private final CustomerOrderRepository customerOrderRepository = null; private final CustomerOrderRepository customerOrderRepository = null;
@Autowired
private final RestServiceForDelivery restServiceForDelivery = null;
@Autowired
private final AppSettings appSettings = null;
@GetMapping("/") @GetMapping("/")
public String user() { public String user() {
return "redirect:/user/settings"; return "redirect:/user/settings";
@ -35,28 +42,49 @@ public class UserController {
@GetMapping("/settings") @GetMapping("/settings")
public String userSettings(Model model, public String userSettings(Model model,
HttpSession session @RequestAttribute("user") User user
) { ) {
long userId = (long) session.getAttribute("userId");
User user = userRepository.findById(userId).get();
if(user.defaultDeliveryAddress == null){
user.defaultDeliveryAddress = new Address();
}
model.addAttribute("user", user); model.addAttribute("user", user);
return "user/settings"; return "user/settings";
} }
@GetMapping("/orders/") @GetMapping("/orders/")
public String userOrdeers(HttpSession session, public String userOrders(
Model model @RequestAttribute("user") User user,
Model model
) { ) {
List<CustomerOrder> orders = customerOrderRepository.getOrdersByUserId((long) session.getAttribute("userId")); List<CustomerOrder> orders = customerOrderRepository.getOrdersByUserId(user.id);
model.addAttribute("orders", orders);
List<CustomerOrderDelivery> customerOrderDeliveryDataMap = orders
.stream()
.map(o -> new CustomerOrderDelivery(o, CreateDeliveryData.getDeliveryDataFromCustomerOrder(o, customerOrderRepository, restServiceForDelivery)))
.collect(Collectors.toList());
model.addAttribute("orderDeliveryDataMap", customerOrderDeliveryDataMap);
model.addAttribute("deliveryService", appSettings.getParcelServiceName());
return "user/orders/index"; return "user/orders/index";
} }
static class CustomerOrderDelivery {
private CustomerOrder customerOrder;
private DeliveryData deliveryData;
public CustomerOrderDelivery(CustomerOrder customerOrder, DeliveryData deliveryData) {
this.customerOrder = customerOrder;
this.deliveryData = deliveryData;
}
public CustomerOrder getCustomerOrder() {
return customerOrder;
}
public DeliveryData getDeliveryData() {
return deliveryData;
}
}
@PostMapping("/settings/changeMail") @PostMapping("/settings/changeMail")
public String changeMail(HttpSession session, public String changeMail(HttpSession session,
@RequestParam("email") String email, @RequestParam("email") String email,

View File

@ -0,0 +1,77 @@
package org.hso.ecommerce.controller.cronjob;
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.entities.supplier.Supplier;
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Calendar;
@Component
public class AutoSupplierPayment implements ICronjob {
@Autowired
private SupplierRepository supplierRepository = null;
@Autowired
private BookingAccountEntryRepository bookingAccountEntryRepository = null;
@Autowired
private BookingRepository bookingRepository = null;
@Override
public Calendar nextExecution(Calendar reference) {
if (reference.get(Calendar.HOUR_OF_DAY) >= 10) {
reference.add(Calendar.DAY_OF_MONTH, 1);
}
reference.set(Calendar.HOUR_OF_DAY, 10);
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) < 10) {
reference.add(Calendar.DAY_OF_MONTH, -1);
}
reference.set(Calendar.HOUR_OF_DAY, 10);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public void executeAt(Calendar time, CronjobController controller) {
for (Supplier supplier : supplierRepository.findAll()) {
int debts = bookingAccountEntryRepository
.getBySupplier(supplier.id)
.map(entry -> entry.newSumCent).orElse(0);
if (debts > 0) {
Booking booking = new CreateBookingAction(
bookingAccountEntryRepository.getBySupplier(supplier.id).orElseGet(() -> BookingAccountEntry.newSupplier(supplier)),
null,
new BookingReason(supplier),
debts
).finish();
bookingRepository.save(booking);
}
}
}
@Override
public String getDisplayName() {
return "Supplier Auto Payment";
}
}

View File

@ -1,33 +1,10 @@
package org.hso.ecommerce.controller.cronjob; package org.hso.ecommerce.controller.cronjob;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
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.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.BookingAccountEntryRepository;
import org.hso.ecommerce.repos.booking.BookingRepository; import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository; import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository;
import org.hso.ecommerce.repos.dashboard.DashboardSummaryRepository;
import org.hso.ecommerce.repos.shop.ArticleRepository; import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.supplier.ArticleOfferRepository; import org.hso.ecommerce.repos.supplier.ArticleOfferRepository;
@ -44,200 +21,17 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
interface ICronjob { import javax.annotation.PostConstruct;
/** import java.sql.Timestamp;
* Calculate the earliest cronjob execution time that happens after the given reference time. import java.util.*;
* import java.util.Map.Entry;
* @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);
/**
* Get a name for this cronjob, that can be presented to the user in the frontend.
*
* @return A german name of this cronjob.
*/
String getDisplayName();
}
@Component
class Reorder implements ICronjob {
@Override
public String getDisplayName() {
return "Nachbestellung";
}
@Override
public Calendar nextExecution(Calendar reference) {
if (reference.get(Calendar.HOUR_OF_DAY) >= 8) {
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 @Component
@RequestMapping("intern/cronjobs") @RequestMapping("intern/cronjobs")
class CronjobController { class CronjobController {
private static final Logger log = LoggerFactory.getLogger(CronjobController.class); private static final Logger log = LoggerFactory.getLogger(CronjobController.class);
private static final Map<String, ICronjob> cronjobs = getCronjobs(); private Map<String, ICronjob> cronjobs;
@Autowired @Autowired
private final BackgroundJobRepository cronjobRepository = null; private final BackgroundJobRepository cronjobRepository = null;
@ -260,17 +54,36 @@ class CronjobController {
@Autowired @Autowired
final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null; final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
final DashboardSummaryRepository dashboardSummaryRepository = null;
@Autowired @Autowired
final SupplierRepository supplierRepository = null; final SupplierRepository supplierRepository = null;
@Autowired @Autowired
final SupplierOrderRepository supplierOrderRepository = null; final SupplierOrderRepository supplierOrderRepository = null;
private static Map<String, ICronjob> getCronjobs() { @Autowired
final Reorder reorderJob = null;
@Autowired
final DashboardCronjob dashboardCronjob = null;
@Autowired
final AutoSupplierPayment autoSupplierPaymentJob = null;
@PostConstruct
public void init() {
cronjobs = getCronjobs();
}
private Map<String, ICronjob> getCronjobs() {
HashMap<String, ICronjob> map = new HashMap<>(); HashMap<String, ICronjob> map = new HashMap<>();
// Register all existing cronjobs // Register all existing cronjobs
map.put(BackgroundJob.JOB_REORDER, new Reorder()); map.put(BackgroundJob.JOB_REORDER, reorderJob);
map.put(BackgroundJob.JOB_DASHBOARD, dashboardCronjob);
map.put(BackgroundJob.JOB_SUPPLIER_AUTO_PAYMENT, autoSupplierPaymentJob);
return Collections.unmodifiableMap(map); return Collections.unmodifiableMap(map);
} }

View File

@ -0,0 +1,100 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.action.warehouse.CalculateWarehouseStatsAction;
import org.hso.ecommerce.entities.dashboard.DashboardSummary;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.user.UserRepository;
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.Component;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class DashboardCronjob implements ICronjob {
@Autowired
private SlotRepository slotRepository = null;
@Autowired
private UserRepository userRepository = null;
@Autowired
private WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
private CustomerOrderRepository customerOrderRepository = null;
@Override
public Calendar nextExecution(Calendar reference) {
reference.add(Calendar.DAY_OF_MONTH, 1);
reference.set(Calendar.HOUR_OF_DAY, 0);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public Calendar previousExecution(Calendar reference) {
reference.set(Calendar.HOUR_OF_DAY, 0);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public void executeAt(Calendar time, CronjobController controller) {
Calendar oneDayBefore = (Calendar) time.clone();
oneDayBefore.add(Calendar.DAY_OF_MONTH, -1);
DashboardSummary dashboardSummary = new DashboardSummary();
List<WarehouseBookingPositionSlotEntry> entries = slotRepository.findAll().stream().map(
s -> warehouseBookingPositionSlotEntryRepository
.getBySlotNum(s.slotNum)
.orElseGet(() -> WarehouseBookingPositionSlotEntry.empty(null, s))
).collect(Collectors.toList());
CalculateWarehouseStatsAction.WarehouseStats warehouseStats = new CalculateWarehouseStatsAction(entries).finish();
dashboardSummary.created = new java.sql.Date(time.getTimeInMillis());
dashboardSummary.todaysCustomersOrders = nullToZero(getSales(oneDayBefore, time));
dashboardSummary.todaysNewCustomers = nullToZero(getNewUsers(oneDayBefore, time));
dashboardSummary.todaysWarehouseCapacity = warehouseStats.efficiency;
dashboardSummary.currentWarehouseCapacity = warehouseStats.ratioUsedSlots;
dashboardSummary.todaysSalesCent = nullToZero(getTurnover(oneDayBefore, time));
controller.dashboardSummaryRepository.save(dashboardSummary);
}
@Override
public String getDisplayName() {
return "Dashboard refresh";
}
private Integer getSales(Calendar begin, Calendar end) {
return customerOrderRepository.countOrdersInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private Integer getTurnover(Calendar begin, Calendar end) {
return customerOrderRepository.countTurnoverInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private Integer getNewUsers(Calendar begin, Calendar end) {
return userRepository.countUsersInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private int nullToZero(Integer input) {
return input == null ? 0 : input;
}
}

View File

@ -0,0 +1,38 @@
package org.hso.ecommerce.controller.cronjob;
import java.util.Calendar;
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);
/**
* Get a name for this cronjob, that can be presented to the user in the frontend.
*
* @return A german name of this cronjob.
*/
String getDisplayName();
}

View File

@ -0,0 +1,164 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.action.booking.CreateBookingAction;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction;
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.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.springframework.stereotype.Component;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
@Component
class Reorder implements ICronjob {
@Override
public String getDisplayName() {
return "Nachbestellung";
}
@Override
public Calendar nextExecution(Calendar reference) {
if (reference.get(Calendar.HOUR_OF_DAY) >= 8) {
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<ReadSupplierDataAction.ArticleIdentifier, ArticleOffer> mapArticleOffers(List<ArticleOffer> articleOffers) {
HashMap<ReadSupplierDataAction.ArticleIdentifier, ArticleOffer> map = new HashMap<>();
for (ArticleOffer articleOffer : articleOffers) {
ReadSupplierDataAction.ArticleIdentifier identifier = new ReadSupplierDataAction.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<ReadSupplierDataAction.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
.getByArticle(article.id)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum();
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);
}
}
}
}

View File

@ -0,0 +1,17 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.entities.cron.BackgroundJob;
import java.util.Calendar;
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;
}
}

View File

@ -0,0 +1,25 @@
package org.hso.ecommerce.controller.intern;
import org.hso.ecommerce.entities.dashboard.DashboardSummary;
import org.hso.ecommerce.repos.dashboard.DashboardSummaryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DashboardController {
@Autowired
private DashboardSummaryRepository dashboardSummaryRepository;
@GetMapping("/intern/dashboardsummary")
public List<DashboardSummary> getDashboardEntries()
{
List<DashboardSummary> inTimespan = dashboardSummaryRepository.findInTimespan(PageRequest.of(0, 7) );
return inTimespan;
}
}

View File

@ -53,7 +53,11 @@ public class InternArticleController {
for (Article article : articleRepository.findAll()) { for (Article article : articleRepository.findAll()) {
UImodelArticles tmp = new UImodelArticles(); UImodelArticles tmp = new UImodelArticles();
tmp.addListedArticle(article, warehouseEntryRepository.getArticleStock(article.id).orElse(0)); tmp.addListedArticle(article, warehouseEntryRepository
.getByArticle(article.id)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum());
totals.add(tmp); totals.add(tmp);
} }
@ -63,10 +67,14 @@ public class InternArticleController {
@GetMapping("/{id}") @GetMapping("/{id}")
public String internListedArticlesId(Model model, @PathVariable String id) { public String internListedArticlesId(Model model, @PathVariable String id) {
int articleid = Integer.parseInt(id); long articleId = Long.parseLong(id);
UImodelArticle total = new UImodelArticle(); UImodelArticle total = new UImodelArticle();
total.addArticle(articleRepository.findArticleById(articleid), total.addArticle(
warehouseEntryRepository.getArticleStock(articleid).orElse(0)); articleRepository.findById(articleId).get(),
warehouseEntryRepository.getByArticle(articleId)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum());
model.addAttribute("ArticleID", total); model.addAttribute("ArticleID", total);
return "intern/listedArticles/id"; return "intern/listedArticles/id";
@ -112,11 +120,17 @@ public class InternArticleController {
@PostMapping("/addArticle/{id}") @PostMapping("/addArticle/{id}")
public RedirectView addArticle(@PathVariable(required = true) String id) { public RedirectView addArticle(@PathVariable(required = true) String id) {
// article is not already listed, create new one // article is not already listed, create new one
int offeredArticleID = Integer.parseInt(id); long offeredArticleID = Long.parseLong(id);
Article tmpArticle = new Article(); Article tmpArticle = new Article();
ArticleOffer offeredArticle = offersRepository.findOfferedArticleById(offeredArticleID); ArticleOffer offeredArticle = offersRepository.findById(offeredArticleID).get();
// Check for duplicates
Optional<Article> related = articleRepository.findArticleByArticleOffer(offeredArticle);
if (related.isPresent()) {
return new RedirectView("../" + related.get().id);
}
// set default values // set default values
tmpArticle.description = ""; tmpArticle.description = "";

View File

@ -21,7 +21,7 @@ public class WarehouseController {
Model model, Model model,
HttpServletRequest request HttpServletRequest request
) { ) {
model.addAttribute("bookings", warehouseBookingRepository.findAll()); model.addAttribute("bookings", warehouseBookingRepository.findAllDesc());
return "intern/warehouse/index"; return "intern/warehouse/index";
} }
} }

View File

@ -1,9 +1,5 @@
package org.hso.ecommerce.controller.intern.accounting; 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.Booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry; import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.BookingReason; import org.hso.ecommerce.entities.booking.BookingReason;
@ -13,6 +9,10 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
@Controller @Controller
public class AccountingController { public class AccountingController {
@ -153,7 +153,9 @@ public class AccountingController {
} else if (reason.customerPayment != null) { } else if (reason.customerPayment != null) {
return "Bezahlung mit Kreditkarte " + reason.customerPayment.payment.creditCardNumber; return "Bezahlung mit Kreditkarte " + reason.customerPayment.payment.creditCardNumber;
} else if (reason.supplierOrder != null) { } else if (reason.supplierOrder != null) {
return "Lieferanten-Bestellung " + reason.supplierOrder.id; return "Lieferanten-Bestellung " + reason.supplierOrder.supplier.name;
} else if (reason.supplierPayment != null) {
return "Lieferanten-Zahlung " + reason.supplierPayment.name;
} else { } else {
return "-"; return "-";
} }
@ -162,6 +164,10 @@ public class AccountingController {
private String linkToReference(BookingReason reason) { private String linkToReference(BookingReason reason) {
if (reason.customerOrder != null) { if (reason.customerOrder != null) {
return "/intern/customerOrders/" + reason.customerOrder.id; return "/intern/customerOrders/" + reason.customerOrder.id;
} else if (reason.supplierPayment != null) {
return "/intern/suppliers/#q=" + reason.supplierPayment.id;
} else if (reason.supplierOrder != null) {
return "/intern/supplierOrders/#q=" + reason.supplierOrder.id;
} else { } else {
return null; return null;
} }

View File

@ -1,8 +1,71 @@
package org.hso.ecommerce.controller.intern.customers; package org.hso.ecommerce.controller.intern.customers;
import org.hso.ecommerce.action.user.CreateDeliveryData;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; 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;
import java.util.List;
import java.util.stream.Collectors;
@Controller @Controller
//@RequestMapping("...") @RequestMapping("intern/customerOrders")
public class CustomerOrderController { public class CustomerOrderController {
@Autowired
private final CustomerOrderRepository customerOrderRepository = null;
@Autowired
private final RestServiceForDelivery restServiceForDelivery = null;
@GetMapping("")
public String internCustomerOrder(Model model) {
List<CustomerOrder> orders = customerOrderRepository.getAllOrders();
List<CustomerOrderDelivery> customerOrderDeliveryDataMap = orders
.stream()
.map(o -> new CustomerOrderDelivery(o, CreateDeliveryData.getDeliveryDataFromCustomerOrder(o, customerOrderRepository, restServiceForDelivery)))
.collect(Collectors.toList());
model.addAttribute("orderDeliveryDataMap", customerOrderDeliveryDataMap);
return "intern/customerOrders/index";
}
static class CustomerOrderDelivery {
private CustomerOrder customerOrder;
private DeliveryData deliveryData;
public CustomerOrderDelivery(CustomerOrder customerOrder, DeliveryData deliveryData) {
this.customerOrder = customerOrder;
this.deliveryData = deliveryData;
}
public CustomerOrder getCustomerOrder() {
return customerOrder;
}
public DeliveryData getDeliveryData() {
return deliveryData;
}
}
@GetMapping("/{id}")
public String internCustomerOrdersId(Model model,
@PathVariable("id") String id
) {
CustomerOrder order = customerOrderRepository.findById(Long.parseLong(id)).get();
DeliveryData deliveryData = CreateDeliveryData.getDeliveryDataFromCustomerOrder(order, customerOrderRepository, restServiceForDelivery);
model.addAttribute("order", order);
model.addAttribute("deliveryData", deliveryData);
return "intern/customerOrders/id";
}
} }

View File

@ -1,9 +1,5 @@
package org.hso.ecommerce.controller.intern.customers; 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;
import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult; import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult;
import org.hso.ecommerce.entities.booking.Booking; import org.hso.ecommerce.entities.booking.Booking;
@ -13,12 +9,13 @@ import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository; import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.user.UserRepository; import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional; import java.util.Optional;
@Controller @Controller
@ -104,17 +101,18 @@ public class CustomersIndexController {
) { ) {
if (!password.equals(password2)) { if (!password.equals(password2)) {
request.setAttribute("error", "Passwörter stimmen nicht überein!"); request.setAttribute("error", "Passwörter stimmen nicht überein!");
return "/intern/customers/id"; return "intern/customers/id";
} }
User user = userRepository.findById(id).get(); User user = userRepository.findById(id).get();
if (!user.validatePassword(password)) { if (!user.validatePassword(password)) {
request.setAttribute("error", "Die Passwörter stimmen nicht mit dem Original überein!"); request.setAttribute("error", "Die Passwörter stimmen nicht mit dem Original überein!");
return "/intern/customers/id"; return "intern/customers/id";
} }
user.setPassword("12345"); user.setPassword("12345");
userRepository.save(user); userRepository.save(user);
request.setAttribute("info", "Passwort wurde auf 12345 geändert!"); request.setAttribute("info", "Passwort wurde auf 12345 geändert!");
return "/intern/customers/id"; return "intern/customers/id";
} }
} }

View File

@ -20,7 +20,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -48,7 +47,7 @@ public class SupplierOrderController {
List<UImodelSupplierOrder> totals = new ArrayList<UImodelSupplierOrder>(); List<UImodelSupplierOrder> totals = new ArrayList<UImodelSupplierOrder>();
for (SupplierOrder order : supplierOrderRepository.findAll()) { for (SupplierOrder order : supplierOrderRepository.findAllDesc()) {
final Article article = articleRepository.findArticleByArticleOffer(order.ordered).orElse(null); final Article article = articleRepository.findArticleByArticleOffer(order.ordered).orElse(null);
totals.add(new UImodelSupplierOrder(order, article)); totals.add(new UImodelSupplierOrder(order, article));
} }
@ -115,6 +114,10 @@ public class SupplierOrderController {
public String quantity; public String quantity;
public String priceTotal; public String priceTotal;
public boolean arrived; public boolean arrived;
public String carrier;
public String trackingId;
public String estimatedArrival;
public UImodelSupplierOrder(SupplierOrder order, Article article) { public UImodelSupplierOrder(SupplierOrder order, Article article) {
this.id = order.id; this.id = order.id;
@ -125,9 +128,13 @@ public class SupplierOrderController {
this.quantity = String.valueOf(order.numberOfUnits); this.quantity = String.valueOf(order.numberOfUnits);
this.priceTotal = String.format("%.2f", ((float) order.totalPriceNet / 100)); this.priceTotal = String.format("%.2f", ((float) order.totalPriceNet / 100));
Date date = new Date(); this.carrier = order.carrier != null ? order.carrier : " - ";
date.setTime(order.created.getTime()); this.trackingId = order.trackingId != null ? order.trackingId : " - ";
this.dateOrder = new SimpleDateFormat("dd.MM.yyyy").format(date); this.estimatedArrival = order.estimatedArrival != null
? new SimpleDateFormat("yyyy.MM.dd HH:00").format(order.estimatedArrival) + " Uhr"
: " - ";
this.dateOrder = new SimpleDateFormat("yyyy.MM.dd").format(order.created);
arrived = order.delivered != null; arrived = order.delivered != null;
} }

View File

@ -1,6 +1,7 @@
package org.hso.ecommerce.controller.intern.warehouse; package org.hso.ecommerce.controller.intern.warehouse;
import org.hso.ecommerce.action.shop.EnableTrackingAction; import org.hso.ecommerce.action.shop.EnableTrackingAction;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.warehouse.WarehouseBooking; import org.hso.ecommerce.entities.warehouse.WarehouseBooking;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -10,6 +11,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.ResourceAccessException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -22,6 +24,9 @@ public class TodoController {
@Autowired @Autowired
private final WarehouseBookingRepository warehouseBookingRepository = null; private final WarehouseBookingRepository warehouseBookingRepository = null;
@Autowired
private final RestServiceForDelivery deliveryService = null;
@GetMapping("todo") @GetMapping("todo")
public String accountingWarehouseTodo( public String accountingWarehouseTodo(
Model model Model model
@ -75,7 +80,14 @@ public class TodoController {
// Update Delivery Date // Update Delivery Date
if (booking.get().reason.customerOrder != null) { if (booking.get().reason.customerOrder != null) {
EnableTrackingAction.addTrackingInfo(booking.get().reason.customerOrder); try{
EnableTrackingAction.addTrackingInfo(deliveryService, booking.get().reason.customerOrder);
}
catch(ResourceAccessException e)
{
return "error/500";
}
} }
warehouseBookingRepository.save(booking.get()); warehouseBookingRepository.save(booking.get());

View File

@ -52,7 +52,11 @@ public class ShopArticleController {
} }
model.addAttribute("article", article); model.addAttribute("article", article);
if (warehouseBookingPositionSlotEntryRepository.getByArticle(id).get(0).newSumSlot > 0) { //check if in Stock if (warehouseBookingPositionSlotEntryRepository
.getByArticle(id)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum() > 0) { //check if in Stock
model.addAttribute("inStock", true); model.addAttribute("inStock", true);
} else { } else {
model.addAttribute("inStock", false); model.addAttribute("inStock", false);

View File

@ -109,7 +109,7 @@ public class ShopCheckoutController {
) { ) {
if (shoppingCart.getRevision() != cartRevision) { if (shoppingCart.getRevision() != cartRevision) {
request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher die Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut."); request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher konnte der Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut.");
response.setStatus(HttpServletResponse.SC_CONFLICT); response.setStatus(HttpServletResponse.SC_CONFLICT);
return "shop/checkout"; return "shop/checkout";
} }

View File

@ -7,7 +7,9 @@ import org.hso.ecommerce.repos.shop.CategoryRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -45,6 +47,9 @@ public class ShopSearchController {
return "error/404"; return "error/404";
} }
return "/shop/search"; // Show term in search box
model.addAttribute("searchterm", term != null ? term : "");
return "shop/search";
} }
} }

View File

@ -1,6 +1,7 @@
package org.hso.ecommerce.entities.booking; package org.hso.ecommerce.entities.booking;
import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.supplier.Supplier;
import org.hso.ecommerce.entities.supplier.SupplierOrder; import org.hso.ecommerce.entities.supplier.SupplierOrder;
import javax.persistence.*; import javax.persistence.*;
@ -28,6 +29,9 @@ public class BookingReason {
@ManyToOne(optional = true) @ManyToOne(optional = true)
public SupplierOrder supplierOrder; public SupplierOrder supplierOrder;
@ManyToOne(optional = true)
public Supplier supplierPayment;
// Default Constructor is needed for construction by ORM // Default Constructor is needed for construction by ORM
public BookingReason() { public BookingReason() {
} }
@ -40,6 +44,10 @@ public class BookingReason {
this.customerPayment = customerPayment; this.customerPayment = customerPayment;
} }
public BookingReason(Supplier supplierPayment) {
this.supplierPayment = supplierPayment;
}
public BookingReason(SupplierOrder supplierOrder) { public BookingReason(SupplierOrder supplierOrder) {
this.supplierOrder = supplierOrder; this.supplierOrder = supplierOrder;
} }

View File

@ -9,6 +9,7 @@ public class BackgroundJob {
public static final String JOB_DASHBOARD = "Dashboard"; public static final String JOB_DASHBOARD = "Dashboard";
public static final String JOB_REORDER = "SupplierOrder"; public static final String JOB_REORDER = "SupplierOrder";
public static final String JOB_SUPPLIER_AUTO_PAYMENT = "SupplierAutoPayment";
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)

View File

@ -15,11 +15,18 @@ public class DashboardSummary {
@NotNull @NotNull
public java.sql.Date created; public java.sql.Date created;
public int todaysCustomers; @NotNull
public int todaysCustomersOrders; public Integer todaysNewCustomers;
public int todaysSuppliersOrders;
public int todaysItemsSold; @NotNull
public int todaysSalesCent; public Integer todaysCustomersOrders;
public int totalWarehouseCapacity;
public int currentWarehouseCapacity; @NotNull
public Integer todaysSalesCent;
@NotNull
public Double currentWarehouseCapacity;
@NotNull
public Double todaysWarehouseCapacity;
} }

View File

@ -56,9 +56,4 @@ public class CustomerOrder {
public String formatDeliveredAt(){ public String formatDeliveredAt(){
return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt); return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt);
} }
public String getEstimatedArrival() {
//TODO: get estimated arrival from api
return "TODO TODO TODO";
}
} }

View File

@ -28,6 +28,15 @@ public class SupplierOrder {
// Includes discounts // Includes discounts
public int totalPriceNet; public int totalPriceNet;
@Column(nullable = true)
public String carrier;
@Column(nullable = true)
public String trackingId;
@Column(nullable = true)
public Timestamp estimatedArrival;
@Column(nullable = true) @Column(nullable = true)
public Timestamp delivered; public Timestamp delivered;

View File

@ -24,9 +24,6 @@ public class User {
@Column(unique = true) @Column(unique = true)
public String email; public String email;
@Column(insertable = false, updatable = false)
public String name;
public String salutation; public String salutation;
public String passwordHash; public String passwordHash;

View File

@ -1,34 +0,0 @@
package org.hso.ecommerce.entities.warehouse;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import javax.persistence.*;
@Entity
@Table(name = "warehouse_booking_reasons")
public class WarehouseBookingReason {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic
public long id;
public String comment;
@ManyToOne(optional = true)
public SupplierOrder causeSupplierOrder;
@ManyToOne(optional = true)
public CustomerOrder customerOrder;
public boolean isManuel;
// Default Constructor is needed for construction by ORM
public WarehouseBookingReason() {
}
public WarehouseBookingReason(CustomerOrder order) {
this.customerOrder = order;
}
}

View File

@ -0,0 +1,20 @@
package org.hso.ecommerce.repos.dashboard;
import org.hso.ecommerce.entities.dashboard.DashboardSummary;
import org.springframework.data.domain.Pageable;
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 DashboardSummaryRepository extends JpaRepository<DashboardSummary, Long> {
@Query("SELECT ds FROM DashboardSummary ds ORDER BY ds.id DESC")
List<DashboardSummary> findInTimespan(
Pageable pageable
);
}

View File

@ -23,13 +23,13 @@ public interface ArticleRepository extends JpaRepository<Article, Long> {
@Query("SELECT a FROM Article a") @Query("SELECT a FROM Article a")
List<Article> findAll(); 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) @Query(value = "SELECT DISTINCT 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(); 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") @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(); 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") @Query("SELECT DISTINCT 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); List<Article> getOrderedArticles(long customerId);
/*** /***
@ -48,6 +48,6 @@ public interface ArticleRepository extends JpaRepository<Article, Long> {
@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) @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); 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) @Query(value = "Select distinct 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); List<Article> getArticlesByCategory(String category);
} }

View File

@ -15,7 +15,20 @@ public interface CustomerOrderRepository extends JpaRepository<CustomerOrder, Lo
long articleId, java.sql.Timestamp begin, java.sql.Timestamp end long articleId, java.sql.Timestamp begin, java.sql.Timestamp end
); );
@Query("SELECT co FROM CustomerOrder co ORDER BY co.id DESC")
List<CustomerOrder> getAllOrders();
@Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId ORDER BY co.id DESC") @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId ORDER BY co.id DESC")
List<CustomerOrder> getOrdersByUserId(long userId); List<CustomerOrder> getOrdersByUserId(long userId);
@Query("SELECT COUNT(co.id) FROM CustomerOrder co WHERE co.created >= :begin AND co.created < :end")
Integer countOrdersInTimespan(
java.sql.Timestamp begin, java.sql.Timestamp end
);
@Query("SELECT SUM(co.totalGrossCent) FROM CustomerOrder co WHERE co.created >= :begin AND co.created < :end")
Integer countTurnoverInTimespan(
java.sql.Timestamp begin, java.sql.Timestamp end
);
} }

View File

@ -1,22 +1,25 @@
package org.hso.ecommerce.repos.supplier; package org.hso.ecommerce.repos.supplier;
import java.util.List;
import org.hso.ecommerce.entities.supplier.SupplierOrder; import org.hso.ecommerce.entities.supplier.SupplierOrder;
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.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.List;
@Repository @Repository
public interface SupplierOrderRepository extends JpaRepository<SupplierOrder, Long> { 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") @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); Integer countUndeliveredReorders(String articleNumber);
@Query(value = "SELECT * FROM supplier_orders as a WHERE a.supplier_id = :supplierId", nativeQuery = true) @Query(value = "SELECT * FROM supplier_orders as a WHERE a.supplier_id = :supplierId ORDER BY a.id DESC", nativeQuery = true)
List<SupplierOrder> findOrderBySupplierID(@Param("supplierId") long supplierId); List<SupplierOrder> findOrderBySupplierID(@Param("supplierId") long supplierId);
@Query("SELECT a FROM SupplierOrder a") @Query("SELECT a FROM SupplierOrder a")
List<SupplierOrder> findAll(); List<SupplierOrder> findAll();
@Query("SELECT a FROM SupplierOrder a ORDER BY a.id DESC")
List<SupplierOrder> findAllDesc();
} }

View File

@ -1,13 +1,14 @@
package org.hso.ecommerce.repos.supplier; package org.hso.ecommerce.repos.supplier;
import java.util.List;
import org.hso.ecommerce.entities.supplier.Supplier; import org.hso.ecommerce.entities.supplier.Supplier;
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.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository @Repository
public interface SupplierRepository extends JpaRepository<Supplier, Long> { public interface SupplierRepository extends JpaRepository<Supplier, Long> {
@ -16,4 +17,7 @@ public interface SupplierRepository extends JpaRepository<Supplier, Long> {
@Query("SELECT a FROM Supplier a WHERE a.id = :supplierId") @Query("SELECT a FROM Supplier a WHERE a.id = :supplierId")
Supplier findSupplierById(@Param("supplierId") long supplierId); Supplier findSupplierById(@Param("supplierId") long supplierId);
@Query("SELECT a FROM Supplier a WHERE a.uuid = :uuid")
Optional<Supplier> findByUuid(@Param("uuid") String uuid);
} }

View File

@ -13,6 +13,11 @@ public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT c FROM User c WHERE c.email = :email") @Query("SELECT c FROM User c WHERE c.email = :email")
Optional<User> findByEmail(String email); Optional<User> findByEmail(String email);
@Query("SELECT COUNT(c.id) FROM User c WHERE c.created >= :begin AND c.created < :end AND c.isEmployee = false")
Integer countUsersInTimespan(
java.sql.Timestamp begin, java.sql.Timestamp end
);
@Query("SELECT count(*) FROM User WHERE isEmployee = true") @Query("SELECT count(*) FROM User WHERE isEmployee = true")
Optional<Integer> numberOfEmployees(); Optional<Integer> numberOfEmployees();
} }

View File

@ -11,13 +11,10 @@ import java.util.Optional;
@Repository @Repository
public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository<WarehouseBookingPositionSlotEntry, Long> { public interface WarehouseBookingPositionSlotEntryRepository extends JpaRepository<WarehouseBookingPositionSlotEntry, Long> {
@Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true) @Query(value = "Select e.* from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND e.article_id = :article GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true)
List<WarehouseBookingPositionSlotEntry> getByArticle(long article); List<WarehouseBookingPositionSlotEntry> getByArticle(long article);
@Query(value = "SELECT SUM(w.new_sum_slot) FROM warehouse_booking_position_entries as w WHERE w.article_id = :articleid", nativeQuery = true) @Query(value = "Select e.* from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND s.slot_num = :slotnum GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true)
Optional<Integer> getArticleStock(long articleid);
@Query(value = "Select e.id, e.article_id, e.new_sum_slot, e.slot_id from warehouse_booking_position_entries as e, warehouse_slots as s where e.slot_id = s.id AND s.slot_num = :slotnum GROUP BY s.slot_num HAVING max(e.id)", nativeQuery = true)
Optional<WarehouseBookingPositionSlotEntry> getBySlotNum(long slotnum); Optional<WarehouseBookingPositionSlotEntry> getBySlotNum(long slotnum);
} }

View File

@ -13,5 +13,8 @@ public interface WarehouseBookingRepository extends JpaRepository<WarehouseBooki
@Query("Select b FROM WarehouseBooking b WHERE b.isDone = 0") @Query("Select b FROM WarehouseBooking b WHERE b.isDone = 0")
List<WarehouseBooking> findNotDone(); List<WarehouseBooking> findNotDone();
@Query("Select b FROM WarehouseBooking b ORDER BY b.id DESC")
List<WarehouseBooking> findAllDesc();
} }

View File

@ -0,0 +1,57 @@
package org.hso.ecommerce.uimodel;
import com.fasterxml.jackson.annotation.JsonCreator;
public class DeliveryData
{
private final String status;
private final DeliveryDataEnum deliveryDataEnum;
private final String estimatedArrival;
private boolean isDelivered;
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public DeliveryData(String status, String estimatedArrival) {
this.status = status;
this.estimatedArrival = estimatedArrival;
this.deliveryDataEnum = DeliveryDataEnum.OK;
isDelivered = status.equals("Lieferung erfolgreich");
}
public DeliveryData(String status, String estimatedArrival, DeliveryDataEnum deliveryDataEnum) {
this.status = status;
this.estimatedArrival = estimatedArrival;
this.deliveryDataEnum = deliveryDataEnum;
isDelivered = status.equals("Lieferung erfolgreich");
}
public boolean isDelivered() {
return isDelivered;
}
public String getStatus() {
return status;
}
public String getEstimatedArrival() {
return estimatedArrival;
}
public boolean allOk() {
return deliveryDataEnum == DeliveryDataEnum.OK;
}
public boolean noTrackingID() {
return deliveryDataEnum == DeliveryDataEnum.NO_TRACKING_ID;
}
public boolean noData() {
return deliveryDataEnum == DeliveryDataEnum.NO_DATA;
}
}

View File

@ -0,0 +1,7 @@
package org.hso.ecommerce.uimodel;
public enum DeliveryDataEnum {
OK,
NO_TRACKING_ID,
NO_DATA
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -17,7 +17,7 @@
<li><a href="/login">Login</a></li> <li><a href="/login">Login</a></li>
<li><a href="/">Angebote</a></li> <li><a href="/">Angebote</a></li>
<li><a href="/shop/search">Suche</a></li> <li><a href="/shop/search">Suche</a></li>
<li><a href="/intern/">Mitarbeiter Bereich</a></li> <li><a href="/intern/" th:if="${user != null && user.isEmployee}">Mitarbeiter Bereich</a></li>
</ul> </ul>
</div> </div>
<div> <div>

View File

@ -11,7 +11,8 @@
<div class='content-width bar-flex'> <div class='content-width bar-flex'>
<a class="button no-padding" href="/"><img class="logo" th:src="@{/img/ecom-logo-base.svg}"></a> <a class="button no-padding" href="/"><img class="logo" th:src="@{/img/ecom-logo-base.svg}"></a>
<form class='spacer input-icon secondary' th:action="@{/shop/search}" method="GET"> <form class='spacer input-icon secondary' th:action="@{/shop/search}" method="GET">
<input type="text" name="term" placeholder="Nach Produkten suchen..."/> <input type="text" name="term" placeholder="Nach Produkten suchen..."
th:value="${searchterm != null ? searchterm : ''}"/>
<button>Finden</button> <button>Finden</button>
</form> </form>
<a th:unless="${user}" class="button" th:href="@{/login}">Login</a> <a th:unless="${user}" class="button" th:href="@{/login}">Login</a>
@ -25,7 +26,10 @@
</form> </form>
</div> </div>
</div> </div>
<a class="button" th:href="@{/shop/checkout}">Warenkorb (<span th:text="${shoppingCart.itemCount}"></span>)</a> <a class="button" th:href="@{/shop/checkout}">Warenkorb
<th:block th:if="${shoppingCart.itemCount > 0}"> (<span th:text="${shoppingCart.itemCount}"></span>)
</th:block>
</a>
</div> </div>
<div th:if="${error}" class="error" id="error-msg"> <div th:if="${error}" class="error" id="error-msg">
<div class="content-width bar-flex"> <div class="content-width bar-flex">

View File

@ -93,7 +93,8 @@
<fieldset> <fieldset>
<input type="radio" id="r-manual" th:field="*{reason}" value="Manual"> <input type="radio" id="r-manual" th:field="*{reason}" value="Manual">
<label for="r-manual">Manuell</label> <label for="r-manual">Manuell</label>
<input placeholder="Grund" class="full-width" type="text" th:field="*{reasonText}" value=""/> <input placeholder="Grund" class="full-width" type="text" th:field="*{reasonText}" value=""
required="required"/>
</fieldset> </fieldset>
</fieldset> </fieldset>
</div> </div>

View File

@ -15,7 +15,7 @@
<div class="sidebar-layout content-width"> <div class="sidebar-layout content-width">
<nav></nav> <nav></nav>
<div> <div>
<h1>Bestellung 1010</h1> <h1 th:text="|Bestellung ${order.id}|"></h1>
<script th:src="@{/js/back.js}"></script> <script th:src="@{/js/back.js}"></script>
<div class="back" data-group="intern" data-insert="true"></div> <div class="back" data-group="intern" data-insert="true"></div>
@ -25,33 +25,32 @@
<nav th:replace="fragments/intern :: sidebar"></nav> <nav th:replace="fragments/intern :: sidebar"></nav>
<div class="content-width"> <div class="content-width">
<div> <div>
<h2 id="20202701"> Bestellung vom 27.01.2020 </h2> <h2 th:text="| Bestellung vom ${order.created.toString().substring(0,10)}|"></h2>
<div> <div>
<table class="key-value"> <table class="key-value">
<tr> <tr>
<th>Nutzer</th> <th>Nutzer</th>
<td><a th:href="@{/intern/customers/498}">K-4850</a></td> <td><a th:href="@{/intern/customers/{id}(id=${order.customer.id})}" th:text="${order.customer.id}"></a></td>
</tr> </tr>
<tr> <tr>
<th>Lieferstatus</th> <th>Lieferstatus</th>
<td><b>Unterwegs</b> <br/> Vorraussichtliche Ankunft: 29.01.2020</td> <td th:if="${deliveryData.allOk()}"><span th:text="${deliveryData.getStatus()}" /></td>
<td th:if="${deliveryData.noTrackingID()}">Bestellung wurde elektronisch angekündigt</td>
<td th:if="${deliveryData.noData()}">DHL-Server ist gerade nicht erreichbar</td>
</tr>
<tr>
<td></td>
<td th:if="${order.deliveredAt == null && order.trackingId!=null && !deliveryData.noData()}">Vorraussichtliche Ankunft: <span th:text="${deliveryData.getEstimatedArrival()}" /></td>
<td th:if="${order.deliveredAt == null && order.trackingId!=null && deliveryData.noData()}">Vorraussichtliche Ankunft: --:--:---- </td>
<td th:if="${order.deliveredAt != null && order.trackingId!=null}"><b>Angekommen</b> Ankunft: <span th:text="${deliveryData.getEstimatedArrival()}" /></td>
</tr> </tr>
<tr> <tr>
<th>Sendeverfolgungsnummer</th> <th>Sendeverfolgungsnummer</th>
<td>XE51451436DE</td> <td th:text="${order.trackingId!=null} ? ${order.trackingId} : 'Es wurde noch keine Sendungsnummer vergeben'"></td>
</tr> </tr>
<tr> <tr>
<th></th> <th></th>
<td> <td th:text="${order.destination.toString()}"></td>
Hans Maier <br/>
Hauptstraße 12<br/>
74880 Musterstadt<br/>
Deutschland <br/>
</td>
</tr>
<tr>
<th>Eingelösste Bonuspunkte</th>
<td>10</td>
</tr> </tr>
</table> </table>
</div> </div>
@ -62,18 +61,11 @@
<th>Menge</th> <th>Menge</th>
<th>Preis (Brutto)</th> <th>Preis (Brutto)</th>
</tr> </tr>
<tr> <tr th:each="position: ${order.positions}">
<td><a th:href="@{/shop/articles/4151}"><img th:src="@{/img/product-1.jpg}" class="s"/></a></td> <td><a th:href="@{/shop/articles/{id}(id = ${position.article.id})}"><img th:src="@{/shop/articles/{id}/image.jpg(id=${position.article.id})}" class="s"/></a></td>
<td><a th:href="@{/shop/articles/4151}">Kamera</a></td> <td><a th:href="@{/shop/articles/{id}(id = ${position.article.id})}" th:text="${position.article.title}" class="s"></a></td>
<td> 1</td> <td th:text="${position.quantity}" />
<td>100,50&nbsp;EUR</td> <td th:text="${#numbers.formatDecimal(position.getSumPrice() * 0.01, 1, 'POINT', 2, 'COMMA')}" />
</tr>
<tr>
<td><a th:href="@{/shop/articles/4151}"><img th:src="@{/img/product-2.jpg}" class="s"/></a></td>
<td><a th:href="@{/shop/articles/4151}">Earbuds</a></td>
<td> 3</td>
<td>63,95&nbsp;EUR</td>
</tr>
<tr> <tr>
<th></th> <th></th>
<th></th> <th></th>
@ -84,28 +76,22 @@
<td></td> <td></td>
<td></td> <td></td>
<td>Artikel (Netto)</td> <td>Artikel (Netto)</td>
<td> 120,00&nbsp;EUR</td> <td th:text="${#numbers.formatDecimal(order.totalNetCent * 0.01, 1, 'POINT', 2, 'COMMA')}" />
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td></td> <td></td>
<td>Bonuspunkte</td> <td>Umsatzsteuer</td>
<td> 5,00&nbsp;EUR</td> <td th:text="${#numbers.formatDecimal(order.totalVatCent * 0.01, 1, 'POINT', 2, 'COMMA')}" />
</tr>
<tr>
<td></td>
<td></td>
<td>Umsatzsteuer (19%)</td>
<td> 42,00&nbsp;EUR</td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td></td> <td></td>
<td> <td>
<h3>Gesammtpreis</h3> <h3>Gesamtpreis</h3>
</td> </td>
<td> <td>
<h3>240,79&nbsp;EUR</h3> <h3 th:text="${#numbers.formatDecimal(order.totalGrossCent * 0.01, 1, 'POINT', 2, 'COMMA')}"/>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -40,37 +40,17 @@
<th>Status</th> <th>Status</th>
<th></th> <th></th>
</tr> </tr>
<tr> <tr th:each="order: ${orderDeliveryDataMap}">
<td><a th:href="@{/intern/customers/48584}">101</a></td> <td><a th:href="@{/intern/customers/{id}(id=${order.customerOrder.customer.id})}"
<td>440</td> th:text="${order.customerOrder.customer.id}">101</a></td>
<td>2019-12-54</td> <td th:text="${order.customerOrder.id}"></td>
<td>10,13&nbsp;EUR</td> <td th:text="${order.customerOrder.created.toString().substring(0, 10)}"></td>
<td>Zugestellt</td> <td th:text="${#numbers.formatDecimal(order.customerOrder.totalGrossCent * 0.01, 1, 'POINT', 2, 'COMMA')}"></td>
<td><a th:href="@{/intern/customerOrders/48584}" class="button smaller">Details</a></td> <td th:if="${order.deliveryData.allOk()}"><span th:text="${order.deliveryData.getStatus()}"/></td>
</tr> <td th:if="${order.deliveryData.noTrackingID()}">Bestellung wurde elektronisch angekündigt</td>
<tr> <td th:if="${order.deliveryData.noData()}">DHL-Server ist gerade nicht erreichbar</td>
<td><a th:href="@{/intern/customers/48584}">102</a></td> <td><a th:href="@{/intern/customerOrders/{id}(id=${order.customerOrder.id})}" class="button smaller">Details</a>
<td>241</td> </td>
<td>2019-11-10</td>
<td>40,13&nbsp;EUR</td>
<td>In Zustellung</td>
<td><a th:href="@{/intern/customerOrders/48584}" class="button smaller">Details</a></td>
</tr>
<tr>
<td><a th:href="@{/intern/customers/48584}">101</a></td>
<td>241</td>
<td>2019-11-10</td>
<td>10,13&nbsp;EUR</td>
<td>Erfasst</td>
<td><a th:href="@{/intern/customerOrders/48584}" class="button smaller">Details</a></td>
</tr>
<tr>
<td><a th:href="@{/intern/customers/48584}">755</a></td>
<td>544</td>
<td>2019-12-10</td>
<td>45,13&nbsp;EUR</td>
<td>Erfasst</td>
<td><a th:href="@{/intern/customerOrders/48584}" class="button smaller">Details</a></td>
</tr> </tr>
</table> </table>
</p> </p>

View File

@ -23,7 +23,7 @@
<div class="sidebar-layout content-width"> <div class="sidebar-layout content-width">
<nav></nav> <nav></nav>
<div> <div>
<h1 th:text="|Kunde ${user.id}"></h1> <h1 th:text="|Kunde ${user.id}|"></h1>
<script th:src="@{/js/back.js}"></script> <script th:src="@{/js/back.js}"></script>
<div class="back" data-group="intern" data-insert="true"></div> <div class="back" data-group="intern" data-insert="true"></div>
@ -41,7 +41,7 @@
</tr> </tr>
<tr> <tr>
<th>Name</th> <th>Name</th>
<td th:text="${user.name}"></td> <td th:text="${user.defaultDeliveryAddress != null ? user.defaultDeliveryAddress.name : '-'}"></td>
</tr> </tr>
<tr> <tr>
<th>E-Mail</th> <th>E-Mail</th>
@ -56,7 +56,7 @@
</tr> </tr>
<tr> <tr>
<th>Adresse</th> <th>Adresse</th>
<td th:Text="${user.defaultDeliveryAddress}"></td> <td th:Text="${user.defaultDeliveryAddress != null ? user.defaultDeliveryAddress : ''}"></td>
</tr> </tr>
</table> </table>
</p> </p>
@ -91,7 +91,10 @@
<div> <div>
<label for="password">Passwort</label> <label for="password">Passwort</label>
<input class="full-width" type="password" name="password" placeholder="Passwort" id="password" <input class="full-width" type="password" name="password" placeholder="Passwort" id="password"
pattern="(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}"
required> required>
<small>Das Passwort muss mindestens 8 Zeichen enthalten. Es muss aus mindestens einem
Großbuchstaben, einem Kleinbuchstaben und einer Zahl bestehen.</small>
</div> </div>
<div> <div>

View File

@ -42,7 +42,7 @@
<tr th:each="user: ${users}"> <tr th:each="user: ${users}">
<td th:text="${user.id}"></td> <td th:text="${user.id}"></td>
<td th:text="${user.created.toString().substring(0, 10)}"></td> <td th:text="${user.created.toString().substring(0, 10)}"></td>
<td th:text="${user.name}"></td> <td th:text="${user.defaultDeliveryAddress != null ? user.defaultDeliveryAddress.name : ''}"></td>
<td th:text="${user.email}"></td> <td th:text="${user.email}"></td>
<td> <td>
<span th:text="${user.isActive} ? 'Aktiv,' : 'Inaktiv,'"></span> <span th:text="${user.isActive} ? 'Aktiv,' : 'Inaktiv,'"></span>

View File

@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=0.75, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=0.75, user-scalable=no">
<script src="/js/chart.js"></script> <script src="/js/chart.js"></script>
<script> <script>
function chart(id, name, data, unit) { function chart(id, name, data, unit, dates) {
document.addEventListener("DOMContentLoaded", function() {
let elm = document.getElementById(id); let elm = document.getElementById(id);
let cs = getComputedStyle(elm) let cs = getComputedStyle(elm)
let ctx = elm.getContext('2d'); let ctx = elm.getContext('2d');
@ -20,7 +20,7 @@
// The data for our dataset // The data for our dataset
data: { data: {
labels: ['Freitag', 'Donnerstag', 'Mittwoch', 'Dienstag', 'Montag'], labels: dates,
datasets: [{ datasets: [{
label: name, label: name,
backgroundColor: cs.getPropertyValue('--c-primary'), backgroundColor: cs.getPropertyValue('--c-primary'),
@ -61,7 +61,6 @@
} }
} }
}); });
});
} }
</script> </script>
@ -107,7 +106,12 @@
<h3>Verkäufe (Anzahl)</h3> <h3>Verkäufe (Anzahl)</h3>
<canvas id="sales"></canvas> <canvas id="sales"></canvas>
<script> <script>
chart('sales', 'Verkäufe/Tag', [15, 12, 14, 8, 7]) fetch('./dashboardsummary')
.then(response => response.json())
.then(data => {
chart('sales', 'Verkäufe/Tag', data.map(d => d.todaysCustomersOrders),
"", data.map(g => " "+g.created));
});
</script> </script>
</section> </section>
@ -116,7 +120,12 @@
<h3>Verkäufe (Umsatz)</h3> <h3>Verkäufe (Umsatz)</h3>
<canvas id="umsatz"></canvas> <canvas id="umsatz"></canvas>
<script> <script>
chart('umsatz', 'Umsatz/Tag', [1512.45, 1225.15, 1452.54, 814.54, 746.00], "EUR") fetch('./dashboardsummary')
.then(response => response.json())
.then(data => {
chart('umsatz', 'Umsatz/Tag', data.map(d => d.todaysSalesCent / 100),
"€", data.map(g => " "+g.created));
});
</script> </script>
</section> </section>
@ -125,7 +134,12 @@
<h3>Neukunden</h3> <h3>Neukunden</h3>
<canvas id="Neukunden"></canvas> <canvas id="Neukunden"></canvas>
<script> <script>
chart('Neukunden', 'Neukunden', [1, 1, 0, 5, 0], "") fetch('./dashboardsummary')
.then(response => response.json())
.then(data => {
chart('Neukunden', 'Neukunden', data.map(d => d.todaysNewCustomers),
"", data.map(g => " "+g.created));
});
</script> </script>
</section> </section>
@ -139,7 +153,12 @@
<h2>Lagerauslastung</h2> <h2>Lagerauslastung</h2>
<canvas id="warehouse"></canvas> <canvas id="warehouse"></canvas>
<script> <script>
chart('warehouse', 'Lagerauslastung', [50, 30, 20, 80, 60], "%") fetch('./dashboardsummary')
.then(response => response.json())
.then(data => {
chart('warehouse', 'Lagerauslastung', data.map(d => d.currentWarehouseCapacity * 100),
"%", data.map(g => g.created));
});
</script> </script>
</section> </section>
@ -148,7 +167,12 @@
<h2>Lagereffizienz</h2> <h2>Lagereffizienz</h2>
<canvas id="warehouse-ef"></canvas> <canvas id="warehouse-ef"></canvas>
<script> <script>
chart('warehouse-ef', 'Lagereffizienz', [80, 70, 60, 75, 54], "%") fetch('./dashboardsummary')
.then(response => response.json())
.then(data => {
chart('warehouse-ef', 'Lagereffizienz', data.map(d => d.todaysWarehouseCapacity * 100),
"%", data.map(g => g.created));
});
</script> </script>
</section> </section>

View File

@ -28,7 +28,7 @@
<input class=" full-width" type="text" id="title" name="title" required="required" pattern=".+" 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_disabled">Refernzierter Artikel</label> <label for="ref_disabled">Referenzierter Artikel</label>
<input class="" type="text" id="ref_disabled" th:value="${ArticleID.offerID}" disabled/> <input class="" type="text" id="ref_disabled" th:value="${ArticleID.offerID}" disabled/>
<input type="hidden" id="ref_hidden" th:value="${ArticleID.offerID}" name="ref-article" /> <input type="hidden" id="ref_hidden" th:value="${ArticleID.offerID}" name="ref-article" />
@ -38,7 +38,7 @@
<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="img" name="img"/> <input class="full-width" type="file" id="img" name="img"/>
</p> </p>
<p> <p>
@ -88,7 +88,7 @@
</p> </p>
<p> <p>
Der Wert wird nur für zukünftige Lagerbuchungen verwendet. Der Wert wird nur für zukünftige Lagerbuchungen verwendet.
Bei Problemen kann können Einheiten aus- und wieder eingebucht werden. Bei Problemen können Einheiten aus- und wieder eingebucht werden.
<!-- TODO: set link g--> <!-- TODO: set link g-->
</p> </p>
<p> <p>

View File

@ -47,7 +47,8 @@
<td><span th:text="${order.id}"></span></td> <td><span th:text="${order.id}"></span></td>
<td><span th:text="${order.dateOrder}"></span></td> <td><span th:text="${order.dateOrder}"></span></td>
<td><span th:text="${order.supplierName}"></span></td> <td><span th:text="${order.supplierName}"></span></td>
<td><a th:href="@{/intern/articles/{id}(id = ${order.articleId})}" class="button smaller" th:text="${order.articleName}"></a></td> <td><a th:href="@{/intern/articles/{id}(id = ${order.articleId})}" class="smaller"
th:text="${order.articleName}"></a></td>
<td><span th:text="${order.priceNet}"></span></td> <td><span th:text="${order.priceNet}"></span></td>
<td><span th:text="${order.quantity}"></span></td> <td><span th:text="${order.quantity}"></span></td>
<td><span th:text="${order.priceTotal}"></span></td> <td><span th:text="${order.priceTotal}"></span></td>
@ -57,10 +58,13 @@
</div> </div>
<!-- ELSE --> <!-- ELSE -->
<div th:unless="${order.arrived}"> <div th:unless="${order.arrived}">
Unterwegs <br> Unterwegs: <span th:text="${order.carrier}"></span>, <span
<form class="detailgrid" action="#" th:action="@{/intern/supplierOrders/store/{id}(id = ${order.id})}" method="POST"> th:text="${order.trackingId}"></span><br/>
<input class="button smaller" type="submit" value="Eingang verbuchen" /> <form class="detailgrid" action="#"
th:action="@{/intern/supplierOrders/store/{id}(id = ${order.id})}" method="POST">
<input class="button smaller" type="submit" value="Eingang verbuchen"/>
</form> </form>
Geschätzte Ankunft: <span th:text="${order.estimatedArrival}"></span>.
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -34,7 +34,7 @@
</div> </div>
<div class="l"> <div class="l">
<label for="reason">Grund:</label> <label for="reason">Grund:</label>
<input placeholder="Grund" class="full-width" type="text" name="reason" value=""/> <input placeholder="Grund" class="full-width" type="text" name="reason" value="" required="required"/>
</div> </div>
<div class="s"> <div class="s">

View File

@ -24,6 +24,10 @@
<main class="sidebar-layout content-width"> <main class="sidebar-layout content-width">
<nav th:replace="fragments/intern :: sidebar"></nav> <nav th:replace="fragments/intern :: sidebar"></nav>
<div class="content-width"> <div class="content-width">
<p>
Es wird der Lagerstand angezeigt, der entsteht nachdem alle austehenden Lagerbuchungen auf der Packliste
angearbeitet wurden.
</p>
<!-- Dirty --> <!-- Dirty -->
<div style="min-width: 10rem; display: inline-block; margin: var(--u0);"> <div style="min-width: 10rem; display: inline-block; margin: var(--u0);">
<h3>Plätze in Verwendung</h3> <h3>Plätze in Verwendung</h3>

View File

@ -23,7 +23,10 @@
</div> </div>
<div> <div>
<label for="password">Passwort</label> <label for="password">Passwort</label>
<input class="full-width" type="password" name="password" placeholder="Passwort" id="password" required> <input class="full-width" type="password" name="password" placeholder="Passwort" id="password"
pattern="(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}" required>
<small>Das Passwort muss mindestens 8 Zeichen enthalten. Es muss aus mindestens einem Großbuchstaben,
einem Kleinbuchstaben und einer Zahl bestehen.</small>
</div> </div>
<div> <div>
<label for="password2">Passwort wiederholen</label> <label for="password2">Passwort wiederholen</label>
@ -55,7 +58,7 @@
placeholder="Optional: Zusatz&#10;Optional: Unternehmen&#10;Straße Hausnummer&#10;Postleitzeit Ort&#10;Land"></textarea> placeholder="Optional: Zusatz&#10;Optional: Unternehmen&#10;Straße Hausnummer&#10;Postleitzeit Ort&#10;Land"></textarea>
</div> </div>
<div> <div>
<button class="full-width" type="submit" name="action" value="login">Registeren</button> <button class="full-width" type="submit" name="action" value="login">Registrieren</button>
<a th:href="@{/terms}"> <a th:href="@{/terms}">
Unsere AGBs finden sie hier. Unsere AGBs finden sie hier.
</a> </a>

View File

@ -135,7 +135,7 @@ Musterstraße 4
<button class=" no-margin secondary full-width">jetzt kostenpflichtig bestellen</button> <button class=" no-margin secondary full-width">jetzt kostenpflichtig bestellen</button>
</div> </div>
<div th:unless="${user}"> <div th:unless="${user}">
<a th:href="@{/login}" class="button secondary no-margin full-width">Einloggen und forfahren.</a> <a th:href="@{/login}" class="button secondary no-margin full-width">Einloggen und fortfahren.</a>
</div> </div>
</div> </div>
</form> </form>

View File

@ -52,7 +52,7 @@
<p> Jetzt Kunde werden und viele Vorteile sichern, <p> Jetzt Kunde werden und viele Vorteile sichern,
wie z.B. personalisierte Empfehlungen. </p> wie z.B. personalisierte Empfehlungen. </p>
<p> <p>
<a class="button" th:href="@{/register}">Registieren</a> <a class="button" th:href="@{/register}">Registrieren</a>
</p> </p>
</div> </div>
</div> </div>

View File

@ -21,25 +21,42 @@
<main class="sidebar-layout content-width"> <main class="sidebar-layout content-width">
<nav th:replace="fragments/customer :: sidebar"></nav> <nav th:replace="fragments/customer :: sidebar"></nav>
<div class="content-width detailflex"> <div class="content-width detailflex">
<div th:if="${orders.isEmpty()}"> <div th:if="${orderDeliveryDataMap.isEmpty()}">
<h2>Mit diesem Account wurden noch keine Bestellungen getätigt.</h2> <h2>Mit diesem Account wurden noch keine Bestellungen getätigt.</h2>
</div> </div>
<div th:if="${!orders.isEmpty()}" th:each="order: ${orders}"> <div th:if="${!orderDeliveryDataMap.isEmpty()}" th:each="order: ${orderDeliveryDataMap}">
<h2 id="20202701" th:text="|Bestellung vom ${order.formatCreated()}" />
<div> <div>
<table class="key-value"> <table class="key-value">
<tr>
<th>Bestelldatum</th>
<td th:text="${order.customerOrder.created}"></td>
</tr>
<tr> <tr>
<th>Lieferstatus</th> <th>Lieferstatus</th>
<td th:if="${order.deliveredAt == null}"><b>Unterwegs</b> <br/> Vorraussichtliche Ankunft: <span th:text="${order.getEstimatedArrival()}" /></td> <td th:if="${order.deliveryData.allOk()}"><span th:text="${order.deliveryData.getStatus()}"/>
<td th:if="${order.deliveredAt != null}"><b>Angekommen</b> <br/> Ankunft: <span th:text="${order.formatDeliveredAt()}" /></td> </td>
<td th:if="${order.deliveryData.noTrackingID()}">Bestellung wurde elektronisch angekündigt</td>
<td th:if="${order.deliveryData.noData()}">DHL-Server ist gerade nicht erreichbar</td>
</tr>
<tr>
<td></td>
<td th:if="${order.customerOrder.deliveredAt == null && order.customerOrder.trackingId!=null && !order.deliveryData.noData()}">
Vorraussichtliche Ankunft: <span th:text="${order.deliveryData.getEstimatedArrival()}"/>
</td>
<td th:if="${order.customerOrder.deliveredAt == null && order.customerOrder.trackingId!=null && order.deliveryData.noData()}">
Vorraussichtliche Ankunft: --:--:----
</td>
<td th:if="${order.customerOrder.deliveredAt != null && order.customerOrder.trackingId!=null}">
<b>Angekommen</b> Ankunft: <span th:text="${order.deliveryData.getEstimatedArrival()}"/>
</td>
</tr> </tr>
<tr> <tr>
<th>Sendeverfolgungsnummer</th> <th>Sendeverfolgungsnummer</th>
<td th:text="${order.trackingId}"></td> <td th:text="${order.customerOrder.trackingId!=null} ? ${deliveryService + ', ' + order.customerOrder.trackingId} : 'Es wurde noch keine Sendungsnummer vergeben'"></td>
</tr> </tr>
<tr> <tr>
<th></th> <th></th>
<td th:text="${order.destination.toString()}" /> <td th:text="${order.customerOrder.destination.toString()}"/>
</tr> </tr>
</table> </table>
</div> </div>
@ -50,11 +67,13 @@
<th>Menge</th> <th>Menge</th>
<th>Preis (Brutto)</th> <th>Preis (Brutto)</th>
</tr> </tr>
<tr th:each="position: ${order.positions}"> <tr th:each="position: ${order.customerOrder.positions}">
<td><a th:href="@{/shop/articles/{id}(id = ${position.article.id})}"><img th:src="@{/shop/articles/{id}/image.jpg(id=${position.article.id})}" class="s"/></a></td> <td><a th:href="@{/shop/articles/{id}(id = ${position.article.id})}"><img
<td><a th:href="@{/shop/articles/{id}(id = ${position.article.id})}" th:text="${position.article.title}" class="s"></a></td> th:src="@{/shop/articles/{id}/image.jpg(id=${position.article.id})}" class="s"/></a></td>
<td th:text="${position.quantity}" /> <td><a th:href="@{/shop/articles/{id}(id = ${position.article.id})}"
<td th:text="${#numbers.formatDecimal(position.getSumPrice() * 0.01, 1, 'POINT', 2, 'COMMA')}" /> th:text="${position.article.title}" class="s"></a></td>
<td th:text="${position.quantity}"/>
<td th:text="${#numbers.formatDecimal(position.getSumPrice() * 0.01, 1, 'POINT', 2, 'COMMA')}"/>
</tr> </tr>
<tr> <tr>
<th></th> <th></th>
@ -66,13 +85,13 @@
<td></td> <td></td>
<td></td> <td></td>
<td>Artikel (Netto)</td> <td>Artikel (Netto)</td>
<td th:text="${#numbers.formatDecimal(order.totalNetCent * 0.01, 1, 'POINT', 2, 'COMMA')}" /> <td th:text="${#numbers.formatDecimal(order.customerOrder.totalNetCent * 0.01, 1, 'POINT', 2, 'COMMA')}"/>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td></td> <td></td>
<td>Umsatzsteuer</td> <td>Umsatzsteuer</td>
<td th:text="${#numbers.formatDecimal(order.totalVatCent * 0.01, 1, 'POINT', 2, 'COMMA')}" /> <td th:text="${#numbers.formatDecimal(order.customerOrder.totalVatCent * 0.01, 1, 'POINT', 2, 'COMMA')}"/>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
@ -81,7 +100,7 @@
<h3>Gesammtpreis</h3> <h3>Gesammtpreis</h3>
</td> </td>
<td> <td>
<h3 th:text="${#numbers.formatDecimal(order.totalGrossCent * 0.01, 1, 'POINT', 2, 'COMMA')}"/> <h3 th:text="${#numbers.formatDecimal(order.customerOrder.totalGrossCent * 0.01, 1, 'POINT', 2, 'COMMA')}"/>
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -43,7 +43,10 @@
required> required>
<label for="password1">Neues Passwort</label> <label for="password1">Neues Passwort</label>
<input class="full-width" type="password" name="password1" placeholder="Passwort" id="password1" required> <input class="full-width" type="password" name="password1" placeholder="Passwort" id="password1"
pattern="(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}" required>
<small>Das Passwort muss mindestens 8 Zeichen enthalten. Es muss aus mindestens einem Großbuchstaben,
einem Kleinbuchstaben und einer Zahl bestehen.</small>
<label for="password2">Neues Passwort wiederholen</label> <label for="password2">Neues Passwort wiederholen</label>
<input class="full-width" type="password" name="password2" placeholder="Passwort" id="password2" <input class="full-width" type="password" name="password2" placeholder="Passwort" id="password2"
required> required>
@ -70,7 +73,8 @@
</div> </div>
<div> <div>
<label for="name">Name</label> <label for="name">Name</label>
<input class="full-width" type="text" name="name" id="name" placeholder="Nachname Vorname" th:value="${user.name}" <input class="full-width" type="text" name="name" id="name" placeholder="Nachname Vorname"
th:value="${user.defaultDeliveryAddress != null ? user.defaultDeliveryAddress.name : ''}"
required/> required/>
</div> </div>
</div> </div>
@ -78,7 +82,8 @@
<div> <div>
<label for="address">Anschrift</label> <label for="address">Anschrift</label>
<textarea rows="5" class="full-width" type="text" name="address" id="address" <textarea rows="5" class="full-width" type="text" name="address" id="address"
placeholder="Optional: Zusatz&#10;Optional: Unternehmen&#10;Straße Hausnummer&#10;Postleitzeit Ort&#10;Land" th:text="${user.defaultDeliveryAddress.addressString}"/> placeholder="Optional: Zusatz&#10;Optional: Unternehmen&#10;Straße Hausnummer&#10;Postleitzeit Ort&#10;Land"
th:text="${user.defaultDeliveryAddress != null ? user.defaultDeliveryAddress.addressString : ''}"/>
</div> </div>
<div> <div>
<button> Lieferinformation ändern</button> <button> Lieferinformation ändern</button>

View File

@ -7,7 +7,7 @@
}, },
"articles": [ "articles": [
{ {
"title": "TROPIC Geh<EFBFBD>usel<EFBFBD>fter", "title": "TROPIC Gehäuselüfter",
"manufacturer": "TROPIC", "manufacturer": "TROPIC",
"articleNumber": "tgl", "articleNumber": "tgl",
"vatPercent": 19, "vatPercent": 19,
@ -31,7 +31,7 @@
"shouldBeAdvertised": false "shouldBeAdvertised": false
}, },
{ {
"title": "Aeroheat CYLON PC-Geh<65>use", "title": "Aeroheat CYLON PC-Geh<65>use",
"manufacturer": "Aeroheat", "manufacturer": "Aeroheat",
"articleNumber": "acpcg", "articleNumber": "acpcg",
"vatPercent": 19, "vatPercent": 19,

View File

@ -1,5 +1,9 @@
package org.hso.ecommerce.supplier; package org.hso.ecommerce.supplier;
import org.hso.ecommerce.supplier.carrier.Avian;
import org.hso.ecommerce.supplier.carrier.Carrier;
import org.hso.ecommerce.supplier.carrier.Posaidon;
import org.hso.ecommerce.supplier.carrier.Shredder;
import org.hso.ecommerce.supplier.data.Article; import org.hso.ecommerce.supplier.data.Article;
import org.hso.ecommerce.supplier.data.Order; import org.hso.ecommerce.supplier.data.Order;
import org.hso.ecommerce.supplier.data.OrderConfirmation; import org.hso.ecommerce.supplier.data.OrderConfirmation;
@ -19,9 +23,13 @@ import java.util.List;
public class RequestController { public class RequestController {
private final HashMap<String, Integer> dailySalesVolumeCent = new HashMap<>(); private final HashMap<String, Integer> dailySalesVolumeCent = new HashMap<>();
private final HashMap<String, Supplier> knownSuppliers = new HashMap<>(); private final HashMap<String, Supplier> knownSuppliers = new HashMap<>();
private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
private final Carrier[] carriers = new Carrier[]{
new Avian(), new Posaidon(), new Shredder()
};
@PostConstruct @PostConstruct
public void init() throws IOException { public void init() throws IOException {
for (Supplier s : ConfigurationReader.read()) { for (Supplier s : ConfigurationReader.read()) {
@ -38,7 +46,7 @@ public class RequestController {
@GetMapping("/{supplier}/") @GetMapping("/{supplier}/")
public Supplier supplier(HttpServletResponse res, @PathVariable("supplier") String supplierName) { public Supplier supplier(HttpServletResponse res, @PathVariable("supplier") String supplierName) {
Supplier s = knownSuppliers.get(supplierName); Supplier s = knownSuppliers.get(supplierName);
if(s == null) { if (s == null) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND); res.setStatus(HttpServletResponse.SC_NOT_FOUND);
} }
return s; return s;
@ -47,16 +55,16 @@ public class RequestController {
@PostMapping("/{supplier}/order") @PostMapping("/{supplier}/order")
public OrderConfirmation order(HttpServletResponse res, @PathVariable("supplier") String supplierName, @RequestBody Order order) { public OrderConfirmation order(HttpServletResponse res, @PathVariable("supplier") String supplierName, @RequestBody Order order) {
Supplier s = knownSuppliers.get(supplierName); Supplier s = knownSuppliers.get(supplierName);
if(s == null) { if (s == null) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND); res.setStatus(HttpServletResponse.SC_NOT_FOUND);
return null; return null;
} }
String dateKey = simpleDateFormat.format(new Date()); String dateKey = simpleDateFormat.format(new Date());
int dailyVolume = dailySalesVolumeCent.getOrDefault(dateKey,0); int dailyVolume = dailySalesVolumeCent.getOrDefault(dateKey, 0);
Article a = s.findArticle(order.manufacturer, order.articleNumber); Article a = s.findArticle(order.manufacturer, order.articleNumber);
if(a == null) { if (a == null) {
res.setStatus(HttpServletResponse.SC_BAD_REQUEST); res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return null; return null;
} }
@ -72,6 +80,8 @@ public class RequestController {
} }
int discount = (discountableNetAmount * s.discount.percentDiscount) / 100; int discount = (discountableNetAmount * s.discount.percentDiscount) / 100;
Carrier selectedCarrier = carriers[Math.abs((supplierName + java.time.LocalDate.now()).hashCode()) % carriers.length];
OrderConfirmation confirmation = new OrderConfirmation(); OrderConfirmation confirmation = new OrderConfirmation();
confirmation.articleNumber = order.articleNumber; confirmation.articleNumber = order.articleNumber;
confirmation.discountNetCent = discount; confirmation.discountNetCent = discount;
@ -79,6 +89,9 @@ public class RequestController {
confirmation.manufacturer = a.manufacturer; confirmation.manufacturer = a.manufacturer;
confirmation.quantity = order.quantity; confirmation.quantity = order.quantity;
confirmation.totalPriceNetCharged = priceNet - discount; confirmation.totalPriceNetCharged = priceNet - discount;
confirmation.carrier = selectedCarrier.getName();
confirmation.trackingId = selectedCarrier.generateTrackingId();
confirmation.estimatedArrival = selectedCarrier.arrivalEstimate();
if (confirmation.totalPriceNetCharged > order.maxTotalPriceCentNet) { if (confirmation.totalPriceNetCharged > order.maxTotalPriceCentNet) {
res.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED); res.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);

View File

@ -0,0 +1,28 @@
package org.hso.ecommerce.supplier.carrier;
import java.time.LocalDateTime;
import java.util.Random;
public class Avian implements Carrier {
@Override
public String getName() {
return "Avian Carriers";
}
@Override
public String generateTrackingId() {
Random rnd = new Random();
return "2001-"
+ Integer.toHexString(rnd.nextInt(0xFFFF))
+ "--"
+ Integer.toHexString(rnd.nextInt(0xFFFF))
+ "-"
+ Integer.toHexString(rnd.nextInt(0xFFFF));
}
@Override
public LocalDateTime arrivalEstimate() {
return LocalDateTime.now().plusHours(8);
}
}

View File

@ -0,0 +1,12 @@
package org.hso.ecommerce.supplier.carrier;
import java.time.LocalDateTime;
public interface Carrier {
public String getName();
public String generateTrackingId();
public LocalDateTime arrivalEstimate();
}

View File

@ -0,0 +1,26 @@
package org.hso.ecommerce.supplier.carrier;
import java.time.LocalDateTime;
import java.util.Random;
public class Posaidon implements Carrier {
@Override
public String getName() {
return "Poseidon Inc.";
}
@Override
public String generateTrackingId() {
Random rnd = new Random();
return "WAT"
+ Integer.toString(rnd.nextInt(Short.MAX_VALUE))
+ "3"
+ Integer.toString(rnd.nextInt(Short.MAX_VALUE))
+ "R";
}
@Override
public LocalDateTime arrivalEstimate() {
return LocalDateTime.now().plusHours(50);
}
}

View File

@ -0,0 +1,31 @@
package org.hso.ecommerce.supplier.carrier;
import java.time.LocalDateTime;
import java.util.Random;
public class Shredder implements Carrier {
private Random rnd = new Random();
@Override
public String getName() {
return "Schree & Derr";
}
@Override
public String generateTrackingId() {
return "O" + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + d() + "0";
}
@Override
public LocalDateTime arrivalEstimate() {
return LocalDateTime.now().plusHours(22);
}
/**
* @return a random digit followed by a dash.
*/
private String d() {
return Integer.toString(rnd.nextInt(9)) + "-";
}
}

View File

@ -1,5 +1,7 @@
package org.hso.ecommerce.supplier.data; package org.hso.ecommerce.supplier.data;
import java.time.LocalDateTime;
public class OrderConfirmation { public class OrderConfirmation {
public String manufacturer; public String manufacturer;
public String articleNumber; public String articleNumber;
@ -9,4 +11,8 @@ public class OrderConfirmation {
public int pricePerUnitNetCent; public int pricePerUnitNetCent;
public int discountNetCent; public int discountNetCent;
public int totalPriceNetCharged; public int totalPriceNetCharged;
public String carrier;
public String trackingId;
public LocalDateTime estimatedArrival;
} }