Close chat #169
@@ -0,0 +1,62 @@
|
||||
package com.petshop.backend.dto.refund;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class RefundItemResponse {
|
||||
private Long id;
|
||||
private Long prodId;
|
||||
private String prodName;
|
||||
private Integer quantity;
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
public RefundItemResponse() {
|
||||
}
|
||||
|
||||
public RefundItemResponse(Long id, Long prodId, String prodName, Integer quantity, BigDecimal unitPrice) {
|
||||
this.id = id;
|
||||
this.prodId = prodId;
|
||||
this.prodName = prodName;
|
||||
this.quantity = quantity;
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public void setProdId(Long prodId) {
|
||||
this.prodId = prodId;
|
||||
}
|
||||
|
||||
public String getProdName() {
|
||||
return prodName;
|
||||
}
|
||||
|
||||
public void setProdName(String prodName) {
|
||||
this.prodName = prodName;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public BigDecimal getUnitPrice() {
|
||||
return unitPrice;
|
||||
}
|
||||
|
||||
public void setUnitPrice(BigDecimal unitPrice) {
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.petshop.backend.dto.refund;
|
||||
|
||||
import com.petshop.backend.dto.sale.SaleItemRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RefundRequest {
|
||||
@@ -11,6 +15,10 @@ public class RefundRequest {
|
||||
@NotBlank(message = "Reason is required")
|
||||
private String reason;
|
||||
|
||||
@NotEmpty(message = "At least one item is required")
|
||||
@Valid
|
||||
private List<SaleItemRequest> items;
|
||||
|
||||
public Long getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
@@ -27,18 +35,27 @@ public class RefundRequest {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public List<SaleItemRequest> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<SaleItemRequest> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RefundRequest that = (RefundRequest) o;
|
||||
return Objects.equals(saleId, that.saleId) &&
|
||||
Objects.equals(reason, that.reason);
|
||||
Objects.equals(reason, that.reason) &&
|
||||
Objects.equals(items, that.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(saleId, reason);
|
||||
return Objects.hash(saleId, reason, items);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,6 +63,7 @@ public class RefundRequest {
|
||||
return "RefundRequest{" +
|
||||
"saleId=" + saleId +
|
||||
", reason='" + reason + '\'' +
|
||||
", items=" + items +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.petshop.backend.dto.refund;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RefundResponse {
|
||||
@@ -11,22 +12,32 @@ public class RefundResponse {
|
||||
private BigDecimal amount;
|
||||
private String reason;
|
||||
private String status;
|
||||
private List<RefundItemResponse> items;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public RefundResponse() {
|
||||
}
|
||||
|
||||
public RefundResponse(Long id, Long saleId, Long customerId, BigDecimal amount, String reason, String status, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public RefundResponse(Long id, Long saleId, Long customerId, BigDecimal amount, String reason, String status, List<RefundItemResponse> items, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.saleId = saleId;
|
||||
this.customerId = customerId;
|
||||
this.amount = amount;
|
||||
this.reason = reason;
|
||||
this.status = status;
|
||||
this.items = items;
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
// ...
|
||||
public List<RefundItemResponse> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<RefundItemResponse> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
||||
@@ -6,6 +6,8 @@ import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@@ -32,9 +34,30 @@ public class Refund {
|
||||
@Column(nullable = false, length = 20, columnDefinition = "VARCHAR(20)")
|
||||
private RefundStatus status;
|
||||
|
||||
@OneToMany(mappedBy = "refund", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<RefundItem> items = new ArrayList<>();
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
// ...
|
||||
public List<RefundItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<RefundItem> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void addItem(RefundItem item) {
|
||||
items.add(item);
|
||||
item.setRefund(this);
|
||||
}
|
||||
|
||||
public void removeItem(RefundItem item) {
|
||||
items.remove(item);
|
||||
item.setRefund(null);
|
||||
}
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at")
|
||||
|
||||
112
backend/src/main/java/com/petshop/backend/entity/RefundItem.java
Normal file
112
backend/src/main/java/com/petshop/backend/entity/RefundItem.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.petshop.backend.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Table(name = "refund_item")
|
||||
public class RefundItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "refund_id", nullable = false)
|
||||
private Refund refund;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "prod_id", nullable = false)
|
||||
private Product product;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Integer quantity;
|
||||
|
||||
@Column(name = "unit_price", nullable = false, precision = 10, scale = 2)
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "created_at", updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@UpdateTimestamp
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public RefundItem() {
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Refund getRefund() {
|
||||
return refund;
|
||||
}
|
||||
|
||||
public void setRefund(Refund refund) {
|
||||
this.refund = refund;
|
||||
}
|
||||
|
||||
public Product getProduct() {
|
||||
return product;
|
||||
}
|
||||
|
||||
public void setProduct(Product product) {
|
||||
this.product = product;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public BigDecimal getUnitPrice() {
|
||||
return unitPrice;
|
||||
}
|
||||
|
||||
public void setUnitPrice(BigDecimal unitPrice) {
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RefundItem that = (RefundItem) o;
|
||||
return Objects.equals(id, that.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,21 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import com.petshop.backend.dto.refund.RefundItemResponse;
|
||||
import com.petshop.backend.dto.refund.RefundRequest;
|
||||
import com.petshop.backend.dto.refund.RefundResponse;
|
||||
import com.petshop.backend.dto.sale.SaleRequest;
|
||||
import com.petshop.backend.entity.Product;
|
||||
import com.petshop.backend.entity.Refund;
|
||||
import com.petshop.backend.entity.RefundItem;
|
||||
import com.petshop.backend.entity.Sale;
|
||||
import com.petshop.backend.entity.User;
|
||||
import com.petshop.backend.repository.ProductRepository;
|
||||
import com.petshop.backend.repository.RefundRepository;
|
||||
import com.petshop.backend.repository.SaleRepository;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -18,10 +24,17 @@ public class RefundService {
|
||||
|
||||
private final RefundRepository refundRepository;
|
||||
private final SaleRepository saleRepository;
|
||||
private final ProductRepository productRepository;
|
||||
private final SaleService saleService;
|
||||
|
||||
public RefundService(RefundRepository refundRepository, SaleRepository saleRepository) {
|
||||
public RefundService(RefundRepository refundRepository,
|
||||
SaleRepository saleRepository,
|
||||
ProductRepository productRepository,
|
||||
@Lazy SaleService saleService) {
|
||||
this.refundRepository = refundRepository;
|
||||
this.saleRepository = saleRepository;
|
||||
this.productRepository = productRepository;
|
||||
this.saleService = saleService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -40,10 +53,30 @@ public class RefundService {
|
||||
Refund refund = new Refund();
|
||||
refund.setSaleId(sale.getSaleId());
|
||||
refund.setCustomerId(sale.getCustomer().getCustomerId());
|
||||
refund.setAmount(sale.getTotalAmount());
|
||||
refund.setReason(request.getReason());
|
||||
refund.setStatus(Refund.RefundStatus.PENDING);
|
||||
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
for (var itemRequest : request.getItems()) {
|
||||
Product product = productRepository.findById(itemRequest.getProdId())
|
||||
.orElseThrow(() -> new RuntimeException("Product not found: " + itemRequest.getProdId()));
|
||||
|
||||
BigDecimal unitPrice = sale.getItems().stream()
|
||||
.filter(item -> item.getProduct().getProdId().equals(itemRequest.getProdId()))
|
||||
.findFirst()
|
||||
.map(item -> item.getUnitPrice())
|
||||
.orElseThrow(() -> new RuntimeException("Product " + itemRequest.getProdId() + " was not in original sale"));
|
||||
|
||||
RefundItem refundItem = new RefundItem();
|
||||
refundItem.setProduct(product);
|
||||
refundItem.setQuantity(itemRequest.getQuantity());
|
||||
refundItem.setUnitPrice(unitPrice);
|
||||
refund.addItem(refundItem);
|
||||
|
||||
totalAmount = totalAmount.add(unitPrice.multiply(BigDecimal.valueOf(itemRequest.getQuantity())));
|
||||
}
|
||||
|
||||
refund.setAmount(totalAmount);
|
||||
Refund savedRefund = refundRepository.save(refund);
|
||||
return toResponse(savedRefund);
|
||||
}
|
||||
@@ -78,12 +111,36 @@ public class RefundService {
|
||||
Refund refund = refundRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Refund not found"));
|
||||
|
||||
Refund.RefundStatus newStatus;
|
||||
try {
|
||||
refund.setStatus(Refund.RefundStatus.valueOf(status.toUpperCase()));
|
||||
newStatus = Refund.RefundStatus.valueOf(status.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException("Invalid status: " + status);
|
||||
}
|
||||
|
||||
if (refund.getStatus() == Refund.RefundStatus.PENDING && newStatus == Refund.RefundStatus.APPROVED) {
|
||||
Sale originalSale = saleRepository.findById(refund.getSaleId())
|
||||
.orElseThrow(() -> new RuntimeException("Original sale not found"));
|
||||
|
||||
SaleRequest saleRequest = new SaleRequest();
|
||||
saleRequest.setStoreId(originalSale.getStore().getStoreId());
|
||||
saleRequest.setCustomerId(refund.getCustomerId());
|
||||
saleRequest.setOriginalSaleId(refund.getSaleId());
|
||||
saleRequest.setIsRefund(true);
|
||||
saleRequest.setPaymentMethod("Card");
|
||||
saleRequest.setItems(refund.getItems().stream()
|
||||
.map(item -> {
|
||||
var ir = new com.petshop.backend.dto.sale.SaleItemRequest();
|
||||
ir.setProdId(item.getProduct().getProdId());
|
||||
ir.setQuantity(item.getQuantity());
|
||||
return ir;
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
saleService.createSale(saleRequest);
|
||||
}
|
||||
|
||||
refund.setStatus(newStatus);
|
||||
Refund updatedRefund = refundRepository.save(refund);
|
||||
return toResponse(updatedRefund);
|
||||
}
|
||||
@@ -97,6 +154,16 @@ public class RefundService {
|
||||
}
|
||||
|
||||
private RefundResponse toResponse(Refund refund) {
|
||||
List<RefundItemResponse> itemResponses = refund.getItems().stream()
|
||||
.map(item -> new RefundItemResponse(
|
||||
item.getId(),
|
||||
item.getProduct().getProdId(),
|
||||
item.getProduct().getProdName(),
|
||||
item.getQuantity(),
|
||||
item.getUnitPrice()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new RefundResponse(
|
||||
refund.getId(),
|
||||
refund.getSaleId(),
|
||||
@@ -104,6 +171,7 @@ public class RefundService {
|
||||
refund.getAmount(),
|
||||
refund.getReason(),
|
||||
refund.getStatus().name(),
|
||||
itemResponses,
|
||||
refund.getCreatedAt(),
|
||||
refund.getUpdatedAt()
|
||||
);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
-- Consolidated Updates: Phone Normalization and Refund Items
|
||||
|
||||
-- 1. Create refund_item table
|
||||
CREATE TABLE IF NOT EXISTS refund_item (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
refund_id BIGINT NOT NULL,
|
||||
prod_id BIGINT NOT NULL,
|
||||
quantity INT NOT NULL,
|
||||
unit_price DECIMAL(10, 2) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (refund_id) REFERENCES refund(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (prod_id) REFERENCES product(prodId)
|
||||
);
|
||||
|
||||
-- 2. Normalize existing phone numbers (MySQL Set-based)
|
||||
UPDATE users
|
||||
SET phone = CONCAT('(', SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -10, 3), ') ',
|
||||
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -7, 3), '-',
|
||||
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -4))
|
||||
WHERE phone REGEXP '[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9]';
|
||||
|
||||
UPDATE supplier
|
||||
SET supPhone = CONCAT('(', SUBSTRING(REGEXP_REPLACE(supPhone, '[^0-9]', ''), -10, 3), ') ',
|
||||
SUBSTRING(REGEXP_REPLACE(supPhone, '[^0-9]', ''), -7, 3), '-',
|
||||
SUBSTRING(REGEXP_REPLACE(supPhone, '[^0-9]', ''), -4))
|
||||
WHERE supPhone REGEXP '[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9]';
|
||||
|
||||
UPDATE storeLocation
|
||||
SET phone = CONCAT('(', SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -10, 3), ') ',
|
||||
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -7, 3), '-',
|
||||
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -4))
|
||||
WHERE phone REGEXP '[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9]';
|
||||
4004
web/pnpm-lock.yaml
generated
Normal file
4004
web/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user