added points to sale and logic backend
This commit is contained in:
@@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotEmpty;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
public class SaleRequest {
|
public class SaleRequest {
|
||||||
@NotNull(message = "Store ID is required")
|
@NotNull(message = "Store ID is required")
|
||||||
@@ -28,6 +29,10 @@ public class SaleRequest {
|
|||||||
|
|
||||||
private Long cartId;
|
private Long cartId;
|
||||||
|
|
||||||
|
private Integer pointsUsed;
|
||||||
|
|
||||||
|
private BigDecimal pointsDiscountAmount;
|
||||||
|
|
||||||
public Long getStoreId() {
|
public Long getStoreId() {
|
||||||
return storeId;
|
return storeId;
|
||||||
}
|
}
|
||||||
@@ -100,6 +105,22 @@ public class SaleRequest {
|
|||||||
this.cartId = cartId;
|
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
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ public class SaleResponse {
|
|||||||
private BigDecimal couponDiscountAmount;
|
private BigDecimal couponDiscountAmount;
|
||||||
private BigDecimal employeeDiscountAmount;
|
private BigDecimal employeeDiscountAmount;
|
||||||
private Integer pointsEarned;
|
private Integer pointsEarned;
|
||||||
|
private Integer pointsUsed;
|
||||||
|
private BigDecimal pointsDiscountAmount;
|
||||||
private String channel;
|
private String channel;
|
||||||
private Long couponId;
|
private Long couponId;
|
||||||
private Long cartId;
|
private Long cartId;
|
||||||
@@ -135,6 +137,22 @@ public class SaleResponse {
|
|||||||
this.pointsEarned = pointsEarned;
|
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() {
|
public String getChannel() {
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ public class Sale {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Integer pointsEarned = 0;
|
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)
|
@OneToMany(mappedBy = "sale", cascade = CascadeType.ALL)
|
||||||
private List<SaleItem> items = new ArrayList<>();
|
private List<SaleItem> items = new ArrayList<>();
|
||||||
|
|
||||||
@@ -211,6 +217,22 @@ public class Sale {
|
|||||||
this.pointsEarned = pointsEarned;
|
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() {
|
public List<SaleItem> getItems() {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,9 +152,33 @@ public class SaleService {
|
|||||||
saleItems.add(saleItem);
|
saleItems.add(saleItem);
|
||||||
subtotalAmount = subtotalAmount.add(itemTotal);
|
subtotalAmount = subtotalAmount.add(itemTotal);
|
||||||
}
|
}
|
||||||
subtotalAmount = subtotalAmount.negate();
|
|
||||||
sale.setSubtotalAmount(subtotalAmount);
|
Sale originalSale = sale.getOriginalSale();
|
||||||
sale.setTotalAmount(subtotalAmount);
|
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 {
|
} else {
|
||||||
for (var itemRequest : request.getItems()) {
|
for (var itemRequest : request.getItems()) {
|
||||||
Product product = productRepository.findById(itemRequest.getProdId())
|
Product product = productRepository.findById(itemRequest.getProdId())
|
||||||
@@ -188,10 +212,23 @@ public class SaleService {
|
|||||||
BigDecimal couponDiscount = calculateCouponDiscount(sale.getCoupon(), subtotalAmount);
|
BigDecimal couponDiscount = calculateCouponDiscount(sale.getCoupon(), subtotalAmount);
|
||||||
sale.setCouponDiscountAmount(couponDiscount);
|
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);
|
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.setTotalAmount(finalTotal.max(BigDecimal.ZERO));
|
||||||
|
|
||||||
sale.setPointsEarned(sale.getTotalAmount().setScale(0, RoundingMode.FLOOR).intValue());
|
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);
|
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) {
|
private BigDecimal calculateEmployeeDiscount(User customer, BigDecimal remainingAmount) {
|
||||||
if (customer == null || remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
if (customer == null || remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
@@ -274,6 +315,8 @@ public class SaleService {
|
|||||||
response.setCouponDiscountAmount(sale.getCouponDiscountAmount());
|
response.setCouponDiscountAmount(sale.getCouponDiscountAmount());
|
||||||
response.setEmployeeDiscountAmount(sale.getEmployeeDiscountAmount());
|
response.setEmployeeDiscountAmount(sale.getEmployeeDiscountAmount());
|
||||||
response.setPointsEarned(sale.getPointsEarned());
|
response.setPointsEarned(sale.getPointsEarned());
|
||||||
|
response.setPointsUsed(sale.getPointsUsed());
|
||||||
|
response.setPointsDiscountAmount(sale.getPointsDiscountAmount());
|
||||||
response.setChannel(sale.getChannel());
|
response.setChannel(sale.getChannel());
|
||||||
if (sale.getCoupon() != null) {
|
if (sale.getCoupon() != null) {
|
||||||
response.setCouponId(sale.getCoupon().getCouponId());
|
response.setCouponId(sale.getCoupon().getCouponId());
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -230,6 +230,8 @@ CREATE TABLE IF NOT EXISTS sale (
|
|||||||
couponDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
|
couponDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
|
||||||
employeeDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
|
employeeDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
|
||||||
pointsEarned INT NOT NULL DEFAULT 0,
|
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,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT fk_sale_employee FOREIGN KEY (employeeId) REFERENCES users(id),
|
CONSTRAINT fk_sale_employee FOREIGN KEY (employeeId) REFERENCES users(id),
|
||||||
|
|||||||
Reference in New Issue
Block a user