added points to sale and logic backend

This commit is contained in:
Alex
2026-04-13 19:46:28 -06:00
parent 9dad410c54
commit f13bb90aa0
6 changed files with 114 additions and 5 deletions

View File

@@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Objects;
import java.math.BigDecimal;
public class SaleRequest {
@NotNull(message = "Store ID is required")
@@ -28,6 +29,10 @@ public class SaleRequest {
private Long cartId;
private Integer pointsUsed;
private BigDecimal pointsDiscountAmount;
public Long getStoreId() {
return storeId;
}
@@ -100,6 +105,22 @@ public class SaleRequest {
this.cartId = cartId;
}
public Integer getPointsUsed() {
return pointsUsed;
}
public void setPointsUsed(Integer pointsUsed) {
this.pointsUsed = pointsUsed;
}
public BigDecimal getPointsDiscountAmount() {
return pointsDiscountAmount;
}
public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) {
this.pointsDiscountAmount = pointsDiscountAmount;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -19,6 +19,8 @@ public class SaleResponse {
private BigDecimal couponDiscountAmount;
private BigDecimal employeeDiscountAmount;
private Integer pointsEarned;
private Integer pointsUsed;
private BigDecimal pointsDiscountAmount;
private String channel;
private Long couponId;
private Long cartId;
@@ -135,6 +137,22 @@ public class SaleResponse {
this.pointsEarned = pointsEarned;
}
public Integer getPointsUsed() {
return pointsUsed;
}
public void setPointsUsed(Integer pointsUsed) {
this.pointsUsed = pointsUsed;
}
public BigDecimal getPointsDiscountAmount() {
return pointsDiscountAmount;
}
public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) {
this.pointsDiscountAmount = pointsDiscountAmount;
}
public String getChannel() {
return channel;
}

View File

@@ -69,6 +69,12 @@ public class Sale {
@Column(nullable = false)
private Integer pointsEarned = 0;
@Column(nullable = false)
private Integer pointsUsed = 0;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal pointsDiscountAmount = BigDecimal.ZERO;
@OneToMany(mappedBy = "sale", cascade = CascadeType.ALL)
private List<SaleItem> items = new ArrayList<>();
@@ -211,6 +217,22 @@ public class Sale {
this.pointsEarned = pointsEarned;
}
public Integer getPointsUsed() {
return pointsUsed;
}
public void setPointsUsed(Integer pointsUsed) {
this.pointsUsed = pointsUsed;
}
public BigDecimal getPointsDiscountAmount() {
return pointsDiscountAmount;
}
public void setPointsDiscountAmount(BigDecimal pointsDiscountAmount) {
this.pointsDiscountAmount = pointsDiscountAmount;
}
public List<SaleItem> getItems() {
return items;
}

View File

@@ -152,9 +152,33 @@ public class SaleService {
saleItems.add(saleItem);
subtotalAmount = subtotalAmount.add(itemTotal);
}
subtotalAmount = subtotalAmount.negate();
sale.setSubtotalAmount(subtotalAmount);
sale.setTotalAmount(subtotalAmount);
Sale originalSale = sale.getOriginalSale();
BigDecimal originalSubtotal = originalSale.getSubtotalAmount() != null
? originalSale.getSubtotalAmount()
: originalSale.getItems().stream()
.map(i -> i.getUnitPrice().multiply(BigDecimal.valueOf(Math.abs(i.getQuantity()))))
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal refundRatio = originalSubtotal.compareTo(BigDecimal.ZERO) != 0
? subtotalAmount.divide(originalSubtotal, 10, RoundingMode.HALF_UP)
: BigDecimal.ONE;
BigDecimal refundTotal = originalSale.getTotalAmount().abs()
.multiply(refundRatio).setScale(2, RoundingMode.HALF_UP);
sale.setSubtotalAmount(subtotalAmount.negate());
sale.setTotalAmount(refundTotal.negate());
User refundCustomer = customer != null ? customer : originalSale.getCustomer();
if (refundCustomer != null) {
int pointsToRestore = BigDecimal.valueOf(originalSale.getPointsUsed())
.multiply(refundRatio).setScale(0, RoundingMode.FLOOR).intValue();
int pointsToDeduct = BigDecimal.valueOf(originalSale.getPointsEarned())
.multiply(refundRatio).setScale(0, RoundingMode.FLOOR).intValue();
refundCustomer.setLoyaltyPoints(refundCustomer.getLoyaltyPoints() + pointsToRestore - pointsToDeduct);
userRepository.save(refundCustomer);
}
} else {
for (var itemRequest : request.getItems()) {
Product product = productRepository.findById(itemRequest.getProdId())
@@ -188,10 +212,23 @@ public class SaleService {
BigDecimal couponDiscount = calculateCouponDiscount(sale.getCoupon(), subtotalAmount);
sale.setCouponDiscountAmount(couponDiscount);
BigDecimal employeeDiscount = calculateEmployeeDiscount(customer, subtotalAmount.subtract(couponDiscount));
BigDecimal pointsDiscount = BigDecimal.ZERO;
int pointsUsed = 0;
if (customer != null && request.getPointsUsed() != null && request.getPointsUsed() > 0) {
if (customer.getLoyaltyPoints() < request.getPointsUsed()) {
throw new BusinessException("Customer does not have enough loyalty points");
}
pointsUsed = request.getPointsUsed();
pointsDiscount = calculatePointsDiscount(pointsUsed);
customer.setLoyaltyPoints(customer.getLoyaltyPoints() - pointsUsed);
}
sale.setPointsUsed(pointsUsed);
sale.setPointsDiscountAmount(pointsDiscount);
BigDecimal employeeDiscount = calculateEmployeeDiscount(customer, subtotalAmount.subtract(couponDiscount).subtract(pointsDiscount));
sale.setEmployeeDiscountAmount(employeeDiscount);
BigDecimal finalTotal = subtotalAmount.subtract(couponDiscount).subtract(employeeDiscount);
BigDecimal finalTotal = subtotalAmount.subtract(couponDiscount).subtract(pointsDiscount).subtract(employeeDiscount);
sale.setTotalAmount(finalTotal.max(BigDecimal.ZERO));
sale.setPointsEarned(sale.getTotalAmount().setScale(0, RoundingMode.FLOOR).intValue());
@@ -240,6 +277,10 @@ public class SaleService {
return discount.min(subtotal).setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal calculatePointsDiscount(int pointsUsed) {
return new BigDecimal(pointsUsed).divide(new BigDecimal("20"), 2, RoundingMode.HALF_UP);
}
private BigDecimal calculateEmployeeDiscount(User customer, BigDecimal remainingAmount) {
if (customer == null || remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
return BigDecimal.ZERO;
@@ -274,6 +315,8 @@ public class SaleService {
response.setCouponDiscountAmount(sale.getCouponDiscountAmount());
response.setEmployeeDiscountAmount(sale.getEmployeeDiscountAmount());
response.setPointsEarned(sale.getPointsEarned());
response.setPointsUsed(sale.getPointsUsed());
response.setPointsDiscountAmount(sale.getPointsDiscountAmount());
response.setChannel(sale.getChannel());
if (sale.getCoupon() != null) {
response.setCouponId(sale.getCoupon().getCouponId());

View File

@@ -0,0 +1,3 @@
ALTER TABLE sale
ADD COLUMN pointsUsed INT NOT NULL DEFAULT 0,
ADD COLUMN pointsDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00;

View File

@@ -230,6 +230,8 @@ CREATE TABLE IF NOT EXISTS sale (
couponDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
employeeDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
pointsEarned INT NOT NULL DEFAULT 0,
pointsUsed INT NOT NULL DEFAULT 0,
pointsDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_sale_employee FOREIGN KEY (employeeId) REFERENCES users(id),