Dashboard working

This commit is contained in:
Danny 2020-06-15 17:47:55 +02:00
parent 2ba4968556
commit 6284a4d7bf
10 changed files with 199 additions and 23 deletions

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

@ -1,16 +1,5 @@
package org.hso.ecommerce.controller.cronjob; package org.hso.ecommerce.controller.cronjob;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.PostConstruct;
import org.hso.ecommerce.action.booking.CreateBookingAction; import org.hso.ecommerce.action.booking.CreateBookingAction;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction; import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier; import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier;
@ -27,17 +16,25 @@ 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;
import org.hso.ecommerce.repos.supplier.SupplierOrderRepository; import org.hso.ecommerce.repos.supplier.SupplierOrderRepository;
import org.hso.ecommerce.repos.supplier.SupplierRepository; import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository; import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
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.util.*;
import java.util.Map.Entry;
interface ICronjob { interface ICronjob {
/** /**
* Calculate the earliest cronjob execution time that happens after the given reference time. * Calculate the earliest cronjob execution time that happens after the given reference time.
@ -223,6 +220,9 @@ class CronjobController {
@Autowired @Autowired
private final BackgroundJobRepository cronjobRepository = null; private final BackgroundJobRepository cronjobRepository = null;
@Autowired
final SlotRepository slotRepository = null;
@Autowired @Autowired
final ArticleRepository articleRepository = null; final ArticleRepository articleRepository = null;
@ -241,17 +241,24 @@ 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;
@Autowired
final UserRepository userRepository = null;
private static Map<String, ICronjob> getCronjobs() { private static 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, new Reorder());
map.put(BackgroundJob.JOB_DASHBOARD, new DashboardCronjob());
return Collections.unmodifiableMap(map); return Collections.unmodifiableMap(map);
} }

View File

@ -0,0 +1,79 @@
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.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 {
@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 = controller.slotRepository.findAll().stream().map(
s -> controller.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 = getSales(oneDayBefore, time, controller);
dashboardSummary.todaysNewCustomers = getNewUsers(oneDayBefore, time, controller);
dashboardSummary.todaysWarehouseCapacity = warehouseStats.efficiency;
dashboardSummary.currentWarehouseCapacity = warehouseStats.ratioUsedSlots;
dashboardSummary.todaysSalesCent = getTurnover(oneDayBefore, time, controller);
controller.dashboardSummaryRepository.save(dashboardSummary);
}
private Integer getSales (Calendar begin, Calendar end, CronjobController controller)
{
return controller.customerOrderRepository.countOrdersInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private Integer getTurnover (Calendar begin, Calendar end, CronjobController controller)
{
return controller.customerOrderRepository.countTurnoverInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private Integer getNewUsers (Calendar begin, Calendar end, CronjobController controller)
{
return controller.userRepository.countUsersInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
}

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

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

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

@ -18,4 +18,14 @@ public interface CustomerOrderRepository extends JpaRepository<CustomerOrder, Lo
@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

@ -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

@ -22,3 +22,8 @@ spring.servlet.multipart.max-request-size=10MB
# EMBEDDED SERVER CONFIGURATION (ServerProperties) # EMBEDDED SERVER CONFIGURATION (ServerProperties)
server.address=::1 server.address=::1
server.port=8080 server.port=8080
spring.thymeleaf.prefix=file:src/main/resources/templates/
spring.thymeleaf.cache=false
spring.resources.static-locations=file:src/main/resources/static/
spring.resources.cache=false

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.todaysWarehouseCapacity),
"%", 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),
"%", data.map(g => g.created));
});
</script> </script>
</section> </section>