Scope staff analytics
This commit is contained in:
@@ -1,26 +1,32 @@
|
||||
package com.petshop.backend.controller;
|
||||
|
||||
import com.petshop.backend.dto.analytics.DashboardResponse;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import com.petshop.backend.service.AnalyticsService;
|
||||
import com.petshop.backend.util.AuthenticationHelper;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/analytics")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'STAFF')")
|
||||
public class AnalyticsController {
|
||||
|
||||
private final AnalyticsService analyticsService;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public AnalyticsController(AnalyticsService analyticsService) {
|
||||
public AnalyticsController(AnalyticsService analyticsService, UserRepository userRepository) {
|
||||
this.analyticsService = analyticsService;
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/dashboard")
|
||||
public ResponseEntity<DashboardResponse> getDashboard(
|
||||
@RequestParam(defaultValue = "30") int days,
|
||||
@RequestParam(defaultValue = "10") int top) {
|
||||
return ResponseEntity.ok(analyticsService.getDashboardData(days, top));
|
||||
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||
return ResponseEntity.ok(analyticsService.getDashboardData(days, top, user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,19 @@ public class DashboardResponse {
|
||||
private InventorySummary inventorySummary;
|
||||
private List<TopProduct> topProducts;
|
||||
private List<DailySales> dailySales;
|
||||
private List<PaymentMethodData> paymentMethods;
|
||||
private List<EmployeePerformanceData> employeePerformance;
|
||||
|
||||
public DashboardResponse() {
|
||||
}
|
||||
|
||||
public DashboardResponse(SalesSummary salesSummary, InventorySummary inventorySummary, List<TopProduct> topProducts, List<DailySales> dailySales) {
|
||||
public DashboardResponse(SalesSummary salesSummary, InventorySummary inventorySummary, List<TopProduct> topProducts, List<DailySales> dailySales, List<PaymentMethodData> paymentMethods, List<EmployeePerformanceData> employeePerformance) {
|
||||
this.salesSummary = salesSummary;
|
||||
this.inventorySummary = inventorySummary;
|
||||
this.topProducts = topProducts;
|
||||
this.dailySales = dailySales;
|
||||
this.paymentMethods = paymentMethods;
|
||||
this.employeePerformance = employeePerformance;
|
||||
}
|
||||
|
||||
public SalesSummary getSalesSummary() {
|
||||
@@ -52,17 +56,33 @@ public class DashboardResponse {
|
||||
this.dailySales = dailySales;
|
||||
}
|
||||
|
||||
public List<PaymentMethodData> getPaymentMethods() {
|
||||
return paymentMethods;
|
||||
}
|
||||
|
||||
public void setPaymentMethods(List<PaymentMethodData> paymentMethods) {
|
||||
this.paymentMethods = paymentMethods;
|
||||
}
|
||||
|
||||
public List<EmployeePerformanceData> getEmployeePerformance() {
|
||||
return employeePerformance;
|
||||
}
|
||||
|
||||
public void setEmployeePerformance(List<EmployeePerformanceData> employeePerformance) {
|
||||
this.employeePerformance = employeePerformance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
DashboardResponse that = (DashboardResponse) o;
|
||||
return Objects.equals(salesSummary, that.salesSummary) && Objects.equals(inventorySummary, that.inventorySummary) && Objects.equals(topProducts, that.topProducts) && Objects.equals(dailySales, that.dailySales);
|
||||
return Objects.equals(salesSummary, that.salesSummary) && Objects.equals(inventorySummary, that.inventorySummary) && Objects.equals(topProducts, that.topProducts) && Objects.equals(dailySales, that.dailySales) && Objects.equals(paymentMethods, that.paymentMethods) && Objects.equals(employeePerformance, that.employeePerformance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(salesSummary, inventorySummary, topProducts, dailySales);
|
||||
return Objects.hash(salesSummary, inventorySummary, topProducts, dailySales, paymentMethods, employeePerformance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,6 +92,8 @@ public class DashboardResponse {
|
||||
", inventorySummary=" + inventorySummary +
|
||||
", topProducts=" + topProducts +
|
||||
", dailySales=" + dailySales +
|
||||
", paymentMethods=" + paymentMethods +
|
||||
", employeePerformance=" + employeePerformance +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -80,15 +102,17 @@ public class DashboardResponse {
|
||||
private Long totalSales;
|
||||
private BigDecimal totalRefunds;
|
||||
private Long totalRefundCount;
|
||||
private Long totalItemsSold;
|
||||
|
||||
public SalesSummary() {
|
||||
}
|
||||
|
||||
public SalesSummary(BigDecimal totalRevenue, Long totalSales, BigDecimal totalRefunds, Long totalRefundCount) {
|
||||
public SalesSummary(BigDecimal totalRevenue, Long totalSales, BigDecimal totalRefunds, Long totalRefundCount, Long totalItemsSold) {
|
||||
this.totalRevenue = totalRevenue;
|
||||
this.totalSales = totalSales;
|
||||
this.totalRefunds = totalRefunds;
|
||||
this.totalRefundCount = totalRefundCount;
|
||||
this.totalItemsSold = totalItemsSold;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalRevenue() {
|
||||
@@ -123,17 +147,25 @@ public class DashboardResponse {
|
||||
this.totalRefundCount = totalRefundCount;
|
||||
}
|
||||
|
||||
public Long getTotalItemsSold() {
|
||||
return totalItemsSold;
|
||||
}
|
||||
|
||||
public void setTotalItemsSold(Long totalItemsSold) {
|
||||
this.totalItemsSold = totalItemsSold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
SalesSummary that = (SalesSummary) o;
|
||||
return Objects.equals(totalRevenue, that.totalRevenue) && Objects.equals(totalSales, that.totalSales) && Objects.equals(totalRefunds, that.totalRefunds) && Objects.equals(totalRefundCount, that.totalRefundCount);
|
||||
return Objects.equals(totalRevenue, that.totalRevenue) && Objects.equals(totalSales, that.totalSales) && Objects.equals(totalRefunds, that.totalRefunds) && Objects.equals(totalRefundCount, that.totalRefundCount) && Objects.equals(totalItemsSold, that.totalItemsSold);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(totalRevenue, totalSales, totalRefunds, totalRefundCount);
|
||||
return Objects.hash(totalRevenue, totalSales, totalRefunds, totalRefundCount, totalItemsSold);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,10 +175,69 @@ public class DashboardResponse {
|
||||
", totalSales=" + totalSales +
|
||||
", totalRefunds=" + totalRefunds +
|
||||
", totalRefundCount=" + totalRefundCount +
|
||||
", totalItemsSold=" + totalItemsSold +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class PaymentMethodData {
|
||||
private String paymentMethod;
|
||||
private Long count;
|
||||
|
||||
public PaymentMethodData() {
|
||||
}
|
||||
|
||||
public PaymentMethodData(String paymentMethod, Long count) {
|
||||
this.paymentMethod = paymentMethod;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public String getPaymentMethod() {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
public void setPaymentMethod(String paymentMethod) {
|
||||
this.paymentMethod = paymentMethod;
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Long count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EmployeePerformanceData {
|
||||
private String employeeName;
|
||||
private BigDecimal revenue;
|
||||
|
||||
public EmployeePerformanceData() {
|
||||
}
|
||||
|
||||
public EmployeePerformanceData(String employeeName, BigDecimal revenue) {
|
||||
this.employeeName = employeeName;
|
||||
this.revenue = revenue;
|
||||
}
|
||||
|
||||
public String getEmployeeName() {
|
||||
return employeeName;
|
||||
}
|
||||
|
||||
public void setEmployeeName(String employeeName) {
|
||||
this.employeeName = employeeName;
|
||||
}
|
||||
|
||||
public BigDecimal getRevenue() {
|
||||
return revenue;
|
||||
}
|
||||
|
||||
public void setRevenue(BigDecimal revenue) {
|
||||
this.revenue = revenue;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InventorySummary {
|
||||
private Long totalProducts;
|
||||
private Long lowStockProducts;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.dto.analytics.DashboardResponse;
|
||||
import com.petshop.backend.entity.Employee;
|
||||
import com.petshop.backend.entity.Inventory;
|
||||
import com.petshop.backend.entity.Product;
|
||||
import com.petshop.backend.entity.Sale;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.EmployeeRepository;
|
||||
import com.petshop.backend.repository.InventoryRepository;
|
||||
import com.petshop.backend.repository.ProductRepository;
|
||||
import com.petshop.backend.repository.SaleRepository;
|
||||
@@ -23,28 +26,33 @@ public class AnalyticsService {
|
||||
private final SaleRepository saleRepository;
|
||||
private final InventoryRepository inventoryRepository;
|
||||
private final ProductRepository productRepository;
|
||||
private final EmployeeRepository employeeRepository;
|
||||
|
||||
public AnalyticsService(SaleRepository saleRepository,
|
||||
InventoryRepository inventoryRepository, ProductRepository productRepository) {
|
||||
InventoryRepository inventoryRepository, ProductRepository productRepository, EmployeeRepository employeeRepository) {
|
||||
this.saleRepository = saleRepository;
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.productRepository = productRepository;
|
||||
this.employeeRepository = employeeRepository;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public DashboardResponse getDashboardData(int days, int top) {
|
||||
public DashboardResponse getDashboardData(int days, int top, User user) {
|
||||
LocalDateTime startDate = LocalDateTime.now().minusDays(days);
|
||||
|
||||
List<Sale> sales = saleRepository.findAll().stream()
|
||||
.filter(sale -> sale.getSaleDate().isAfter(startDate))
|
||||
.filter(sale -> includeSaleForUser(sale, user))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
DashboardResponse.SalesSummary salesSummary = calculateSalesSummary(sales);
|
||||
DashboardResponse.InventorySummary inventorySummary = calculateInventorySummary();
|
||||
DashboardResponse.InventorySummary inventorySummary = user.getRole() == User.Role.ADMIN ? calculateInventorySummary() : null;
|
||||
List<DashboardResponse.TopProduct> topProducts = calculateTopProducts(sales, top);
|
||||
List<DashboardResponse.DailySales> dailySales = calculateDailySales(sales, days);
|
||||
List<DashboardResponse.PaymentMethodData> paymentMethods = calculatePaymentMethods(sales);
|
||||
List<DashboardResponse.EmployeePerformanceData> employeePerformance = calculateEmployeePerformance(sales, user);
|
||||
|
||||
return new DashboardResponse(salesSummary, inventorySummary, topProducts, dailySales);
|
||||
return new DashboardResponse(salesSummary, inventorySummary, topProducts, dailySales, paymentMethods, employeePerformance);
|
||||
}
|
||||
|
||||
private DashboardResponse.SalesSummary calculateSalesSummary(List<Sale> sales) {
|
||||
@@ -66,7 +74,13 @@ public class AnalyticsService {
|
||||
.filter(Sale::getIsRefund)
|
||||
.count();
|
||||
|
||||
return new DashboardResponse.SalesSummary(totalRevenue, totalSales, totalRefunds, totalRefundCount);
|
||||
Long totalItemsSold = sales.stream()
|
||||
.filter(sale -> !sale.getIsRefund())
|
||||
.flatMap(sale -> sale.getItems().stream())
|
||||
.mapToLong(item -> item.getQuantity())
|
||||
.sum();
|
||||
|
||||
return new DashboardResponse.SalesSummary(totalRevenue, totalSales, totalRefunds, totalRefundCount, totalItemsSold);
|
||||
}
|
||||
|
||||
private DashboardResponse.InventorySummary calculateInventorySummary() {
|
||||
@@ -138,4 +152,50 @@ public class AnalyticsService {
|
||||
|
||||
return new ArrayList<>(dailySalesMap.values());
|
||||
}
|
||||
|
||||
private List<DashboardResponse.PaymentMethodData> calculatePaymentMethods(List<Sale> sales) {
|
||||
return sales.stream()
|
||||
.filter(sale -> !sale.getIsRefund())
|
||||
.collect(Collectors.groupingBy(
|
||||
sale -> sale.getPaymentMethod() == null ? "Unknown" : sale.getPaymentMethod(),
|
||||
TreeMap::new,
|
||||
Collectors.counting()))
|
||||
.entrySet().stream()
|
||||
.map(entry -> new DashboardResponse.PaymentMethodData(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<DashboardResponse.EmployeePerformanceData> calculateEmployeePerformance(List<Sale> sales, User user) {
|
||||
Map<String, BigDecimal> employeeRevenue = new TreeMap<>();
|
||||
|
||||
for (Sale sale : sales) {
|
||||
if (sale.getIsRefund()) {
|
||||
continue;
|
||||
}
|
||||
String employeeName = sale.getEmployee().getFirstName() + " " + sale.getEmployee().getLastName();
|
||||
employeeRevenue.merge(employeeName, sale.getTotalAmount(), BigDecimal::add);
|
||||
}
|
||||
|
||||
if (user.getRole() == User.Role.STAFF && employeeRevenue.isEmpty()) {
|
||||
Employee employee = employeeRepository.findByUserId(user.getId()).orElse(null);
|
||||
if (employee != null) {
|
||||
String employeeName = employee.getFirstName() + " " + employee.getLastName();
|
||||
employeeRevenue.put(employeeName, BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
return employeeRevenue.entrySet().stream()
|
||||
.map(entry -> new DashboardResponse.EmployeePerformanceData(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean includeSaleForUser(Sale sale, User user) {
|
||||
if (user.getRole() == User.Role.ADMIN) {
|
||||
return true;
|
||||
}
|
||||
if (user.getRole() == User.Role.STAFF) {
|
||||
return sale.getEmployee() != null && sale.getEmployee().getUserId() != null && sale.getEmployee().getUserId().equals(user.getId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user