ReportsController.java
package com.ctrlbuy.webshop.controller;
import com.ctrlbuy.webshop.entity.Order;
import com.ctrlbuy.webshop.entity.Product;
import com.ctrlbuy.webshop.repository.OrderRepository;
import com.ctrlbuy.webshop.repository.ProductRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
@Controller
@RequestMapping("/admin/reports")
@PreAuthorize("hasRole('ADMIN')")
public class ReportsController {
private static final Logger logger = LoggerFactory.getLogger(ReportsController.class);
private final ProductRepository productRepository;
private final OrderRepository orderRepository;
public ReportsController(ProductRepository productRepository,
OrderRepository orderRepository) {
this.productRepository = productRepository;
this.orderRepository = orderRepository;
}
// 🏠 HUVUDSIDA - /admin/reports
@GetMapping("")
public String reports(Model model) {
logger.info("Genererar rapport huvudsida");
try {
List<Product> allProducts = productRepository.findAll();
long totalProducts = allProducts.size();
long activeProducts = allProducts.stream().filter(p -> p.getStockQuantity() != null && p.getStockQuantity() > 0).count();
model.addAttribute("totalProducts", totalProducts);
model.addAttribute("activeProducts", activeProducts);
logger.debug("Huvudsida - Total: {}, Aktiva: {}", totalProducts, activeProducts);
} catch (Exception e) {
logger.error("Fel vid generering av rapport huvudsida", e);
model.addAttribute("totalProducts", 0);
model.addAttribute("activeProducts", 0);
}
return "admin/reports/index";
}
// 📊 PRODUKTRAPPORT - /admin/reports/products
@GetMapping("/products")
public String productReport(Model model) {
try {
logger.info("Genererar produktrapport");
// Använd standard JPA findAll() - enklast och mest pålitlig
List<Product> allProducts = productRepository.findAll();
logger.debug("Hämtade {} produkter", allProducts.size());
// Grundläggande statistik
int totalProducts = allProducts.size();
model.addAttribute("totalProducts", totalProducts);
// Prisstatistik - MANUELL BERÄKNING
BigDecimal maxPrice = BigDecimal.ZERO;
BigDecimal minPrice = new BigDecimal("999999");
BigDecimal totalPrice = BigDecimal.ZERO;
int validPriceCount = 0;
Product mostExpensive = null;
Product cheapest = null;
for (Product product : allProducts) {
BigDecimal currentPrice = product.getCurrentPrice(); // Använd getCurrentPrice() istället för getPrice()
if (currentPrice != null && currentPrice.compareTo(BigDecimal.ZERO) > 0) {
validPriceCount++;
totalPrice = totalPrice.add(currentPrice);
// Kontrollera max pris
if (currentPrice.compareTo(maxPrice) > 0) {
maxPrice = currentPrice;
mostExpensive = product;
}
// Kontrollera min pris
if (currentPrice.compareTo(minPrice) < 0) {
minPrice = currentPrice;
cheapest = product;
}
}
}
// Säkerställ att vi inte har "999999" som min-värde
if (minPrice.equals(new BigDecimal("999999"))) {
minPrice = BigDecimal.ZERO;
}
// Beräkna genomsnitt
BigDecimal averagePrice = BigDecimal.ZERO;
if (validPriceCount > 0) {
averagePrice = totalPrice.divide(new BigDecimal(validPriceCount), 2, RoundingMode.HALF_UP);
}
logger.debug("Prisstatistik - Max: {}, Min: {}, Genomsnitt: {}", maxPrice, minPrice, averagePrice);
// Sätt attribut för frontend - ALLA VARIANTER
model.addAttribute("maxPrice", maxPrice);
model.addAttribute("minPrice", minPrice);
model.addAttribute("highestPrice", maxPrice); // Template-kompatibilitet
model.addAttribute("lowestPrice", minPrice); // Template-kompatibilitet
model.addAttribute("averagePrice", averagePrice);
model.addAttribute("mostExpensiveProduct", mostExpensive);
model.addAttribute("cheapestProduct", cheapest);
// Lågt lager beräkning
int lowStockCount = 0;
List<Product> lowStockProducts = new ArrayList<>();
for (Product product : allProducts) {
if (product.getStockQuantity() != null && product.getStockQuantity() < 10) {
lowStockCount++;
lowStockProducts.add(product);
}
}
model.addAttribute("lowStockCount", lowStockCount);
model.addAttribute("lowStockProducts", lowStockProducts);
// Kategori-räkning
Map<String, Long> categoryMap = new HashMap<>();
for (Product product : allProducts) {
String category = "Okänd";
if (product.getCategory() != null && !product.getCategory().trim().isEmpty()) {
category = product.getCategory();
}
categoryMap.put(category, categoryMap.getOrDefault(category, 0L) + 1L);
}
model.addAttribute("productsByCategory", categoryMap);
// Extra statistik för rapporter
long inStockProducts = allProducts.stream()
.mapToLong(p -> p.getStockQuantity() != null && p.getStockQuantity() > 0 ? 1 : 0)
.sum();
long outOfStockProducts = totalProducts - inStockProducts;
model.addAttribute("inStockProducts", inStockProducts);
model.addAttribute("outOfStockProducts", outOfStockProducts);
// Alla produkter för tabellen
model.addAttribute("products", allProducts);
logger.info("Produktrapport genererad framgångsrikt");
} catch (Exception e) {
logger.error("Fel vid generering av produktrapport", e);
// Fallback-värden
model.addAttribute("totalProducts", 0);
model.addAttribute("averagePrice", BigDecimal.ZERO);
model.addAttribute("maxPrice", BigDecimal.ZERO);
model.addAttribute("minPrice", BigDecimal.ZERO);
model.addAttribute("highestPrice", BigDecimal.ZERO);
model.addAttribute("lowestPrice", BigDecimal.ZERO);
model.addAttribute("lowStockCount", 0);
model.addAttribute("products", new ArrayList<>());
model.addAttribute("productsByCategory", new HashMap<>());
model.addAttribute("inStockProducts", 0L);
model.addAttribute("outOfStockProducts", 0L);
}
return "admin/reports/products";
}
// 💰 FÖRSÄLJNINGSRAPPORT - /admin/reports/sales
@GetMapping("/sales")
public String salesReport(Model model) {
try {
logger.info("Genererar försäljningsrapport");
List<Product> allProducts = productRepository.findAll();
List<Order> allOrders = orderRepository.findAll();
// Grundläggande produktstatistik
long totalProducts = allProducts.size();
long saleProducts = allProducts.stream().filter(Product::isOnSale).count();
// Beräkna totala besparingar (för produkter på rea)
BigDecimal totalSavings = allProducts.stream()
.filter(Product::isOnSale)
.map(p -> {
if (p.getOriginalPrice() != null && p.getCurrentPrice() != null) {
return p.getOriginalPrice().subtract(p.getCurrentPrice());
}
return BigDecimal.ZERO;
})
.reduce(BigDecimal.ZERO, BigDecimal::add);
// Beräkna total potentiell intäkt (aktuella priser * lager)
BigDecimal totalPotentialRevenue = allProducts.stream()
.map(p -> {
if (p.getCurrentPrice() != null && p.getStockQuantity() != null) {
return p.getCurrentPrice().multiply(BigDecimal.valueOf(p.getStockQuantity()));
}
return BigDecimal.ZERO;
})
.reduce(BigDecimal.ZERO, BigDecimal::add);
// Genomsnittlig rea-rabatt som BigDecimal
BigDecimal avgDiscount = BigDecimal.ZERO;
try {
if (saleProducts > 0) {
double tempAvg = allProducts.stream()
.filter(Product::isOnSale)
.mapToDouble(p -> p.getDiscountPercentage() != null ? p.getDiscountPercentage().doubleValue() : 0.0)
.average()
.orElse(0.0);
avgDiscount = BigDecimal.valueOf(tempAvg).setScale(2, RoundingMode.HALF_UP);
}
} catch (Exception e) {
logger.warn("Fel vid beräkning av genomsnittlig rabatt", e);
avgDiscount = BigDecimal.ZERO;
}
// Produkter per kategori
Map<String, Long> productsByCategory = allProducts.stream()
.collect(Collectors.groupingBy(
p -> p.getCategory() != null ? p.getCategory() : "Okänd",
Collectors.counting()
));
// REA-produkter per kategori
Map<String, Long> saleProductsByCategory = allProducts.stream()
.filter(Product::isOnSale)
.collect(Collectors.groupingBy(
p -> p.getCategory() != null ? p.getCategory() : "Okänd",
Collectors.counting()
));
// Lägg till i modellen
model.addAttribute("totalProducts", totalProducts);
model.addAttribute("saleProducts", saleProducts);
model.addAttribute("totalSavings", totalSavings);
model.addAttribute("totalRevenue", totalPotentialRevenue); // Total potentiell intäkt
model.addAttribute("avgDiscount", avgDiscount);
model.addAttribute("productsByCategory", productsByCategory);
model.addAttribute("saleProductsByCategory", saleProductsByCategory);
// Order-statistik
model.addAttribute("totalOrders", allOrders.size());
model.addAttribute("recentOrders", allOrders);
// REA-översikt
List<Product> activeDeals = allProducts.stream()
.filter(Product::isOnSale)
.limit(10)
.collect(Collectors.toList());
model.addAttribute("activeDeals", activeDeals);
logger.debug("Försäljningsstatistik - Produkter: {}, REA: {}, Besparingar: {} kr",
totalProducts, saleProducts, totalSavings);
logger.info("Försäljningsrapport genererad framgångsrikt");
} catch (Exception e) {
logger.error("Fel vid generering av försäljningsrapport", e);
// Fallback-värden
model.addAttribute("totalProducts", 0);
model.addAttribute("saleProducts", 0);
model.addAttribute("totalSavings", BigDecimal.ZERO);
model.addAttribute("totalRevenue", BigDecimal.ZERO);
model.addAttribute("avgDiscount", BigDecimal.ZERO);
model.addAttribute("productsByCategory", new HashMap<>());
model.addAttribute("saleProductsByCategory", new HashMap<>());
model.addAttribute("totalOrders", 0);
model.addAttribute("recentOrders", new ArrayList<>());
model.addAttribute("activeDeals", new ArrayList<>());
}
return "admin/reports/sales";
}
@GetMapping("/users")
public String userReport(Model model) {
logger.info("Genererar användarrapport");
// Temporärt inaktiverad - kan implementeras senare
model.addAttribute("totalUsers", 0);
model.addAttribute("adminUsers", 0);
model.addAttribute("customerUsers", 0);
model.addAttribute("users", new ArrayList<>());
logger.debug("Användarrapport: Temporärt inaktiverad");
return "admin/reports/users";
}
/**
* Hjälpmetod för att kontrollera produktdata (används endast för debugging)
*/
@SuppressWarnings("unused")
private void logProductSample(List<Product> products) {
if (logger.isDebugEnabled()) {
logger.debug("Produktsampling (första 3):");
for (int i = 0; i < Math.min(3, products.size()); i++) {
Product p = products.get(i);
logger.debug("[{}] {} - Pris: {} kr - Lager: {} - Kategori: {} - REA: {}",
p.getId(), p.getName(), p.getCurrentPrice(), p.getStockQuantity(),
p.getCategory(), p.isOnSale());
}
}
}
}