Backend bug fixes #314
@@ -7,6 +7,7 @@ import com.petshop.backend.dto.chat.MessageResponse;
|
||||
import com.petshop.backend.dto.chat.UpdateConversationRequest;
|
||||
import com.petshop.backend.entity.Message;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.repository.MessageRepository;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import com.petshop.backend.service.ChatAttachmentStorageService;
|
||||
@@ -115,7 +116,7 @@ public class ChatController {
|
||||
public ResponseEntity<Resource> getMessageAttachment(@PathVariable Long messageId) {
|
||||
User user = getCurrentUser();
|
||||
Message message = messageRepository.findById(messageId)
|
||||
.orElseThrow(() -> new RuntimeException("Message not found"));
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Message not found with id: " + messageId));
|
||||
|
||||
if (!chatService.hasConversationAccess(message.getConversationId(), user.getId(), user.getRole())) {
|
||||
throw new AccessDeniedException("Access denied to this message attachment");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.petshop.backend.controller;
|
||||
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import com.petshop.backend.service.EmailService;
|
||||
import com.petshop.backend.util.AuthenticationHelper;
|
||||
@@ -33,7 +34,7 @@ public class ContactController {
|
||||
@PostMapping
|
||||
public ResponseEntity<Void> sendContactEmail(@Valid @RequestBody ContactRequest req) {
|
||||
Long userId = AuthenticationHelper.getAuthenticatedUserId();
|
||||
User user = userRepository.findById(userId).orElseThrow();
|
||||
User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId));
|
||||
emailService.sendContactMessage(user, req.subject(), req.body());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class SaleController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasRole('STAFF')")
|
||||
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
|
||||
public ResponseEntity<SaleResponse> createSale(@Valid @RequestBody SaleRequest request) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(saleService.createSale(request));
|
||||
}
|
||||
|
||||
@@ -35,11 +35,13 @@ public class StoreController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<StoreResponse> createStore(@Valid @RequestBody StoreRequest request) {
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(storeService.createStore(request));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<StoreResponse> updateStore(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody StoreRequest request) {
|
||||
@@ -47,12 +49,14 @@ public class StoreController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<Void> deleteStore(@PathVariable Long id) {
|
||||
storeService.deleteStore(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<Void> bulkDeleteStores(@Valid @RequestBody BulkDeleteRequest request) {
|
||||
storeService.bulkDeleteStores(request);
|
||||
return ResponseEntity.noContent().build();
|
||||
|
||||
@@ -20,6 +20,7 @@ public class AdoptionRequest {
|
||||
|
||||
private Long employeeId;
|
||||
|
||||
@NotNull(message = "Source store ID is required")
|
||||
private Long sourceStoreId;
|
||||
|
||||
private String paymentMethod;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.petshop.backend.dto.appointment;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
@@ -21,7 +22,7 @@ public class AppointmentRequest {
|
||||
@NotNull(message = "Appointment time is required")
|
||||
private LocalTime appointmentTime;
|
||||
|
||||
@NotNull(message = "Appointment status is required")
|
||||
@NotBlank(message = "Appointment status is required")
|
||||
private String appointmentStatus;
|
||||
|
||||
private Long petId;
|
||||
|
||||
@@ -100,7 +100,7 @@ public class PetRequest {
|
||||
Objects.equals(petSpecies, that.petSpecies) &&
|
||||
Objects.equals(petBreed, that.petBreed) &&
|
||||
Objects.equals(petAge, that.petAge) &&
|
||||
petStatus == that.petStatus &&
|
||||
Objects.equals(petStatus, that.petStatus) &&
|
||||
Objects.equals(petPrice, that.petPrice);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ public class SaleItemRequest {
|
||||
private Long prodId;
|
||||
|
||||
@NotNull(message = "Quantity is required")
|
||||
@Positive(message = "Quantity must be positive")
|
||||
private Integer quantity;
|
||||
|
||||
public Long getProdId() {
|
||||
|
||||
@@ -18,6 +18,7 @@ public class ServiceRequest {
|
||||
@Positive(message = "Price must be positive")
|
||||
private BigDecimal servicePrice;
|
||||
|
||||
@NotNull(message = "Service duration is required")
|
||||
@Positive(message = "Duration must be positive")
|
||||
private Integer serviceDuration;
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.petshop.backend.event;
|
||||
|
||||
public record AdoptionConfirmedEvent(Long adoptionId) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.petshop.backend.event;
|
||||
|
||||
public record AdoptionReminderEvent(Long adoptionId) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.petshop.backend.event;
|
||||
|
||||
public record AppointmentConfirmedEvent(Long appointmentId) {}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.petshop.backend.event;
|
||||
|
||||
public record AppointmentReminderEvent(Long appointmentId) {}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.petshop.backend.event;
|
||||
|
||||
import com.petshop.backend.repository.AdoptionRepository;
|
||||
import com.petshop.backend.repository.AppointmentRepository;
|
||||
import com.petshop.backend.repository.SaleRepository;
|
||||
import com.petshop.backend.service.EmailService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.event.TransactionPhase;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
@Component
|
||||
public class EmailEventListener {
|
||||
|
||||
private final AdoptionRepository adoptionRepository;
|
||||
private final AppointmentRepository appointmentRepository;
|
||||
private final SaleRepository saleRepository;
|
||||
private final EmailService emailService;
|
||||
|
||||
public EmailEventListener(AdoptionRepository adoptionRepository, AppointmentRepository appointmentRepository, SaleRepository saleRepository, EmailService emailService) {
|
||||
this.adoptionRepository = adoptionRepository;
|
||||
this.appointmentRepository = appointmentRepository;
|
||||
this.saleRepository = saleRepository;
|
||||
this.emailService = emailService;
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void onAdoptionConfirmed(AdoptionConfirmedEvent event) {
|
||||
adoptionRepository.findById(event.adoptionId())
|
||||
.ifPresent(emailService::sendAdoptionConfirmation);
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void onAdoptionReminder(AdoptionReminderEvent event) {
|
||||
adoptionRepository.findById(event.adoptionId())
|
||||
.ifPresent(emailService::sendAdoptionReminder);
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void onAppointmentConfirmed(AppointmentConfirmedEvent event) {
|
||||
appointmentRepository.findById(event.appointmentId())
|
||||
.ifPresent(emailService::sendAppointmentConfirmation);
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void onAppointmentReminder(AppointmentReminderEvent event) {
|
||||
appointmentRepository.findById(event.appointmentId())
|
||||
.ifPresent(emailService::sendAppointmentReminder);
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void onSaleReceipt(SaleReceiptEvent event) {
|
||||
saleRepository.findById(event.saleId())
|
||||
.ifPresent(emailService::sendPurchaseReceipt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.petshop.backend.event;
|
||||
|
||||
public record SaleReceiptEvent(Long saleId) {}
|
||||
@@ -9,11 +9,14 @@ import com.petshop.backend.entity.Sale;
|
||||
import com.petshop.backend.entity.StoreLocation;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.event.AdoptionConfirmedEvent;
|
||||
import com.petshop.backend.event.SaleReceiptEvent;
|
||||
import com.petshop.backend.repository.AdoptionRepository;
|
||||
import com.petshop.backend.repository.PetRepository;
|
||||
import com.petshop.backend.repository.SaleRepository;
|
||||
import com.petshop.backend.repository.StoreRepository;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -40,15 +43,15 @@ public class AdoptionService {
|
||||
private final UserRepository userRepository;
|
||||
private final StoreRepository storeRepository;
|
||||
private final SaleRepository saleRepository;
|
||||
private final EmailService emailService;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, UserRepository userRepository, StoreRepository storeRepository, SaleRepository saleRepository, EmailService emailService) {
|
||||
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, UserRepository userRepository, StoreRepository storeRepository, SaleRepository saleRepository, ApplicationEventPublisher eventPublisher) {
|
||||
this.adoptionRepository = adoptionRepository;
|
||||
this.petRepository = petRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.storeRepository = storeRepository;
|
||||
this.saleRepository = saleRepository;
|
||||
this.emailService = emailService;
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
public Page<AdoptionResponse> getAllAdoptions(String query, Long customerId, String status, Long storeId, LocalDate date, Pageable pageable) {
|
||||
@@ -110,7 +113,7 @@ public class AdoptionService {
|
||||
createSaleForAdoption(adoption, request.getPaymentMethod());
|
||||
}
|
||||
if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus)) {
|
||||
emailService.sendAdoptionConfirmation(adoption);
|
||||
eventPublisher.publishEvent(new AdoptionConfirmedEvent(adoption.getAdoptionId()));
|
||||
}
|
||||
return mapToResponse(adoption);
|
||||
}
|
||||
@@ -153,7 +156,7 @@ public class AdoptionService {
|
||||
}
|
||||
boolean statusChanged = !adoptionStatus.equalsIgnoreCase(previousStatus);
|
||||
if (statusChanged && (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus))) {
|
||||
emailService.sendAdoptionConfirmation(adoption);
|
||||
eventPublisher.publishEvent(new AdoptionConfirmedEvent(adoption.getAdoptionId()));
|
||||
}
|
||||
return mapToResponse(adoption);
|
||||
}
|
||||
@@ -343,7 +346,7 @@ public class AdoptionService {
|
||||
sale.setIsRefund(false);
|
||||
sale.setChannel("ADOPTION");
|
||||
Sale savedSale = saleRepository.save(sale);
|
||||
emailService.sendPurchaseReceipt(savedSale);
|
||||
eventPublisher.publishEvent(new SaleReceiptEvent(savedSale.getSaleId()));
|
||||
}
|
||||
|
||||
private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId, User customer) {
|
||||
|
||||
@@ -10,6 +10,9 @@ import com.petshop.backend.entity.StoreLocation;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.exception.BusinessException;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.event.AdoptionReminderEvent;
|
||||
import com.petshop.backend.event.AppointmentConfirmedEvent;
|
||||
import com.petshop.backend.event.AppointmentReminderEvent;
|
||||
import com.petshop.backend.repository.AdoptionRepository;
|
||||
import com.petshop.backend.repository.AppointmentRepository;
|
||||
import com.petshop.backend.repository.PetRepository;
|
||||
@@ -17,6 +20,7 @@ import com.petshop.backend.repository.ServiceRepository;
|
||||
import com.petshop.backend.repository.StoreRepository;
|
||||
import com.petshop.backend.repository.UserRepository;
|
||||
import com.petshop.backend.util.AuthenticationHelper;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -41,16 +45,16 @@ public class AppointmentService {
|
||||
private final StoreRepository storeRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final AdoptionRepository adoptionRepository;
|
||||
private final EmailService emailService;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, AdoptionRepository adoptionRepository, EmailService emailService) {
|
||||
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, AdoptionRepository adoptionRepository, ApplicationEventPublisher eventPublisher) {
|
||||
this.appointmentRepository = appointmentRepository;
|
||||
this.serviceRepository = serviceRepository;
|
||||
this.petRepository = petRepository;
|
||||
this.storeRepository = storeRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.adoptionRepository = adoptionRepository;
|
||||
this.emailService = emailService;
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@@ -140,7 +144,7 @@ public class AppointmentService {
|
||||
appointment.setPet(pet);
|
||||
|
||||
appointment = appointmentRepository.save(appointment);
|
||||
emailService.sendAppointmentConfirmation(appointment);
|
||||
eventPublisher.publishEvent(new AppointmentConfirmedEvent(appointment.getAppointmentId()));
|
||||
return mapToResponse(appointment);
|
||||
}
|
||||
|
||||
@@ -179,7 +183,7 @@ public class AppointmentService {
|
||||
appointment.setEmployee(employee);
|
||||
|
||||
appointment = appointmentRepository.save(appointment);
|
||||
emailService.sendAppointmentConfirmation(appointment);
|
||||
eventPublisher.publishEvent(new AppointmentConfirmedEvent(appointment.getAppointmentId()));
|
||||
return mapToResponse(appointment);
|
||||
}
|
||||
|
||||
@@ -264,7 +268,7 @@ public class AppointmentService {
|
||||
List<Appointment> pastBookedAppointments = appointmentRepository.findPastBookedAppointments(currentDate, currentTime);
|
||||
|
||||
for (Appointment appointment : pastBookedAppointments) {
|
||||
appointment.setAppointmentStatus("COMPLETED");
|
||||
appointment.setAppointmentStatus("Completed");
|
||||
appointmentRepository.save(appointment);
|
||||
}
|
||||
|
||||
@@ -273,13 +277,13 @@ public class AppointmentService {
|
||||
List<Appointment> tomorrowAppointments = appointmentRepository
|
||||
.findByAppointmentDateAndAppointmentStatusIgnoreCase(tomorrow, "Booked");
|
||||
for (Appointment appointment : tomorrowAppointments) {
|
||||
emailService.sendAppointmentReminder(appointment);
|
||||
eventPublisher.publishEvent(new AppointmentReminderEvent(appointment.getAppointmentId()));
|
||||
}
|
||||
|
||||
List<Adoption> tomorrowAdoptions = adoptionRepository
|
||||
.findByAdoptionDateAndAdoptionStatusIgnoreCase(tomorrow, "Pending");
|
||||
for (Adoption adoption : tomorrowAdoptions) {
|
||||
emailService.sendAdoptionReminder(adoption);
|
||||
eventPublisher.publishEvent(new AdoptionReminderEvent(adoption.getAdoptionId()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@ import com.petshop.backend.dto.sale.SaleResponse;
|
||||
import com.petshop.backend.entity.*;
|
||||
import com.petshop.backend.exception.BusinessException;
|
||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||
import com.petshop.backend.event.SaleReceiptEvent;
|
||||
import com.petshop.backend.repository.*;
|
||||
import com.petshop.backend.util.AuthenticationHelper;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -31,9 +33,9 @@ public class SaleService {
|
||||
private final UserRepository userRepository;
|
||||
private final CouponRepository couponRepository;
|
||||
private final CartRepository cartRepository;
|
||||
private final EmailService emailService;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository, CouponRepository couponRepository, CartRepository cartRepository, EmailService emailService) {
|
||||
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository, CouponRepository couponRepository, CartRepository cartRepository, ApplicationEventPublisher eventPublisher) {
|
||||
this.saleRepository = saleRepository;
|
||||
this.productRepository = productRepository;
|
||||
this.storeRepository = storeRepository;
|
||||
@@ -41,7 +43,7 @@ public class SaleService {
|
||||
this.userRepository = userRepository;
|
||||
this.couponRepository = couponRepository;
|
||||
this.cartRepository = cartRepository;
|
||||
this.emailService = emailService;
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@@ -172,7 +174,7 @@ public class SaleService {
|
||||
BigDecimal refundTotal;
|
||||
|
||||
if (originalSubtotal != null && originalSubtotal.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal ratio = subtotalAmount.divide(originalSubtotal, 10, RoundingMode.HALF_UP);
|
||||
BigDecimal ratio = subtotalAmount.abs().divide(originalSubtotal, 10, RoundingMode.HALF_UP);
|
||||
refundTotal = originalSale.getTotalAmount().abs().multiply(ratio).negate().setScale(2, RoundingMode.HALF_UP);
|
||||
if (originalSale.getLoyaltyDiscountAmount() != null) {
|
||||
loyaltyDiscountRefunded = originalSale.getLoyaltyDiscountAmount().multiply(ratio).setScale(2, RoundingMode.HALF_UP);
|
||||
@@ -272,7 +274,7 @@ public class SaleService {
|
||||
Sale savedSale = saleRepository.save(sale);
|
||||
|
||||
if (!Boolean.TRUE.equals(savedSale.getIsRefund()) && savedSale.getCustomer() != null) {
|
||||
emailService.sendPurchaseReceipt(savedSale);
|
||||
eventPublisher.publishEvent(new SaleReceiptEvent(savedSale.getSaleId()));
|
||||
}
|
||||
|
||||
return mapToResponse(savedSale);
|
||||
|
||||
Reference in New Issue
Block a user