fix coupon analytics

This commit is contained in:
2026-04-15 13:39:06 -06:00
parent 654015103b
commit 6555624b81
4 changed files with 40 additions and 4 deletions

View File

@@ -5,12 +5,15 @@ import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AnalyticsService; import com.petshop.backend.service.AnalyticsService;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ResponseStatusException;
import java.time.LocalDate;
@RestController @RestController
@RequestMapping("/api/v1/analytics") @RequestMapping("/api/v1/analytics")
@PreAuthorize("hasAnyRole('ADMIN', 'STAFF')") @PreAuthorize("hasAnyRole('ADMIN', 'STAFF')")
@@ -27,7 +30,11 @@ public class AnalyticsController {
@GetMapping("/dashboard") @GetMapping("/dashboard")
public ResponseEntity<DashboardResponse> getDashboard( public ResponseEntity<DashboardResponse> getDashboard(
@RequestParam(defaultValue = "30") int days, @RequestParam(defaultValue = "30") int days,
@RequestParam(defaultValue = "10") int top) { @RequestParam(defaultValue = "10") int top,
@RequestParam(required = false) String paymentMethod,
@RequestParam(required = false) Long storeId,
@RequestParam(required = false) String channel,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
if (days < 1 || days > 365) { if (days < 1 || days > 365) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "days must be between 1 and 365"); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "days must be between 1 and 365");
} }
@@ -35,6 +42,7 @@ public class AnalyticsController {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "top must be between 1 and 50"); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "top must be between 1 and 50");
} }
User user = AuthenticationHelper.getAuthenticatedUser(userRepository); User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
return ResponseEntity.ok(analyticsService.getDashboardData(days, top, user)); java.time.LocalDateTime endDateTime = endDate != null ? endDate.plusDays(1).atStartOfDay() : null;
return ResponseEntity.ok(analyticsService.getDashboardData(days, top, user, paymentMethod, storeId, channel, endDateTime));
} }
} }

View File

@@ -3,6 +3,7 @@ package com.petshop.backend.repository;
import com.petshop.backend.entity.Sale; import com.petshop.backend.entity.Sale;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import java.time.LocalDateTime;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
@@ -26,6 +27,19 @@ public interface SaleRepository extends JpaRepository<Sale, Long> {
"(:customerId IS NULL OR s.customer.id = :customerId)") "(:customerId IS NULL OR s.customer.id = :customerId)")
Page<Sale> searchSales(@Param("q") String query, @Param("paymentMethod") String paymentMethod, @Param("storeId") Long storeId, @Param("isRefund") Boolean isRefund, @Param("customerId") Long customerId, Pageable pageable); Page<Sale> searchSales(@Param("q") String query, @Param("paymentMethod") String paymentMethod, @Param("storeId") Long storeId, @Param("isRefund") Boolean isRefund, @Param("customerId") Long customerId, Pageable pageable);
long countByCoupon_CouponId(Long couponId);
@Query("SELECT s FROM Sale s WHERE s.saleDate > :startDate " +
"AND (:endDate IS NULL OR s.saleDate <= :endDate) " +
"AND (:paymentMethod IS NULL OR LOWER(s.paymentMethod) = LOWER(:paymentMethod)) " +
"AND (:storeId IS NULL OR s.store.storeId = :storeId) " +
"AND (:channel IS NULL OR s.channel = :channel)")
List<Sale> findForAnalytics(@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate,
@Param("paymentMethod") String paymentMethod,
@Param("storeId") Long storeId,
@Param("channel") String channel);
List<Sale> findByOriginalSaleSaleId(Long originalSaleId); List<Sale> findByOriginalSaleSaleId(Long originalSaleId);
Optional<Sale> findByCartCartId(Long cartId); Optional<Sale> findByCartCartId(Long cartId);

View File

@@ -34,10 +34,17 @@ public class AnalyticsService {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public DashboardResponse getDashboardData(int days, int top, User user) { public DashboardResponse getDashboardData(int days, int top, User user) {
return getDashboardData(days, top, user, null, null, null, null);
}
@Transactional(readOnly = true)
public DashboardResponse getDashboardData(int days, int top, User user,
String paymentMethod, Long storeId,
String channel, LocalDateTime endDate) {
LocalDateTime startDate = LocalDateTime.now().minusDays(days); LocalDateTime startDate = LocalDateTime.now().minusDays(days);
List<Sale> sales = saleRepository.findAll().stream() List<Sale> sales = saleRepository.findForAnalytics(startDate, endDate, paymentMethod, storeId, channel)
.filter(sale -> sale.getSaleDate().isAfter(startDate)) .stream()
.filter(sale -> includeSaleForUser(sale, user)) .filter(sale -> includeSaleForUser(sale, user))
.collect(Collectors.toList()); .collect(Collectors.toList());

View File

@@ -206,6 +206,13 @@ public class CartService {
throw new BusinessException("Minimum order amount of $" + coupon.getMinOrderAmount() + " required"); throw new BusinessException("Minimum order amount of $" + coupon.getMinOrderAmount() + " required");
} }
if (coupon.getUsageLimit() != null) {
long used = saleRepository.countByCoupon_CouponId(coupon.getCouponId());
if (used >= coupon.getUsageLimit()) {
throw new BusinessException("Coupon usage limit has been reached");
}
}
cart.setCoupon(coupon); cart.setCoupon(coupon);
recalculate(cart); recalculate(cart);