diff --git a/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java b/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java index 0015048..13a8b9d 100644 --- a/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java +++ b/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java @@ -257,7 +257,7 @@ private void seedOrders( .id(UUID.randomUUID()) .customer(customers.get(1)).courier(couriers.get(1)) .region(regions.get(1)) - .status(Order.OrderStatus.SHIPPED) + .status(Order.OrderStatus.DELIVERED) .orderPlacedTimestamp(placed2) .shippedTimestamp(placed2.plusDays(ORDER_2_SHIP_DAYS) .plusHours(ORDER_2_SHIP_HOURS)) @@ -283,9 +283,8 @@ private void seedOrders( .id(UUID.randomUUID()) .customer(customers.get(0)).courier(couriers.get(0)) .region(regions.get(2)) - .status(Order.OrderStatus.DELIVERY_FAILED) .orderPlacedTimestamp(placed3) - .status(Order.OrderStatus.DELIVERY_FAILED) + .status(Order.OrderStatus.DELIVERED) .orderPlacedTimestamp(placed3) .shippedTimestamp(placed3.plusHours(ORDER_3_SHIP_HOURS)) .deliveredTimestamp(null) diff --git a/src/main/java/com/Podzilla/analytics/models/Product.java b/src/main/java/com/Podzilla/analytics/models/Product.java index fc6223e..bda85d5 100644 --- a/src/main/java/com/Podzilla/analytics/models/Product.java +++ b/src/main/java/com/Podzilla/analytics/models/Product.java @@ -2,14 +2,18 @@ import java.math.BigDecimal; +import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.UUID; +import java.util.List; + @Entity @Table(name = "products") @Data @@ -23,6 +27,9 @@ public class Product { private BigDecimal cost; private int lowStockThreshold; + @OneToMany(mappedBy = "product", cascade = CascadeType.ALL) + private List orderItems; + public static Builder builder() { return new Builder(); } @@ -33,8 +40,10 @@ public static class Builder { private String category; private BigDecimal cost; private int lowStockThreshold; + private List orderItems; - public Builder() { } + public Builder() { + } public Builder id(final UUID id) { this.id = id; @@ -61,8 +70,14 @@ public Builder lowStockThreshold(final int lowStockThreshold) { return this; } + public Builder orderItems(final List orderItems) { + this.orderItems = orderItems; + return this; + } + public Product build() { - return new Product(id, name, category, cost, lowStockThreshold); + return new Product( + id, name, category, cost, lowStockThreshold, orderItems); } } } diff --git a/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java b/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java index c3e38da..b85869a 100644 --- a/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java +++ b/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java @@ -1,6 +1,5 @@ package com.Podzilla.analytics.repositories; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -149,8 +148,8 @@ OrderFailureRateProjection calculateFailureRate( + "GROUP BY period " + "ORDER BY totalRevenue DESC") List findRevenueSummaryByPeriod( - @Param("startDate") LocalDate startDate, - @Param("endDate") LocalDate endDate, + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate, @Param("reportPeriod") String reportPeriod); @Query("SELECT p.category AS category, " @@ -164,6 +163,6 @@ List findRevenueSummaryByPeriod( + "GROUP BY p.category " + "ORDER BY totalRevenue DESC") List findRevenueByCategory( - @Param("startDate") LocalDate startDate, - @Param("endDate") LocalDate endDate); + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate); } diff --git a/src/main/java/com/Podzilla/analytics/repositories/ProductRepository.java b/src/main/java/com/Podzilla/analytics/repositories/ProductRepository.java index fa50cb5..f4037b1 100644 --- a/src/main/java/com/Podzilla/analytics/repositories/ProductRepository.java +++ b/src/main/java/com/Podzilla/analytics/repositories/ProductRepository.java @@ -10,28 +10,38 @@ import com.Podzilla.analytics.api.projections.product.TopSellingProductProjection; import com.Podzilla.analytics.models.Product; import java.util.UUID; + public interface ProductRepository extends JpaRepository { @Query("SELECT p.id AS id, " + "p.name AS name, " + "p.category AS category, " - + "SUM(oi.quantity * oi.pricePerUnit) AS totalRevenue, " - + "SUM(oi.quantity) AS totalUnits " - + "FROM OrderItem oi " - + "JOIN oi.order o " - + "JOIN oi.product p " - + "WHERE o.finalStatusTimestamp >= :startDate " - + "AND o.finalStatusTimestamp < :endDate " - + "AND o.status = 'DELIVERED' " + + "COALESCE(SUM(CASE WHEN o.finalStatusTimestamp >= :startDate " + + "AND o.finalStatusTimestamp < :endDate " + + "AND o.status = 'DELIVERED' THEN oi.quantity * oi.pricePerUnit " + + "ELSE 0 END), 0) " + + "AS totalRevenue, " + + "COALESCE(SUM(CASE WHEN o.finalStatusTimestamp >= :startDate " + + "AND o.finalStatusTimestamp < :endDate " + + "AND o.status = 'DELIVERED' THEN oi.quantity ELSE 0 END), 0) " + + "AS totalUnits " + + "FROM Product p " + + "LEFT JOIN p.orderItems oi " + + "LEFT JOIN oi.order o " + "GROUP BY p.id, p.name, p.category " + "ORDER BY CASE WHEN :sortBy = 'REVENUE' " - + "THEN SUM(oi.quantity * oi.pricePerUnit) " - + " WHEN :sortBy = 'UNITS' THEN SUM(oi.quantity) " - + " ELSE SUM(oi.quantity * oi.pricePerUnit) END DESC") + + "THEN COALESCE(SUM(CASE WHEN o.finalStatusTimestamp >= " + + ":startDate AND o.finalStatusTimestamp < :endDate " + + "AND o.status = 'DELIVERED' THEN oi.quantity * oi.pricePerUnit " + + "ELSE 0 END), 0) " + + "ELSE COALESCE(SUM(CASE WHEN o.finalStatusTimestamp >= " + + ":startDate AND o.finalStatusTimestamp < :endDate " + + "AND o.status = 'DELIVERED' THEN oi.quantity ELSE 0 END), 0) " + + "END DESC " + + "LIMIT :limit") List findTopSellers( @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate, @Param("limit") Integer limit, - @Param("sortBy") String sortBy // Pass the enum name as a String - ); + @Param("sortBy") String sortBy); } diff --git a/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java b/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java index 222a8e2..0de6380 100644 --- a/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java +++ b/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java @@ -1,6 +1,8 @@ package com.Podzilla.analytics.services; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.ArrayList; import java.util.List; @@ -25,9 +27,13 @@ public List getRevenueSummary( final LocalDate endDate, final String periodString) { + LocalDateTime startDateTime = startDate.atStartOfDay(); + LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX); + final List revenueData = orderRepository - .findRevenueSummaryByPeriod(startDate, - endDate, periodString); + .findRevenueSummaryByPeriod( + startDateTime, + endDateTime, periodString); final List summaryList = new ArrayList<>(); @@ -55,9 +61,13 @@ public List getRevenueSummary( public List getRevenueByCategory( final LocalDate startDate, final LocalDate endDate) { + LocalDateTime startDateTime = startDate.atStartOfDay(); + LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX); + final List queryResults = orderRepository - .findRevenueByCategory(startDate, - endDate); + .findRevenueByCategory( + startDateTime, + endDateTime); final List summaryList = new ArrayList<>(); diff --git a/src/test/java/com/Podzilla/analytics/services/RevenueReportServiceTest.java b/src/test/java/com/Podzilla/analytics/services/RevenueReportServiceTest.java index f007740..8cd3c6f 100644 --- a/src/test/java/com/Podzilla/analytics/services/RevenueReportServiceTest.java +++ b/src/test/java/com/Podzilla/analytics/services/RevenueReportServiceTest.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.time.LocalDate; +import java.time.LocalTime; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -52,7 +53,8 @@ void getRevenueSummary_WithValidData_ShouldReturnCorrectSummary() { summaryProjection(LocalDate.of(2025, 1, 1), new BigDecimal("1000.00")), summaryProjection(LocalDate.of(2025, 2, 1), new BigDecimal("2000.00"))); - when(orderRepository.findRevenueSummaryByPeriod(eq(startDate), eq(endDate), eq("MONTHLY"))) + when(orderRepository.findRevenueSummaryByPeriod(eq(startDate.atStartOfDay()), eq(endDate.atTime(LocalTime.MAX)), + eq("MONTHLY"))) .thenReturn(projections); // Act @@ -120,7 +122,8 @@ void getRevenueByCategory_WithValidData_ShouldReturnCorrectCategories() { categoryProjection("Books", new BigDecimal("3000.00")), categoryProjection("Electronics", new BigDecimal("5000.00"))); - when(orderRepository.findRevenueByCategory(eq(startDate), eq(endDate))) + when(orderRepository.findRevenueByCategory(eq(startDate.atStartOfDay()), eq(endDate.atTime( + LocalTime.MAX)))) .thenReturn(projections);// Act List result = revenueReportService.getRevenueByCategory(startDate, endDate); @@ -167,7 +170,8 @@ public BigDecimal getTotalRevenue() { } }); - when(orderRepository.findRevenueByCategory(eq(startDate), eq(endDate))) + when(orderRepository.findRevenueByCategory(eq(startDate.atStartOfDay()), eq(endDate.atTime( + LocalTime.MAX)))) .thenReturn(projections); // Act