Fixed Coupon to use in sales
This commit is contained in:
@@ -201,7 +201,7 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
if (sale.getItems() != null) {
|
if (sale.getItems() != null) {
|
||||||
binding.llSaleItems.removeAllViews();
|
binding.llSaleItems.removeAllViews();
|
||||||
for (SaleDTO.SaleItemDTO item : sale.getItems()) {
|
for (SaleDTO.SaleItemDTO item : sale.getItems()) {
|
||||||
addItemRow(item.getProductName(), Math.abs(item.getQuantity()), item.getUnitPrice());
|
addItemRow(item.getProductName(), Math.abs(item.getQuantity()), item.getUnitPrice(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,15 +308,16 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addItemRow(name, item.getQuantity(), price);
|
addItemRow(name, item.getQuantity(), price, item.getProdId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addItemRow(String name, int qty, BigDecimal price) {
|
private void addItemRow(String name, int qty, BigDecimal price, Long prodId) {
|
||||||
if (getContext() == null) return;
|
if (getContext() == null) return;
|
||||||
LinearLayout row = new LinearLayout(getContext());
|
LinearLayout row = new LinearLayout(getContext());
|
||||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
row.setPadding(0, 8, 0, 8);
|
row.setPadding(0, 8, 0, 8);
|
||||||
|
row.setGravity(android.view.Gravity.CENTER_VERTICAL);
|
||||||
|
|
||||||
TextView tvName = new TextView(getContext());
|
TextView tvName = new TextView(getContext());
|
||||||
tvName.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f));
|
tvName.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 2f));
|
||||||
@@ -333,6 +334,20 @@ public class SaleDetailFragment extends Fragment {
|
|||||||
row.addView(tvName);
|
row.addView(tvName);
|
||||||
row.addView(tvQty);
|
row.addView(tvQty);
|
||||||
row.addView(tvPrice);
|
row.addView(tvPrice);
|
||||||
|
|
||||||
|
if (prodId != null) {
|
||||||
|
Button btnRemove = new Button(getContext());
|
||||||
|
btnRemove.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||||
|
btnRemove.setText("✕");
|
||||||
|
btnRemove.setTextSize(12f);
|
||||||
|
btnRemove.setBackgroundTintList(android.content.res.ColorStateList.valueOf(0xFFE53935));
|
||||||
|
btnRemove.setTextColor(0xFFFFFFFF);
|
||||||
|
btnRemove.setPadding(16, 4, 16, 4);
|
||||||
|
btnRemove.setOnClickListener(v -> viewModel.removeFromCart(prodId));
|
||||||
|
row.addView(btnRemove);
|
||||||
|
}
|
||||||
|
|
||||||
binding.llSaleItems.addView(row);
|
binding.llSaleItems.addView(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ public class SaleDetailViewModel extends ViewModel {
|
|||||||
cartItems.setValue(currentCart);
|
cartItems.setValue(currentCart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeFromCart(Long prodId) {
|
||||||
|
List<SaleDTO.SaleItemDTO> currentCart = new ArrayList<>(cartItems.getValue());
|
||||||
|
currentCart.removeIf(item -> item.getProdId().equals(prodId));
|
||||||
|
cartItems.setValue(currentCart);
|
||||||
|
}
|
||||||
|
|
||||||
public LiveData<List<SaleDTO.SaleItemDTO>> getCartItems() { return cartItems; }
|
public LiveData<List<SaleDTO.SaleItemDTO>> getCartItems() { return cartItems; }
|
||||||
|
|
||||||
public LiveData<Resource<CouponDTO>> lookupCoupon(String code) {
|
public LiveData<Resource<CouponDTO>> lookupCoupon(String code) {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ public class SaleResponse {
|
|||||||
private String employeeName;
|
private String employeeName;
|
||||||
private Long storeId;
|
private Long storeId;
|
||||||
private String storeName;
|
private String storeName;
|
||||||
|
private Long customerId;
|
||||||
|
private String customerName;
|
||||||
private BigDecimal totalAmount;
|
private BigDecimal totalAmount;
|
||||||
private BigDecimal subtotalAmount;
|
private BigDecimal subtotalAmount;
|
||||||
private BigDecimal couponDiscountAmount;
|
private BigDecimal couponDiscountAmount;
|
||||||
@@ -77,6 +79,22 @@ public class SaleResponse {
|
|||||||
this.storeName = storeName;
|
this.storeName = storeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getCustomerId() {
|
||||||
|
return customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerId(Long customerId) {
|
||||||
|
this.customerId = customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomerName() {
|
||||||
|
return customerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerName(String customerName) {
|
||||||
|
this.customerName = customerName;
|
||||||
|
}
|
||||||
|
|
||||||
public BigDecimal getTotalAmount() {
|
public BigDecimal getTotalAmount() {
|
||||||
return totalAmount;
|
return totalAmount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -20,6 +21,8 @@ import java.util.List;
|
|||||||
@Service
|
@Service
|
||||||
public class SaleService {
|
public class SaleService {
|
||||||
|
|
||||||
|
private static final BigDecimal EMPLOYEE_DISCOUNT_PERCENT = new BigDecimal("0.10");
|
||||||
|
|
||||||
private final SaleRepository saleRepository;
|
private final SaleRepository saleRepository;
|
||||||
private final ProductRepository productRepository;
|
private final ProductRepository productRepository;
|
||||||
private final StoreRepository storeRepository;
|
private final StoreRepository storeRepository;
|
||||||
@@ -76,6 +79,7 @@ public class SaleService {
|
|||||||
if (request.getCouponId() != null) {
|
if (request.getCouponId() != null) {
|
||||||
Coupon coupon = couponRepository.findById(request.getCouponId())
|
Coupon coupon = couponRepository.findById(request.getCouponId())
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Coupon not found with id: " + request.getCouponId()));
|
.orElseThrow(() -> new ResourceNotFoundException("Coupon not found with id: " + request.getCouponId()));
|
||||||
|
validateCoupon(coupon);
|
||||||
sale.setCoupon(coupon);
|
sale.setCoupon(coupon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +89,9 @@ public class SaleService {
|
|||||||
sale.setCart(cart);
|
sale.setCart(cart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
User customer = null;
|
||||||
if (request.getCustomerId() != null) {
|
if (request.getCustomerId() != null) {
|
||||||
User customer = userRepository.findById(request.getCustomerId())
|
customer = userRepository.findById(request.getCustomerId())
|
||||||
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
|
||||||
sale.setCustomer(customer);
|
sale.setCustomer(customer);
|
||||||
}
|
}
|
||||||
@@ -97,7 +102,7 @@ public class SaleService {
|
|||||||
sale.setOriginalSale(originalSale);
|
sale.setOriginalSale(originalSale);
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
BigDecimal subtotalAmount = BigDecimal.ZERO;
|
||||||
List<SaleItem> saleItems = new ArrayList<>();
|
List<SaleItem> saleItems = new ArrayList<>();
|
||||||
|
|
||||||
if (sale.getIsRefund() && sale.getOriginalSale() != null) {
|
if (sale.getIsRefund() && sale.getOriginalSale() != null) {
|
||||||
@@ -145,9 +150,11 @@ public class SaleService {
|
|||||||
saleItem.setUnitPrice(unitPrice);
|
saleItem.setUnitPrice(unitPrice);
|
||||||
|
|
||||||
saleItems.add(saleItem);
|
saleItems.add(saleItem);
|
||||||
totalAmount = totalAmount.add(itemTotal);
|
subtotalAmount = subtotalAmount.add(itemTotal);
|
||||||
}
|
}
|
||||||
totalAmount = totalAmount.negate();
|
subtotalAmount = subtotalAmount.negate();
|
||||||
|
sale.setSubtotalAmount(subtotalAmount);
|
||||||
|
sale.setTotalAmount(subtotalAmount);
|
||||||
} else {
|
} else {
|
||||||
for (var itemRequest : request.getItems()) {
|
for (var itemRequest : request.getItems()) {
|
||||||
Product product = productRepository.findById(itemRequest.getProdId())
|
Product product = productRepository.findById(itemRequest.getProdId())
|
||||||
@@ -174,17 +181,77 @@ public class SaleService {
|
|||||||
saleItem.setUnitPrice(unitPrice);
|
saleItem.setUnitPrice(unitPrice);
|
||||||
|
|
||||||
saleItems.add(saleItem);
|
saleItems.add(saleItem);
|
||||||
totalAmount = totalAmount.add(itemTotal);
|
subtotalAmount = subtotalAmount.add(itemTotal);
|
||||||
|
}
|
||||||
|
sale.setSubtotalAmount(subtotalAmount);
|
||||||
|
|
||||||
|
BigDecimal couponDiscount = calculateCouponDiscount(sale.getCoupon(), subtotalAmount);
|
||||||
|
sale.setCouponDiscountAmount(couponDiscount);
|
||||||
|
|
||||||
|
BigDecimal employeeDiscount = calculateEmployeeDiscount(customer, subtotalAmount.subtract(couponDiscount));
|
||||||
|
sale.setEmployeeDiscountAmount(employeeDiscount);
|
||||||
|
|
||||||
|
BigDecimal finalTotal = subtotalAmount.subtract(couponDiscount).subtract(employeeDiscount);
|
||||||
|
sale.setTotalAmount(finalTotal.max(BigDecimal.ZERO));
|
||||||
|
|
||||||
|
sale.setPointsEarned(sale.getTotalAmount().setScale(0, RoundingMode.FLOOR).intValue());
|
||||||
|
if (customer != null) {
|
||||||
|
customer.setLoyaltyPoints(customer.getLoyaltyPoints() + sale.getPointsEarned());
|
||||||
|
userRepository.save(customer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sale.setTotalAmount(totalAmount);
|
|
||||||
sale.setItems(saleItems);
|
sale.setItems(saleItems);
|
||||||
|
|
||||||
Sale savedSale = saleRepository.save(sale);
|
Sale savedSale = saleRepository.save(sale);
|
||||||
return mapToResponse(savedSale);
|
return mapToResponse(savedSale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateCoupon(Coupon coupon) {
|
||||||
|
if (!Boolean.TRUE.equals(coupon.getActive())) {
|
||||||
|
throw new BusinessException("Coupon is not active");
|
||||||
|
}
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
if (coupon.getStartsAt() != null && now.isBefore(coupon.getStartsAt())) {
|
||||||
|
throw new BusinessException("Coupon has not started yet");
|
||||||
|
}
|
||||||
|
if (coupon.getEndsAt() != null && now.isAfter(coupon.getEndsAt())) {
|
||||||
|
throw new BusinessException("Coupon has expired");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculateCouponDiscount(Coupon coupon, BigDecimal subtotal) {
|
||||||
|
if (coupon == null || subtotal.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coupon.getMinOrderAmount() != null && subtotal.compareTo(coupon.getMinOrderAmount()) < 0) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal discount = BigDecimal.ZERO;
|
||||||
|
String type = coupon.getDiscountType().trim().toUpperCase();
|
||||||
|
if ("PERCENTAGE".equals(type) || "PERCENT".equals(type)) {
|
||||||
|
discount = subtotal.multiply(coupon.getDiscountValue().divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP));
|
||||||
|
} else if ("FIXED".equals(type)) {
|
||||||
|
discount = coupon.getDiscountValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return discount.min(subtotal).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculateEmployeeDiscount(User customer, BigDecimal remainingAmount) {
|
||||||
|
if (customer == null || remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customer.getRole() == User.Role.STAFF || customer.getRole() == User.Role.ADMIN) {
|
||||||
|
return remainingAmount.multiply(EMPLOYEE_DISCOUNT_PERCENT).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
private SaleResponse mapToResponse(Sale sale) {
|
private SaleResponse mapToResponse(Sale sale) {
|
||||||
SaleResponse response = new SaleResponse();
|
SaleResponse response = new SaleResponse();
|
||||||
response.setSaleId(sale.getSaleId());
|
response.setSaleId(sale.getSaleId());
|
||||||
@@ -197,6 +264,11 @@ public class SaleService {
|
|||||||
response.setStoreName(sale.getStore().getStoreName());
|
response.setStoreName(sale.getStore().getStoreName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sale.getCustomer() != null) {
|
||||||
|
response.setCustomerId(sale.getCustomer().getId());
|
||||||
|
response.setCustomerName(sale.getCustomer().getFirstName() + " " + sale.getCustomer().getLastName());
|
||||||
|
}
|
||||||
|
|
||||||
response.setTotalAmount(sale.getTotalAmount());
|
response.setTotalAmount(sale.getTotalAmount());
|
||||||
response.setSubtotalAmount(sale.getSubtotalAmount());
|
response.setSubtotalAmount(sale.getSubtotalAmount());
|
||||||
response.setCouponDiscountAmount(sale.getCouponDiscountAmount());
|
response.setCouponDiscountAmount(sale.getCouponDiscountAmount());
|
||||||
|
|||||||
Reference in New Issue
Block a user