OrderService.java
package cz.vsb.crm.service;
import com.google.common.base.Preconditions;
import cz.vsb.crm.dto.OrderLineRequest;
import cz.vsb.crm.dto.OrderLineResponse;
import cz.vsb.crm.dto.OrderProductView;
import cz.vsb.crm.dto.OrderRequest;
import cz.vsb.crm.dto.OrderResponse;
import cz.vsb.crm.exception.BusinessRuleException;
import cz.vsb.crm.exception.ResourceNotFoundException;
import cz.vsb.crm.model.Order;
import cz.vsb.crm.model.OrderProduct;
import cz.vsb.crm.model.Product;
import cz.vsb.crm.repository.AccountRepository;
import cz.vsb.crm.repository.LeadRepository;
import cz.vsb.crm.repository.OrderProductRepository;
import cz.vsb.crm.repository.OrderRepository;
import cz.vsb.crm.repository.ProductRepository;
import cz.vsb.crm.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
@Service
@RequiredArgsConstructor
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final OrderProductRepository orderProductRepository;
private final AccountRepository accountRepository;
private final LeadRepository leadRepository;
private final UserRepository userRepository;
private final ProductRepository productRepository;
@Transactional(readOnly = true)
public List<OrderResponse> findAll() {
return orderRepository.findAll().stream().map(OrderResponse::from).toList();
}
@Transactional(readOnly = true)
public OrderResponse findById(Long id) {
return OrderResponse.from(get(id));
}
public OrderResponse create(OrderRequest request) {
return OrderResponse.from(save(new Order(), request));
}
public OrderResponse update(Long id, OrderRequest request) {
return OrderResponse.from(save(get(id), request));
}
public void delete(Long id) {
Order order = get(id);
order.getOrderProducts().forEach(line -> restock(line.getProduct(), line.getQuantity()));
orderRepository.delete(order);
}
@Transactional(readOnly = true)
public List<OrderLineResponse> getProducts(Long id) {
return get(id).getOrderProducts().stream().map(OrderLineResponse::from).toList();
}
@Transactional(readOnly = true)
public List<OrderProductView> getAllProductLines() {
return orderProductRepository.findAll().stream().map(OrderProductView::from).toList();
}
private Order get(Long id) {
return orderRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Order", id));
}
private Order save(Order order, OrderRequest request) {
order.setAccount(accountRepository.findById(request.accountId())
.orElseThrow(() -> new ResourceNotFoundException("Account", request.accountId())));
order.setLead(request.leadId() == null ? null
: leadRepository.findById(request.leadId())
.orElseThrow(() -> new ResourceNotFoundException("Lead", request.leadId())));
order.setCreatedBy(request.createdById() == null ? null
: userRepository.findById(request.createdById())
.orElseThrow(() -> new ResourceNotFoundException("User", request.createdById())));
if (request.status() != null) {
order.setStatus(request.status());
}
if (request.orderType() != null) {
order.setOrderType(request.orderType());
}
if (request.orderDate() != null) {
order.setOrderDate(request.orderDate());
}
order.getOrderProducts().forEach(line -> restock(line.getProduct(), line.getQuantity()));
order.getOrderProducts().clear();
orderRepository.saveAndFlush(order);
BigDecimal total = BigDecimal.ZERO;
if (request.products() != null) {
for (OrderLineRequest line : request.products()) {
Product product = productRepository.findById(line.productId())
.orElseThrow(() -> new ResourceNotFoundException("Product", line.productId()));
int quantity = line.quantity();
int available = product.getStock() != null ? product.getStock() : 0;
if (quantity > available) {
throw new BusinessRuleException("Insufficient stock for " + product.getName()
+ ". Available: " + available + ", Requested: " + quantity);
}
product.setStock(available - quantity);
BigDecimal unitPrice = line.unitPrice() != null ? line.unitPrice()
: (product.getPrice() != null ? product.getPrice() : BigDecimal.ZERO);
BigDecimal subtotal = unitPrice.multiply(BigDecimal.valueOf(quantity));
OrderProduct orderProduct = new OrderProduct();
orderProduct.setOrder(order);
orderProduct.setProduct(product);
orderProduct.setQuantity(quantity);
orderProduct.setUnitPrice(unitPrice);
orderProduct.setSubtotal(subtotal);
order.getOrderProducts().add(orderProduct);
total = total.add(subtotal);
}
}
order.setTotalAmount(total);
return orderRepository.save(order);
}
private void restock(Product product, Integer quantity) {
Preconditions.checkNotNull(product, "order line product must not be null");
Preconditions.checkNotNull(quantity, "order line quantity must not be null");
int current = product.getStock() != null ? product.getStock() : 0;
product.setStock(current + quantity);
}
}