Merge pull request #314 from RecentRunner/bug-fixes

Backend bug fixes
This commit was merged in pull request #314.
This commit is contained in:
2026-04-15 16:08:23 -06:00
committed by GitHub
18 changed files with 120 additions and 24 deletions

View File

@@ -7,6 +7,7 @@ import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.dto.chat.UpdateConversationRequest; import com.petshop.backend.dto.chat.UpdateConversationRequest;
import com.petshop.backend.entity.Message; import com.petshop.backend.entity.Message;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.MessageRepository; import com.petshop.backend.repository.MessageRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.ChatAttachmentStorageService; import com.petshop.backend.service.ChatAttachmentStorageService;
@@ -115,7 +116,7 @@ public class ChatController {
public ResponseEntity<Resource> getMessageAttachment(@PathVariable Long messageId) { public ResponseEntity<Resource> getMessageAttachment(@PathVariable Long messageId) {
User user = getCurrentUser(); User user = getCurrentUser();
Message message = messageRepository.findById(messageId) 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())) { if (!chatService.hasConversationAccess(message.getConversationId(), user.getId(), user.getRole())) {
throw new AccessDeniedException("Access denied to this message attachment"); throw new AccessDeniedException("Access denied to this message attachment");

View File

@@ -1,6 +1,7 @@
package com.petshop.backend.controller; package com.petshop.backend.controller;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.EmailService; import com.petshop.backend.service.EmailService;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
@@ -33,7 +34,7 @@ public class ContactController {
@PostMapping @PostMapping
public ResponseEntity<Void> sendContactEmail(@Valid @RequestBody ContactRequest req) { public ResponseEntity<Void> sendContactEmail(@Valid @RequestBody ContactRequest req) {
Long userId = AuthenticationHelper.getAuthenticatedUserId(); 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()); emailService.sendContactMessage(user, req.subject(), req.body());
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }

View File

@@ -40,7 +40,7 @@ public class SaleController {
} }
@PostMapping @PostMapping
@PreAuthorize("hasRole('STAFF')") @PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<SaleResponse> createSale(@Valid @RequestBody SaleRequest request) { public ResponseEntity<SaleResponse> createSale(@Valid @RequestBody SaleRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(saleService.createSale(request)); return ResponseEntity.status(HttpStatus.CREATED).body(saleService.createSale(request));
} }

View File

@@ -35,11 +35,13 @@ public class StoreController {
} }
@PostMapping @PostMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<StoreResponse> createStore(@Valid @RequestBody StoreRequest request) { public ResponseEntity<StoreResponse> createStore(@Valid @RequestBody StoreRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(storeService.createStore(request)); return ResponseEntity.status(HttpStatus.CREATED).body(storeService.createStore(request));
} }
@PutMapping("/{id}") @PutMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<StoreResponse> updateStore( public ResponseEntity<StoreResponse> updateStore(
@PathVariable Long id, @PathVariable Long id,
@Valid @RequestBody StoreRequest request) { @Valid @RequestBody StoreRequest request) {
@@ -47,12 +49,14 @@ public class StoreController {
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> deleteStore(@PathVariable Long id) { public ResponseEntity<Void> deleteStore(@PathVariable Long id) {
storeService.deleteStore(id); storeService.deleteStore(id);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@DeleteMapping @DeleteMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> bulkDeleteStores(@Valid @RequestBody BulkDeleteRequest request) { public ResponseEntity<Void> bulkDeleteStores(@Valid @RequestBody BulkDeleteRequest request) {
storeService.bulkDeleteStores(request); storeService.bulkDeleteStores(request);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();

View File

@@ -20,6 +20,7 @@ public class AdoptionRequest {
private Long employeeId; private Long employeeId;
@NotNull(message = "Source store ID is required")
private Long sourceStoreId; private Long sourceStoreId;
private String paymentMethod; private String paymentMethod;

View File

@@ -1,5 +1,6 @@
package com.petshop.backend.dto.appointment; package com.petshop.backend.dto.appointment;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
@@ -21,7 +22,7 @@ public class AppointmentRequest {
@NotNull(message = "Appointment time is required") @NotNull(message = "Appointment time is required")
private LocalTime appointmentTime; private LocalTime appointmentTime;
@NotNull(message = "Appointment status is required") @NotBlank(message = "Appointment status is required")
private String appointmentStatus; private String appointmentStatus;
private Long petId; private Long petId;

View File

@@ -100,7 +100,7 @@ public class PetRequest {
Objects.equals(petSpecies, that.petSpecies) && Objects.equals(petSpecies, that.petSpecies) &&
Objects.equals(petBreed, that.petBreed) && Objects.equals(petBreed, that.petBreed) &&
Objects.equals(petAge, that.petAge) && Objects.equals(petAge, that.petAge) &&
petStatus == that.petStatus && Objects.equals(petStatus, that.petStatus) &&
Objects.equals(petPrice, that.petPrice); Objects.equals(petPrice, that.petPrice);
} }

View File

@@ -9,6 +9,7 @@ public class SaleItemRequest {
private Long prodId; private Long prodId;
@NotNull(message = "Quantity is required") @NotNull(message = "Quantity is required")
@Positive(message = "Quantity must be positive")
private Integer quantity; private Integer quantity;
public Long getProdId() { public Long getProdId() {

View File

@@ -18,6 +18,7 @@ public class ServiceRequest {
@Positive(message = "Price must be positive") @Positive(message = "Price must be positive")
private BigDecimal servicePrice; private BigDecimal servicePrice;
@NotNull(message = "Service duration is required")
@Positive(message = "Duration must be positive") @Positive(message = "Duration must be positive")
private Integer serviceDuration; private Integer serviceDuration;

View File

@@ -0,0 +1,3 @@
package com.petshop.backend.event;
public record AdoptionConfirmedEvent(Long adoptionId) {}

View File

@@ -0,0 +1,3 @@
package com.petshop.backend.event;
public record AdoptionReminderEvent(Long adoptionId) {}

View File

@@ -0,0 +1,3 @@
package com.petshop.backend.event;
public record AppointmentConfirmedEvent(Long appointmentId) {}

View File

@@ -0,0 +1,3 @@
package com.petshop.backend.event;
public record AppointmentReminderEvent(Long appointmentId) {}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,3 @@
package com.petshop.backend.event;
public record SaleReceiptEvent(Long saleId) {}

View File

@@ -9,11 +9,14 @@ import com.petshop.backend.entity.Sale;
import com.petshop.backend.entity.StoreLocation; import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException; 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.AdoptionRepository;
import com.petshop.backend.repository.PetRepository; import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.SaleRepository; import com.petshop.backend.repository.SaleRepository;
import com.petshop.backend.repository.StoreRepository; import com.petshop.backend.repository.StoreRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import org.springframework.context.ApplicationEventPublisher;
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 org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -40,15 +43,15 @@ public class AdoptionService {
private final UserRepository userRepository; private final UserRepository userRepository;
private final StoreRepository storeRepository; private final StoreRepository storeRepository;
private final SaleRepository saleRepository; 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.adoptionRepository = adoptionRepository;
this.petRepository = petRepository; this.petRepository = petRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
this.saleRepository = saleRepository; 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) { 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()); createSaleForAdoption(adoption, request.getPaymentMethod());
} }
if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus)) { if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus)) {
emailService.sendAdoptionConfirmation(adoption); eventPublisher.publishEvent(new AdoptionConfirmedEvent(adoption.getAdoptionId()));
} }
return mapToResponse(adoption); return mapToResponse(adoption);
} }
@@ -153,7 +156,7 @@ public class AdoptionService {
} }
boolean statusChanged = !adoptionStatus.equalsIgnoreCase(previousStatus); boolean statusChanged = !adoptionStatus.equalsIgnoreCase(previousStatus);
if (statusChanged && (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus))) { if (statusChanged && (ADOPTION_STATUS_PENDING.equalsIgnoreCase(adoptionStatus) || ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus))) {
emailService.sendAdoptionConfirmation(adoption); eventPublisher.publishEvent(new AdoptionConfirmedEvent(adoption.getAdoptionId()));
} }
return mapToResponse(adoption); return mapToResponse(adoption);
} }
@@ -343,7 +346,7 @@ public class AdoptionService {
sale.setIsRefund(false); sale.setIsRefund(false);
sale.setChannel("ADOPTION"); sale.setChannel("ADOPTION");
Sale savedSale = saleRepository.save(sale); 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) { private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId, User customer) {

View File

@@ -10,6 +10,9 @@ import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User; import com.petshop.backend.entity.User;
import com.petshop.backend.exception.BusinessException; import com.petshop.backend.exception.BusinessException;
import com.petshop.backend.exception.ResourceNotFoundException; 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.AdoptionRepository;
import com.petshop.backend.repository.AppointmentRepository; import com.petshop.backend.repository.AppointmentRepository;
import com.petshop.backend.repository.PetRepository; 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.StoreRepository;
import com.petshop.backend.repository.UserRepository; import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
import org.springframework.context.ApplicationEventPublisher;
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 org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
@@ -41,16 +45,16 @@ public class AppointmentService {
private final StoreRepository storeRepository; private final StoreRepository storeRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final AdoptionRepository adoptionRepository; 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.appointmentRepository = appointmentRepository;
this.serviceRepository = serviceRepository; this.serviceRepository = serviceRepository;
this.petRepository = petRepository; this.petRepository = petRepository;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.adoptionRepository = adoptionRepository; this.adoptionRepository = adoptionRepository;
this.emailService = emailService; this.eventPublisher = eventPublisher;
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
@@ -140,7 +144,7 @@ public class AppointmentService {
appointment.setPet(pet); appointment.setPet(pet);
appointment = appointmentRepository.save(appointment); appointment = appointmentRepository.save(appointment);
emailService.sendAppointmentConfirmation(appointment); eventPublisher.publishEvent(new AppointmentConfirmedEvent(appointment.getAppointmentId()));
return mapToResponse(appointment); return mapToResponse(appointment);
} }
@@ -179,7 +183,7 @@ public class AppointmentService {
appointment.setEmployee(employee); appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment); appointment = appointmentRepository.save(appointment);
emailService.sendAppointmentConfirmation(appointment); eventPublisher.publishEvent(new AppointmentConfirmedEvent(appointment.getAppointmentId()));
return mapToResponse(appointment); return mapToResponse(appointment);
} }
@@ -264,7 +268,7 @@ public class AppointmentService {
List<Appointment> pastBookedAppointments = appointmentRepository.findPastBookedAppointments(currentDate, currentTime); List<Appointment> pastBookedAppointments = appointmentRepository.findPastBookedAppointments(currentDate, currentTime);
for (Appointment appointment : pastBookedAppointments) { for (Appointment appointment : pastBookedAppointments) {
appointment.setAppointmentStatus("COMPLETED"); appointment.setAppointmentStatus("Completed");
appointmentRepository.save(appointment); appointmentRepository.save(appointment);
} }
@@ -273,13 +277,13 @@ public class AppointmentService {
List<Appointment> tomorrowAppointments = appointmentRepository List<Appointment> tomorrowAppointments = appointmentRepository
.findByAppointmentDateAndAppointmentStatusIgnoreCase(tomorrow, "Booked"); .findByAppointmentDateAndAppointmentStatusIgnoreCase(tomorrow, "Booked");
for (Appointment appointment : tomorrowAppointments) { for (Appointment appointment : tomorrowAppointments) {
emailService.sendAppointmentReminder(appointment); eventPublisher.publishEvent(new AppointmentReminderEvent(appointment.getAppointmentId()));
} }
List<Adoption> tomorrowAdoptions = adoptionRepository List<Adoption> tomorrowAdoptions = adoptionRepository
.findByAdoptionDateAndAdoptionStatusIgnoreCase(tomorrow, "Pending"); .findByAdoptionDateAndAdoptionStatusIgnoreCase(tomorrow, "Pending");
for (Adoption adoption : tomorrowAdoptions) { for (Adoption adoption : tomorrowAdoptions) {
emailService.sendAdoptionReminder(adoption); eventPublisher.publishEvent(new AdoptionReminderEvent(adoption.getAdoptionId()));
} }
} }

