Merge pull request 'feature/dashboard' (#83) from feature/dashboard into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
This commit is contained in:
		@ -59,7 +59,7 @@ public class CalculateWarehouseStatsAction {
 | 
			
		||||
        return articleIds.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class WarehouseStats {
 | 
			
		||||
    public static class WarehouseStats {
 | 
			
		||||
        public int numArticles;
 | 
			
		||||
        public double efficiency;
 | 
			
		||||
        public double ratioUsedSlots;
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,5 @@
 | 
			
		||||
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;
 | 
			
		||||
@ -28,11 +16,14 @@ import org.hso.ecommerce.entities.supplier.SupplierOrder;
 | 
			
		||||
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.booking.BookingRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.dashboard.DashboardSummaryRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.shop.ArticleRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.supplier.ArticleOfferRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.supplier.SupplierOrderRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.supplier.SupplierRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.user.UserRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.warehouse.SlotRepository;
 | 
			
		||||
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
@ -44,6 +35,11 @@ import org.springframework.web.bind.annotation.PathVariable;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import java.sql.Timestamp;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.Map.Entry;
 | 
			
		||||
 | 
			
		||||
interface ICronjob {
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the earliest cronjob execution time that happens after the given reference time.
 | 
			
		||||
@ -242,6 +238,9 @@ class CronjobController {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private final BackgroundJobRepository cronjobRepository = null;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    final SlotRepository slotRepository = null;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    final ArticleRepository articleRepository = null;
 | 
			
		||||
 | 
			
		||||
@ -260,17 +259,24 @@ class CronjobController {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    final DashboardSummaryRepository dashboardSummaryRepository = null;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    final SupplierRepository supplierRepository = null;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    final SupplierOrderRepository supplierOrderRepository = null;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    final UserRepository userRepository = null;
 | 
			
		||||
 | 
			
		||||
    private static Map<String, ICronjob> getCronjobs() {
 | 
			
		||||
        HashMap<String, ICronjob> map = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
        // Register all existing cronjobs
 | 
			
		||||
        map.put(BackgroundJob.JOB_REORDER, new Reorder());
 | 
			
		||||
        map.put(BackgroundJob.JOB_DASHBOARD, new DashboardCronjob());
 | 
			
		||||
 | 
			
		||||
        return Collections.unmodifiableMap(map);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,86 @@
 | 
			
		||||
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 = nullToZero(getSales(oneDayBefore, time, controller));
 | 
			
		||||
        dashboardSummary.todaysNewCustomers = nullToZero(getNewUsers(oneDayBefore, time, controller));
 | 
			
		||||
        dashboardSummary.todaysWarehouseCapacity = warehouseStats.efficiency;
 | 
			
		||||
        dashboardSummary.currentWarehouseCapacity = warehouseStats.ratioUsedSlots;
 | 
			
		||||
        dashboardSummary.todaysSalesCent = nullToZero(getTurnover(oneDayBefore, time, controller));
 | 
			
		||||
 | 
			
		||||
        controller.dashboardSummaryRepository.save(dashboardSummary);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getDisplayName() {
 | 
			
		||||
        return "Dashboard refresh";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int nullToZero(Integer input) {
 | 
			
		||||
        return input == null ? 0 : input;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -15,11 +15,12 @@ public class DashboardSummary {
 | 
			
		||||
    @NotNull
 | 
			
		||||
    public java.sql.Date created;
 | 
			
		||||
 | 
			
		||||
    public int todaysCustomers;
 | 
			
		||||
    public int todaysNewCustomers;
 | 
			
		||||
    public int todaysCustomersOrders;
 | 
			
		||||
    public int todaysSuppliersOrders;
 | 
			
		||||
    public int todaysItemsSold;
 | 
			
		||||
    public int todaysSalesCent;
 | 
			
		||||
    public int totalWarehouseCapacity;
 | 
			
		||||
    public int currentWarehouseCapacity;
 | 
			
		||||
    public double currentWarehouseCapacity;
 | 
			
		||||
    public double todaysWarehouseCapacity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -25,4 +25,14 @@ public interface CustomerOrderRepository extends JpaRepository<CustomerOrder, Lo
 | 
			
		||||
    @Query("SELECT co FROM CustomerOrder co WHERE co.customer.id = :userId ORDER BY co.id DESC")
 | 
			
		||||
    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
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -13,6 +13,11 @@ public interface UserRepository extends JpaRepository<User, Long> {
 | 
			
		||||
	@Query("SELECT c FROM User c WHERE c.email = :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")
 | 
			
		||||
	Optional<Integer> numberOfEmployees();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,3 +22,8 @@ spring.servlet.multipart.max-request-size=10MB
 | 
			
		||||
# EMBEDDED SERVER CONFIGURATION (ServerProperties)
 | 
			
		||||
server.address=::1
 | 
			
		||||
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
 | 
			
		||||
@ -6,8 +6,8 @@
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=0.75, user-scalable=no">
 | 
			
		||||
    <script src="/js/chart.js"></script>
 | 
			
		||||
    <script>
 | 
			
		||||
      function chart(id, name, data, unit) {
 | 
			
		||||
         document.addEventListener("DOMContentLoaded", function() {
 | 
			
		||||
      function chart(id, name, data, unit, dates) {
 | 
			
		||||
 | 
			
		||||
            let elm = document.getElementById(id);
 | 
			
		||||
            let cs = getComputedStyle(elm)
 | 
			
		||||
            let ctx = elm.getContext('2d');
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
 | 
			
		||||
               // The data for our dataset
 | 
			
		||||
               data: {
 | 
			
		||||
                  labels: ['Freitag', 'Donnerstag', 'Mittwoch', 'Dienstag', 'Montag'],
 | 
			
		||||
                  labels: dates,
 | 
			
		||||
                  datasets: [{
 | 
			
		||||
                     label: name,
 | 
			
		||||
                     backgroundColor: cs.getPropertyValue('--c-primary'),
 | 
			
		||||
@ -61,7 +61,6 @@
 | 
			
		||||
                  }
 | 
			
		||||
               }
 | 
			
		||||
            });
 | 
			
		||||
         });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    </script>
 | 
			
		||||
@ -107,7 +106,12 @@
 | 
			
		||||
                <h3>Verkäufe (Anzahl)</h3>
 | 
			
		||||
                <canvas id="sales"></canvas>
 | 
			
		||||
                <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>
 | 
			
		||||
            </section>
 | 
			
		||||
@ -116,7 +120,12 @@
 | 
			
		||||
                <h3>Verkäufe (Umsatz)</h3>
 | 
			
		||||
                <canvas id="umsatz"></canvas>
 | 
			
		||||
                <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>
 | 
			
		||||
            </section>
 | 
			
		||||
@ -125,7 +134,12 @@
 | 
			
		||||
                <h3>Neukunden</h3>
 | 
			
		||||
                <canvas id="Neukunden"></canvas>
 | 
			
		||||
                <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>
 | 
			
		||||
            </section>
 | 
			
		||||
@ -139,7 +153,12 @@
 | 
			
		||||
                <h2>Lagerauslastung</h2>
 | 
			
		||||
                <canvas id="warehouse"></canvas>
 | 
			
		||||
                <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>
 | 
			
		||||
            </section>
 | 
			
		||||
@ -148,7 +167,12 @@
 | 
			
		||||
                <h2>Lagereffizienz</h2>
 | 
			
		||||
                <canvas id="warehouse-ef"></canvas>
 | 
			
		||||
                <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>
 | 
			
		||||
            </section>
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user