diff --git a/Makefile b/Makefile index d36ba89..d256ab1 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +.PHONY: test build run debug lint/fix + test: ./gradlew test @@ -8,4 +10,7 @@ run: ./gradlew bootRun debug: - ./gradlew bootRun --debug-jvm \ No newline at end of file + ./gradlew bootRun --debug-jvm + +lint/fix: + ./gradlew spotlessApply \ No newline at end of file diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/OrderResource.java b/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/OrderResource.java index 24f3632..9a1147b 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/OrderResource.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/OrderResource.java @@ -2,10 +2,11 @@ import br.com.fiap.grupo30.fastfood.application.dto.AddOrderProductRequest; import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; -import br.com.fiap.grupo30.fastfood.application.services.OrderService; -import br.com.fiap.grupo30.fastfood.domain.commands.AddProductToOrderCommand; -import br.com.fiap.grupo30.fastfood.domain.commands.RemoveProductFromOrderCommand; -import br.com.fiap.grupo30.fastfood.domain.commands.SubmitOrderCommand; +import br.com.fiap.grupo30.fastfood.domain.usecases.order.AddProductToOrderUseCase; +import br.com.fiap.grupo30.fastfood.domain.usecases.order.GetOrderUseCase; +import br.com.fiap.grupo30.fastfood.domain.usecases.order.RemoveProductFromOrderUseCase; +import br.com.fiap.grupo30.fastfood.domain.usecases.order.StartNewOrderUseCase; +import br.com.fiap.grupo30.fastfood.domain.usecases.order.SubmitOrderUseCase; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import java.net.URI; @@ -19,11 +20,24 @@ @Tag(name = "Orders Resource", description = "RESTful API for managing orders.") public class OrderResource { - private final OrderService orderService; + private final StartNewOrderUseCase startNewOrderUseCase; + private final AddProductToOrderUseCase addProductToOrderUseCase; + private final RemoveProductFromOrderUseCase removeProductFromOrderUseCase; + private final GetOrderUseCase getOrderUseCase; + private final SubmitOrderUseCase submitOrderUseCase; @Autowired - public OrderResource(OrderService orderService) { - this.orderService = orderService; + public OrderResource( + StartNewOrderUseCase startNewOrderUseCase, + AddProductToOrderUseCase addProductToOrderUseCase, + RemoveProductFromOrderUseCase removeProductFromOrderUseCase, + GetOrderUseCase getOrderUseCase, + SubmitOrderUseCase submitOrderUseCase) { + this.startNewOrderUseCase = startNewOrderUseCase; + this.addProductToOrderUseCase = addProductToOrderUseCase; + this.removeProductFromOrderUseCase = removeProductFromOrderUseCase; + this.getOrderUseCase = getOrderUseCase; + this.submitOrderUseCase = submitOrderUseCase; } @GetMapping(value = "/{orderId}") @@ -31,7 +45,7 @@ public OrderResource(OrderService orderService) { summary = "Get an order by ID", description = "Retrieve a specific order based on its ID") public ResponseEntity findById(@PathVariable Long orderId) { - OrderDTO order = orderService.getOrder(orderId); + OrderDTO order = this.getOrderUseCase.execute(orderId); return ResponseEntity.ok().body(order); } @@ -40,7 +54,7 @@ public ResponseEntity findById(@PathVariable Long orderId) { summary = "Create a new order", description = "Create a new order and return the new order's data") public ResponseEntity startNewOrder() { - OrderDTO order = orderService.startNewOrder(); + OrderDTO order = this.startNewOrderUseCase.execute(); URI uri = ServletUriComponentsBuilder.fromCurrentRequest() .path("/{orderId}") @@ -53,12 +67,9 @@ public ResponseEntity startNewOrder() { @Operation(summary = "Add a product to an order", description = "Adds a product to an order") public ResponseEntity addProduct( @PathVariable Long orderId, @RequestBody AddOrderProductRequest request) { - var command = new AddProductToOrderCommand(); - command.setOrderId(orderId); - command.setProductId(request.getProductId()); - command.setProductQuantity(request.getQuantity()); - - OrderDTO order = orderService.addProductToOrder(command); + OrderDTO order = + this.addProductToOrderUseCase.execute( + orderId, request.getProductId(), request.getQuantity()); return ResponseEntity.ok().body(order); } @@ -68,11 +79,7 @@ public ResponseEntity addProduct( description = "Removes a product from an order") public ResponseEntity removeProduct( @PathVariable Long orderId, @PathVariable Long productId) { - var command = new RemoveProductFromOrderCommand(); - command.setOrderId(orderId); - command.setProductId(productId); - - OrderDTO order = orderService.removeProductFromOrder(command); + OrderDTO order = this.removeProductFromOrderUseCase.execute(orderId, productId); return ResponseEntity.ok().body(order); } @@ -81,10 +88,7 @@ public ResponseEntity removeProduct( summary = "Submit an order for preparation", description = "Submits an order for preparation and return the order's data") public ResponseEntity submitOrder(@PathVariable Long orderId) { - var command = new SubmitOrderCommand(); - command.setOrderId(orderId); - - OrderDTO order = orderService.submitOrder(command); + OrderDTO order = this.submitOrderUseCase.execute(orderId); return ResponseEntity.ok().body(order); } } diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/exceptions/ResourceExceptionHandler.java b/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/exceptions/ResourceExceptionHandler.java index 2b27d98..01e834e 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/exceptions/ResourceExceptionHandler.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/adapters/in/rest/exceptions/ResourceExceptionHandler.java @@ -64,19 +64,6 @@ public ResponseEntity database(DatabaseException e, HttpServletRe return ResponseEntity.status(status).body(err); } - @ExceptionHandler(UserCantChangeOrderAfterSubmitException.class) - public ResponseEntity userCantChangeOrderAfterSubmit( - UserCantChangeOrderAfterSubmitException e, HttpServletRequest request) { - HttpStatus status = HttpStatus.BAD_REQUEST; - StandardError err = new StandardError(); - err.setTimestamp(Instant.now()); - err.setStatus(status.value()); - err.setError("UserCantChangeOrderAfterSubmit exception"); - err.setMessage(e.getMessage()); - err.setPath(request.getRequestURI()); - return ResponseEntity.status(status).body(err); - } - @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity validation( MethodArgumentNotValidException e, HttpServletRequest request) { diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/OrderService.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/OrderService.java deleted file mode 100644 index d037495..0000000 --- a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/OrderService.java +++ /dev/null @@ -1,19 +0,0 @@ -package br.com.fiap.grupo30.fastfood.application.services; - -import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; -import br.com.fiap.grupo30.fastfood.domain.commands.AddProductToOrderCommand; -import br.com.fiap.grupo30.fastfood.domain.commands.RemoveProductFromOrderCommand; -import br.com.fiap.grupo30.fastfood.domain.commands.SubmitOrderCommand; - -public interface OrderService { - - public OrderDTO startNewOrder(); - - public OrderDTO addProductToOrder(AddProductToOrderCommand command); - - public OrderDTO removeProductFromOrder(RemoveProductFromOrderCommand command); - - public OrderDTO submitOrder(SubmitOrderCommand command); - - public OrderDTO getOrder(Long orderId); -} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/CantChangeOrderProductsAfterSubmitException.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/CantChangeOrderProductsAfterSubmitException.java new file mode 100644 index 0000000..1d4434f --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/CantChangeOrderProductsAfterSubmitException.java @@ -0,0 +1,17 @@ +package br.com.fiap.grupo30.fastfood.application.services.exceptions; + +import java.io.Serial; + +public class CantChangeOrderProductsAfterSubmitException extends DomainValidationException { + + @Serial private static final long serialVersionUID = 1L; + private static final String MESSAGE = "Can not change products of a submitted order"; + + public CantChangeOrderProductsAfterSubmitException() { + super(MESSAGE); + } + + public CantChangeOrderProductsAfterSubmitException(Throwable exception) { + super(MESSAGE, exception); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/CompositeDomainValidationException.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/CompositeDomainValidationException.java new file mode 100644 index 0000000..163eeaf --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/CompositeDomainValidationException.java @@ -0,0 +1,18 @@ +package br.com.fiap.grupo30.fastfood.application.services.exceptions; + +import java.io.Serial; +import java.util.Collection; + +public class CompositeDomainValidationException extends ResourceBadRequestException { + + @Serial private static final long serialVersionUID = 1L; + + public CompositeDomainValidationException(Collection domainErrors) { + super(String.join(", ", domainErrors)); + } + + public CompositeDomainValidationException( + Collection domainErrors, Throwable exception) { + super(String.join(", ", domainErrors), exception); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/DomainValidationException.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/DomainValidationException.java new file mode 100644 index 0000000..81ea5ef --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/DomainValidationException.java @@ -0,0 +1,15 @@ +package br.com.fiap.grupo30.fastfood.application.services.exceptions; + +import java.io.Serial; + +public abstract class DomainValidationException extends ResourceBadRequestException { + @Serial private static final long serialVersionUID = 1L; + + public DomainValidationException(String msg) { + super(msg); + } + + public DomainValidationException(String msg, Throwable exception) { + super(msg, exception); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/InvalidCpfException.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/InvalidCpfException.java new file mode 100644 index 0000000..1c54b2b --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/InvalidCpfException.java @@ -0,0 +1,16 @@ +package br.com.fiap.grupo30.fastfood.application.services.exceptions; + +import java.io.Serial; + +public class InvalidCpfException extends DomainValidationException { + @Serial private static final long serialVersionUID = 1L; + private static final String MESSAGE_TEMPLATE = "Invalid CPF: %s"; + + public InvalidCpfException(String cpf) { + super(String.format(MESSAGE_TEMPLATE, cpf)); + } + + public InvalidCpfException(String cpf, Throwable exception) { + super(String.format(MESSAGE_TEMPLATE, cpf), exception); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/ResourceBadRequestException.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/ResourceBadRequestException.java index 780999f..1117b05 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/ResourceBadRequestException.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/ResourceBadRequestException.java @@ -2,10 +2,14 @@ import java.io.Serial; -public class ResourceBadRequestException extends RuntimeException { +public abstract class ResourceBadRequestException extends RuntimeException { @Serial private static final long serialVersionUID = 1L; public ResourceBadRequestException(String msg) { super(msg); } + + public ResourceBadRequestException(String msg, Throwable exception) { + super(msg, exception); + } } diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/UserCantChangeOrderAfterSubmitException.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/UserCantChangeOrderAfterSubmitException.java deleted file mode 100644 index 9d874f4..0000000 --- a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/exceptions/UserCantChangeOrderAfterSubmitException.java +++ /dev/null @@ -1,16 +0,0 @@ -package br.com.fiap.grupo30.fastfood.application.services.exceptions; - -import java.io.Serial; - -public class UserCantChangeOrderAfterSubmitException extends RuntimeException { - - @Serial private static final long serialVersionUID = 1L; - - public UserCantChangeOrderAfterSubmitException(String msg) { - super(msg); - } - - public UserCantChangeOrderAfterSubmitException(String msg, Throwable exception) { - super(msg, exception); - } -} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/impl/CustomerServiceImpl.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/impl/CustomerServiceImpl.java index 3f7035a..fe37d8f 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/impl/CustomerServiceImpl.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/impl/CustomerServiceImpl.java @@ -4,7 +4,7 @@ import br.com.fiap.grupo30.fastfood.application.mapper.impl.CustomerDTOMapper; import br.com.fiap.grupo30.fastfood.application.mapper.impl.CustomerMapper; import br.com.fiap.grupo30.fastfood.application.services.CustomerService; -import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceBadRequestException; +import br.com.fiap.grupo30.fastfood.application.services.exceptions.InvalidCpfException; import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceConflictException; import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceNotFoundException; import br.com.fiap.grupo30.fastfood.domain.vo.CPF; @@ -45,7 +45,7 @@ public CustomerDTO findCustomerByCpf(String cpf) { public CustomerDTO insert(CustomerDTO dto) { String cpfDTO = dto.getCpf(); if (!CPF.isValid(cpfDTO)) { - throw new ResourceBadRequestException("Invalid CPF: " + cpfDTO); + throw new InvalidCpfException(cpfDTO); } try { dto.setCpf(CPF.removeNonDigits(cpfDTO)); diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/impl/OrderServiceImpl.java b/src/main/java/br/com/fiap/grupo30/fastfood/application/services/impl/OrderServiceImpl.java deleted file mode 100644 index ced289b..0000000 --- a/src/main/java/br/com/fiap/grupo30/fastfood/application/services/impl/OrderServiceImpl.java +++ /dev/null @@ -1,106 +0,0 @@ -package br.com.fiap.grupo30.fastfood.application.services.impl; - -import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; -import br.com.fiap.grupo30.fastfood.application.services.OrderService; -import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceNotFoundException; -import br.com.fiap.grupo30.fastfood.application.services.exceptions.UserCantChangeOrderAfterSubmitException; -import br.com.fiap.grupo30.fastfood.domain.commands.AddProductToOrderCommand; -import br.com.fiap.grupo30.fastfood.domain.commands.RemoveProductFromOrderCommand; -import br.com.fiap.grupo30.fastfood.domain.commands.SubmitOrderCommand; -import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderEntity; -import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderStatus; -import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.ProductEntity; -import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.OrderRepository; -import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.ProductRepository; -import jakarta.transaction.Transactional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class OrderServiceImpl implements OrderService { - - private final String ORDER_NOT_FOUND_MESSAGE = "Order not found"; - - private final OrderRepository orderRepository; - private final ProductRepository productRepository; - - @Autowired - public OrderServiceImpl(OrderRepository orderRepository, ProductRepository productRepository) { - this.orderRepository = orderRepository; - this.productRepository = productRepository; - } - - @Override - @Transactional - public OrderDTO startNewOrder() { - OrderEntity newOrder = OrderEntity.create(); - newOrder = this.orderRepository.save(newOrder); - return newOrder.toDTO(); - } - - @Override - @Transactional - public OrderDTO addProductToOrder(AddProductToOrderCommand command) { - OrderEntity order = - this.orderRepository - .findById(command.getOrderId()) - .orElseThrow(() -> new ResourceNotFoundException(ORDER_NOT_FOUND_MESSAGE)); - - if (!order.isDraft()) { - throw new UserCantChangeOrderAfterSubmitException( - "Can only add products to an order in draft"); - } - - ProductEntity product = - this.productRepository - .findById(command.getProductId()) - .orElseThrow(() -> new ResourceNotFoundException("Product not found")); - - order.addProduct(product, command.getProductQuantity()); - return this.orderRepository.save(order).toDTO(); - } - - @Override - @Transactional - public OrderDTO removeProductFromOrder(RemoveProductFromOrderCommand command) { - OrderEntity order = - this.orderRepository - .findById(command.getOrderId()) - .orElseThrow(() -> new ResourceNotFoundException(ORDER_NOT_FOUND_MESSAGE)); - - if (!order.isDraft()) { - throw new UserCantChangeOrderAfterSubmitException( - "Can only remove products from an order in draft"); - } - - ProductEntity product = - this.productRepository - .findById(command.getProductId()) - .orElseThrow(() -> new ResourceNotFoundException("Product not found")); - - order.removeProduct(product); - return this.orderRepository.save(order).toDTO(); - } - - @Override - @Transactional - public OrderDTO submitOrder(SubmitOrderCommand command) { - OrderEntity order = - this.orderRepository - .findById(command.getOrderId()) - .orElseThrow(() -> new ResourceNotFoundException(ORDER_NOT_FOUND_MESSAGE)); - - order.setStatus(OrderStatus.SUBMITTED); - return this.orderRepository.save(order).toDTO(); - } - - @Override - public OrderDTO getOrder(Long orderId) { - OrderEntity order = - this.orderRepository - .findById(orderId) - .orElseThrow(() -> new ResourceNotFoundException(ORDER_NOT_FOUND_MESSAGE)); - - return order.toDTO(); - } -} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/AddProductToOrderCommand.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/AddProductToOrderCommand.java deleted file mode 100644 index 0288bb3..0000000 --- a/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/AddProductToOrderCommand.java +++ /dev/null @@ -1,10 +0,0 @@ -package br.com.fiap.grupo30.fastfood.domain.commands; - -import lombok.Data; - -@Data -public class AddProductToOrderCommand { - private Long orderId; - private Long productId; - private Long productQuantity; -} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/RemoveProductFromOrderCommand.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/RemoveProductFromOrderCommand.java deleted file mode 100644 index a3ff108..0000000 --- a/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/RemoveProductFromOrderCommand.java +++ /dev/null @@ -1,9 +0,0 @@ -package br.com.fiap.grupo30.fastfood.domain.commands; - -import lombok.Data; - -@Data -public class RemoveProductFromOrderCommand { - private Long orderId; - private Long productId; -} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/SubmitOrderCommand.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/SubmitOrderCommand.java deleted file mode 100644 index c1a3765..0000000 --- a/src/main/java/br/com/fiap/grupo30/fastfood/domain/commands/SubmitOrderCommand.java +++ /dev/null @@ -1,8 +0,0 @@ -package br.com.fiap.grupo30.fastfood.domain.commands; - -import lombok.Data; - -@Data -public class SubmitOrderCommand { - private Long orderId; -} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/AddProductToOrderUseCase.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/AddProductToOrderUseCase.java new file mode 100644 index 0000000..02ca32f --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/AddProductToOrderUseCase.java @@ -0,0 +1,42 @@ +package br.com.fiap.grupo30.fastfood.domain.usecases.order; + +import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; +import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceNotFoundException; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderEntity; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.ProductEntity; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.OrderRepository; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.ProductRepository; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class AddProductToOrderUseCase { + + private final OrderRepository orderRepository; + private final ProductRepository productRepository; + + @Autowired + public AddProductToOrderUseCase( + OrderRepository orderRepository, ProductRepository productRepository) { + this.orderRepository = orderRepository; + this.productRepository = productRepository; + } + + @Transactional + public OrderDTO execute(Long orderId, Long productId, Long productQuantity) { + OrderEntity order = + this.orderRepository + .findById(orderId) + .orElseThrow(() -> new ResourceNotFoundException("Order not found")); + + ProductEntity product = + this.productRepository + .findById(productId) + .orElseThrow(() -> new ResourceNotFoundException("Product not found")); + + order.addProduct(product, productQuantity); + + return this.orderRepository.save(order).toDTO(); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/GetOrderUseCase.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/GetOrderUseCase.java new file mode 100644 index 0000000..025b1ef --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/GetOrderUseCase.java @@ -0,0 +1,28 @@ +package br.com.fiap.grupo30.fastfood.domain.usecases.order; + +import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; +import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceNotFoundException; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderEntity; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.OrderRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class GetOrderUseCase { + + private final OrderRepository orderRepository; + + @Autowired + public GetOrderUseCase(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public OrderDTO execute(Long orderId) { + OrderEntity order = + this.orderRepository + .findById(orderId) + .orElseThrow(() -> new ResourceNotFoundException("Order not found")); + + return order.toDTO(); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/RemoveProductFromOrderUseCase.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/RemoveProductFromOrderUseCase.java new file mode 100644 index 0000000..6d5a618 --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/RemoveProductFromOrderUseCase.java @@ -0,0 +1,39 @@ +package br.com.fiap.grupo30.fastfood.domain.usecases.order; + +import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; +import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceNotFoundException; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderEntity; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.ProductEntity; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.OrderRepository; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.ProductRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RemoveProductFromOrderUseCase { + + private final OrderRepository orderRepository; + private final ProductRepository productRepository; + + @Autowired + public RemoveProductFromOrderUseCase( + OrderRepository orderRepository, ProductRepository productRepository) { + this.orderRepository = orderRepository; + this.productRepository = productRepository; + } + + public OrderDTO execute(Long orderId, Long productId) { + OrderEntity order = + this.orderRepository + .findById(orderId) + .orElseThrow(() -> new ResourceNotFoundException("Order not found")); + + ProductEntity product = + this.productRepository + .findById(productId) + .orElseThrow(() -> new ResourceNotFoundException("Product not found")); + + order.removeProduct(product); + return this.orderRepository.save(order).toDTO(); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/StartNewOrderUseCase.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/StartNewOrderUseCase.java new file mode 100644 index 0000000..9932874 --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/StartNewOrderUseCase.java @@ -0,0 +1,26 @@ +package br.com.fiap.grupo30.fastfood.domain.usecases.order; + +import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderEntity; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.OrderRepository; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class StartNewOrderUseCase { + + private final OrderRepository orderRepository; + + @Autowired + public StartNewOrderUseCase(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + @Transactional + public OrderDTO execute() { + OrderEntity newOrder = OrderEntity.create(); + newOrder = this.orderRepository.save(newOrder); + return newOrder.toDTO(); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/SubmitOrderUseCase.java b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/SubmitOrderUseCase.java new file mode 100644 index 0000000..fa548dd --- /dev/null +++ b/src/main/java/br/com/fiap/grupo30/fastfood/domain/usecases/order/SubmitOrderUseCase.java @@ -0,0 +1,30 @@ +package br.com.fiap.grupo30.fastfood.domain.usecases.order; + +import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; +import br.com.fiap.grupo30.fastfood.application.services.exceptions.ResourceNotFoundException; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderEntity; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.entities.OrderStatus; +import br.com.fiap.grupo30.fastfood.infrastructure.out.persistence.jpa.repositories.OrderRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SubmitOrderUseCase { + + private final OrderRepository orderRepository; + + @Autowired + public SubmitOrderUseCase(OrderRepository orderRepository) { + this.orderRepository = orderRepository; + } + + public OrderDTO execute(Long orderId) { + OrderEntity order = + this.orderRepository + .findById(orderId) + .orElseThrow(() -> new ResourceNotFoundException("Order not found")); + + order.setStatus(OrderStatus.SUBMITTED); + return this.orderRepository.save(order).toDTO(); + } +} diff --git a/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/out/persistence/jpa/entities/OrderEntity.java b/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/out/persistence/jpa/entities/OrderEntity.java index a66b4d7..8a774ff 100644 --- a/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/out/persistence/jpa/entities/OrderEntity.java +++ b/src/main/java/br/com/fiap/grupo30/fastfood/infrastructure/out/persistence/jpa/entities/OrderEntity.java @@ -2,6 +2,8 @@ import br.com.fiap.grupo30.fastfood.application.dto.OrderDTO; import br.com.fiap.grupo30.fastfood.application.dto.OrderItemDTO; +import br.com.fiap.grupo30.fastfood.application.services.exceptions.CantChangeOrderProductsAfterSubmitException; +import br.com.fiap.grupo30.fastfood.application.services.exceptions.CompositeDomainValidationException; import jakarta.persistence.*; import java.time.Instant; import java.util.Collection; @@ -42,6 +44,10 @@ public class OrderEntity { private Instant deletedAt; public void addProduct(ProductEntity product, Long quantity) { + if (!this.isDraft()) { + throw new CantChangeOrderProductsAfterSubmitException(); + } + this.items.stream() .filter(orderItem -> orderItem.getProduct().equals(product)) .findFirst() @@ -54,6 +60,10 @@ public void addProduct(ProductEntity product, Long quantity) { } public void removeProduct(ProductEntity product) { + if (!this.isDraft()) { + throw new CantChangeOrderProductsAfterSubmitException(); + } + this.items.removeIf(orderItem -> orderItem.getProduct().equals(product)); this.recalculateTotalPrice(); @@ -67,14 +77,30 @@ private void recalculateTotalPrice() { this.totalPrice = this.items.stream().mapToDouble(item -> item.getTotalPrice()).sum(); } - public Boolean isDraft() { + private Boolean isDraft() { return OrderStatus.DRAFT.equals(this.status); } + private Boolean hasProducts() { + return !this.items.isEmpty(); + } + public void setStatus(OrderStatus status) { this.status = status; } + public void validate() { + var errors = new LinkedList(); + + if (this.status == OrderStatus.SUBMITTED && !this.hasProducts()) { + errors.add("Can not submit order without products"); + } + + if (!errors.isEmpty()) { + throw new CompositeDomainValidationException(errors); + } + } + @PostLoad protected void postLoad() { recalculateTotalPrice(); @@ -83,11 +109,13 @@ protected void postLoad() { @PrePersist protected void prePersist() { createdAt = Instant.now(); + this.validate(); } @PreUpdate protected void preUpdate() { updatedAt = Instant.now(); + this.validate(); } @PreRemove