View File

@@ -5,8 +5,10 @@ import com.petshop.backend.dto.sale.SaleResponse;
import com.petshop.backend.entity.*; import com.petshop.backend.entity.*;
import com.petshop.backend.exception.BusinessException; import com.petshop.backend.exception.BusinessException;
import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.event.SaleReceiptEvent;
import com.petshop.backend.repository.*; import com.petshop.backend.repository.*;
import com.petshop.backend.util.AuthenticationHelper; import com.petshop.backend.util.AuthenticationHelper;
import org.springframework.context.ApplicationEventPublisher;
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 org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -31,9 +33,9 @@ public class SaleService {
private final UserRepository userRepository; private final UserRepository userRepository;
private final CouponRepository couponRepository; private final CouponRepository couponRepository;
private final CartRepository cartRepository; 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.saleRepository = saleRepository;
this.productRepository = productRepository; this.productRepository = productRepository;
this.storeRepository = storeRepository; this.storeRepository = storeRepository;
@@ -41,7 +43,7 @@ public class SaleService {
this.userRepository = userRepository; this.userRepository = userRepository;
this.couponRepository = couponRepository; this.couponRepository = couponRepository;
this.cartRepository = cartRepository; this.cartRepository = cartRepository;
this.emailService = emailService; this.eventPublisher = eventPublisher;
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
@@ -172,7 +174,7 @@ public class SaleService {
BigDecimal refundTotal; BigDecimal refundTotal;
if (originalSubtotal != null && originalSubtotal.compareTo(BigDecimal.ZERO) > 0) { 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); refundTotal = originalSale.getTotalAmount().abs().multiply(ratio).negate().setScale(2, RoundingMode.HALF_UP);
if (originalSale.getLoyaltyDiscountAmount() != null) { if (originalSale.getLoyaltyDiscountAmount() != null) {
loyaltyDiscountRefunded = originalSale.getLoyaltyDiscountAmount().multiply(ratio).setScale(2, RoundingMode.HALF_UP); loyaltyDiscountRefunded = originalSale.getLoyaltyDiscountAmount().multiply(ratio).setScale(2, RoundingMode.HALF_UP);
@@ -274,7 +276,7 @@ public class SaleService {
Sale savedSale = saleRepository.save(sale); Sale savedSale = saleRepository.save(sale);
if (!Boolean.TRUE.equals(savedSale.getIsRefund()) && savedSale.getCustomer() != null) { if (!Boolean.TRUE.equals(savedSale.getIsRefund()) && savedSale.getCustomer() != null) {
emailService.sendPurchaseReceipt(savedSale); eventPublisher.publishEvent(new SaleReceiptEvent(savedSale.getSaleId()));
} }
return mapToResponse(savedSale); return mapToResponse(savedSale);