- items = new ArrayList<>();
+
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/request/SplitInvoiceRequest.java b/src/main/java/diegosneves/github/rachapedido/request/SplitInvoiceRequest.java
new file mode 100644
index 0000000..45beee7
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/request/SplitInvoiceRequest.java
@@ -0,0 +1,35 @@
+package diegosneves.github.rachapedido.request;
+
+import diegosneves.github.rachapedido.dto.PersonDTO;
+import diegosneves.github.rachapedido.enums.DiscountType;
+import lombok.*;
+
+import java.util.List;
+
+/**
+ * A classe {@link SplitInvoiceRequest} representa uma requisição para dividir um valor de fatura.
+ *
+ * Esta classe contém informações sobre:
+ *
+ * - {@link PersonDTO comprador} - O indivíduo que fez a compra.
+ * - {@link PersonDTO compradores} - Uma lista de pessoas com quem o comprador pretende dividir a fatura.
+ * - {@link DiscountType discountType;} - O tipo de desconto aplicado (se houver).
+ * - {@link Double discount;} - O valor do desconto (se houver).
+ * - {@link Double deliveryFee;} - A taxa de entrega (se aplicável).
+ *
+ * Cada instância desta classe representa uma requisição única para dividir uma fatura.
+ *
+ * @author diegosneves
+ */
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+@Builder
+public class SplitInvoiceRequest {
+ private PersonDTO buyer;
+ private List splitInvoiceWith;
+ private DiscountType discountType;
+ private Double discount;
+ private Double deliveryFee;
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/response/SplitInvoiceResponse.java b/src/main/java/diegosneves/github/rachapedido/response/SplitInvoiceResponse.java
new file mode 100644
index 0000000..17d8312
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/response/SplitInvoiceResponse.java
@@ -0,0 +1,17 @@
+package diegosneves.github.rachapedido.response;
+
+import diegosneves.github.rachapedido.model.Invoice;
+import lombok.*;
+
+import java.util.List;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+@Builder
+public class SplitInvoiceResponse {
+
+ private List invoices;
+ private Double totalPayable;
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/InvoiceService.java b/src/main/java/diegosneves/github/rachapedido/service/InvoiceService.java
new file mode 100644
index 0000000..8665b10
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/InvoiceService.java
@@ -0,0 +1,75 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.core.CashDiscountStrategy;
+import diegosneves.github.rachapedido.core.DiscountStrategy;
+import diegosneves.github.rachapedido.core.NoDiscountStrategy;
+import diegosneves.github.rachapedido.core.PercentageDiscountStrategy;
+import diegosneves.github.rachapedido.dto.InvoiceDTO;
+import diegosneves.github.rachapedido.enums.DiscountType;
+import diegosneves.github.rachapedido.exceptions.CalculateInvoiceException;
+import diegosneves.github.rachapedido.mapper.BuilderMapper;
+import diegosneves.github.rachapedido.model.BillSplit;
+import diegosneves.github.rachapedido.model.Invoice;
+import diegosneves.github.rachapedido.model.Order;
+import diegosneves.github.rachapedido.model.Person;
+import diegosneves.github.rachapedido.service.contract.InvoiceServiceContract;
+import diegosneves.github.rachapedido.service.contract.OrderServiceContract;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+import static java.util.Objects.isNull;
+
+@Service
+public class InvoiceService implements InvoiceServiceContract {
+
+ private static final String CALCULATION_ERROR_MESSAGE = "Houve um problema ao calcular o valor total do pedido.";
+ private static final String NULL_PARAMETER_ERROR_MESSAGE = "Um dos parâmetros necessários para a operação de cálculo da fatura está ausente ou nulo.";
+ private final OrderServiceContract orderService;
+
+ @Autowired
+ public InvoiceService(OrderServiceContract orderService) {
+ this.orderService = orderService;
+ }
+
+ @Override
+ public BillSplit generateInvoice(List consumers, DiscountType discountType, Double discount, Double deliveryFee) {
+ this.validateParameters(consumers, discountType, discount, deliveryFee);
+ List closeOrder = this.orderService.closeOrder(consumers);
+ List invoices = this.calculateDiscount(closeOrder, discountType, discount, deliveryFee);
+ return new BillSplit();
+ }
+
+ private void validateParameters(List consumers, DiscountType discountType, Double discount, Double deliveryFee) throws CalculateInvoiceException {
+ if (isNull(consumers) || isNull(discountType) || isNull(discount) || isNull(deliveryFee)) {
+ throw new CalculateInvoiceException(NULL_PARAMETER_ERROR_MESSAGE);
+ }
+ }
+
+ private List calculateDiscount(List closeOrder, DiscountType discountType, Double discount, Double deliveryFee) throws CalculateInvoiceException {
+ List invoices = closeOrder.stream().map(this::convertToInvoiceDTO).toList();
+ double total;
+ try {
+ total = invoices.stream().mapToDouble(InvoiceDTO::getValueConsumed).sum();
+ } catch (Exception e) {
+ throw new CalculateInvoiceException(CALCULATION_ERROR_MESSAGE, e);
+ }
+ Double finalTotal = total;
+ return invoices.stream().map(dto -> this.calcDiscountForInvoice(dto, discountType, discount, finalTotal, deliveryFee)).toList();
+ }
+
+ private Invoice calcDiscountForInvoice(InvoiceDTO dto, DiscountType discountType, Double discount, Double total, Double deliveryFee){
+ DiscountStrategy strategy = DiscountStrategy.link(
+ new CashDiscountStrategy(),
+ new PercentageDiscountStrategy(),
+ new NoDiscountStrategy());
+
+ return strategy.calculateDiscount(dto,discount, discountType, total, deliveryFee);
+ }
+
+ private InvoiceDTO convertToInvoiceDTO(Order order) {
+ return BuilderMapper.builderMapper(InvoiceDTO.class, order);
+ }
+
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/OrderService.java b/src/main/java/diegosneves/github/rachapedido/service/OrderService.java
new file mode 100644
index 0000000..774ed2b
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/OrderService.java
@@ -0,0 +1,59 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.exceptions.CloseOrderException;
+import diegosneves.github.rachapedido.model.Item;
+import diegosneves.github.rachapedido.model.Order;
+import diegosneves.github.rachapedido.model.Person;
+import diegosneves.github.rachapedido.service.contract.OrderServiceContract;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+import static java.util.Objects.isNull;
+
+/**
+ * A classe {@link OrderService} é responsável por fechar pedidos para cada consumidor fornecido.
+ * Ela implementa a interface {@link OrderServiceContract}.
+ *
+ * @author diegosneves
+ */
+@Service
+public class OrderService implements OrderServiceContract {
+
+ private static final String NULL_CONSTANT = "A lista de consumidores está nula, verifique se foram adicionados consumidores à lista.";
+ private static final String ORDER_CLOSE_FAILURE_MESSAGE = "Ao processar os cálculos do pedido, ocorreu um erro.";
+
+ @Override
+ public List closeOrder(List allConsumers) throws CloseOrderException {
+ if (isNull(allConsumers)) {
+ throw new CloseOrderException(NULL_CONSTANT);
+ }
+ return allConsumers.stream().map(this::takeOrdersPerConsumers).toList();
+ }
+
+ /**
+ * Este método recebe um objeto {@link Person} como parâmetro e calcula o valor total de itens associados a essa pessoa.
+ * Em seguida, cria um objeto {@link Order} com o nome do consumidor e o valor total consumido e retorna esse objeto Order.
+ * Caso qualquer exceção ocorra durante o cálculo, uma exceção {@link CloseOrderException} será lançada com uma mensagem de erro apropriada.
+ *
+ * @param person O objeto {@link Person} para o qual o pedido precisa ser criado.
+ * @return Um objeto {@link Order} com o nome do consumidor e o valor total consumido.
+ * @throws CloseOrderException Se ocorrer alguma exceção durante o cálculo do valor total.
+ */
+ private Order takeOrdersPerConsumers(Person person) throws CloseOrderException {
+ Double totalValue = null;
+ try {
+ totalValue = person.getItems().stream()
+ .mapToDouble(Item::getPrice)
+ .sum();
+ } catch (Exception e) {
+ throw new CloseOrderException(ORDER_CLOSE_FAILURE_MESSAGE, e);
+ }
+ return Order.builder()
+ .consumerName(person.getPersonName())
+ .valueConsumed(totalValue)
+ .build();
+ }
+
+
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/PersonService.java b/src/main/java/diegosneves/github/rachapedido/service/PersonService.java
new file mode 100644
index 0000000..7d42b70
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/PersonService.java
@@ -0,0 +1,72 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.dto.PersonDTO;
+import diegosneves.github.rachapedido.exceptions.NullBuyerException;
+import diegosneves.github.rachapedido.mapper.BuilderMapper;
+import diegosneves.github.rachapedido.mapper.BuyerPersonMapper;
+import diegosneves.github.rachapedido.model.Person;
+import diegosneves.github.rachapedido.service.contract.PersonServiceContract;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+
+/**
+ * A classe {@link PersonService} implementa a interface {@link PersonServiceContract} e fornece métodos
+ * para interagir com objetos do tipo Person.
+ *
+ * @author diegosneves
+ */
+@Service
+public class PersonService implements PersonServiceContract {
+
+ private static final String BUYER_ERROR = PersonDTO.class.getSimpleName();
+
+ @Override
+ public List getConsumers(PersonDTO buyer, List consumers) throws NullBuyerException {
+ if (isNull(buyer)) {
+ throw new NullBuyerException(BUYER_ERROR);
+ }
+ List personList = new ArrayList<>();
+ personList.add(this.convertBuyerToPerson(buyer));
+ if (nonNull(consumers)) {
+ personList.addAll(this.convertAllConsumersToPerson(consumers));
+ }
+
+ return personList;
+ }
+
+ /**
+ * Converte uma lista de objetos {@link PersonDTO} em uma lista de objetos {@link Person}.
+ *
+ * @param consumer A lista de objetos {@link PersonDTO} a serem convertidos.
+ * @return A lista de objetos {@link Person} convertidos.
+ */
+ private List convertAllConsumersToPerson(List consumer) {
+ return consumer.stream().map(this::convertToPerson).toList();
+ }
+
+ /**
+ * Converte um objeto {@link PersonDTO} em um objeto {@link Person}.
+ *
+ * @param consumer O objeto {@link PersonDTO} a ser convertido.
+ * @return O objeto {@link Person} convertido.
+ */
+ private Person convertToPerson(PersonDTO consumer) {
+ return BuilderMapper.builderMapper(Person.class, consumer);
+ }
+
+ /**
+ * Converte um objeto do tipo {@link PersonDTO} em um objeto do tipo {@link Person}.
+ *
+ * @param buyer O objeto {@link PersonDTO} que representa um consumidor.
+ * @return O objeto {@link Person} que representa um comprador, resultado da conversão.
+ */
+ private Person convertBuyerToPerson(PersonDTO buyer) {
+ BuyerPersonMapper mapper = new BuyerPersonMapper();
+ return BuilderMapper.builderMapper(Person.class, buyer, mapper);
+ }
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/SplitInvoiceService.java b/src/main/java/diegosneves/github/rachapedido/service/SplitInvoiceService.java
new file mode 100644
index 0000000..f35f46b
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/SplitInvoiceService.java
@@ -0,0 +1,35 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.mapper.BuilderMapper;
+import diegosneves.github.rachapedido.model.BillSplit;
+import diegosneves.github.rachapedido.model.Person;
+import diegosneves.github.rachapedido.request.SplitInvoiceRequest;
+import diegosneves.github.rachapedido.response.SplitInvoiceResponse;
+import diegosneves.github.rachapedido.service.contract.InvoiceServiceContract;
+import diegosneves.github.rachapedido.service.contract.PersonServiceContract;
+import diegosneves.github.rachapedido.service.contract.SplitInvoiceServiceContract;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class SplitInvoiceService implements SplitInvoiceServiceContract {
+
+ private final PersonServiceContract personService;
+ private final InvoiceServiceContract invoiceService;
+
+ @Autowired
+ public SplitInvoiceService(PersonServiceContract personService, InvoiceServiceContract invoiceService) {
+ this.personService = personService;
+ this.invoiceService = invoiceService;
+ }
+
+ @Override
+ public SplitInvoiceResponse splitInvoice(SplitInvoiceRequest request) {
+ List consumers = this.personService.getConsumers(request.getBuyer(), request.getSplitInvoiceWith());
+ BillSplit billSplit = this.invoiceService.generateInvoice(consumers, request.getDiscountType(), request.getDiscount(), request.getDeliveryFee());
+
+ return BuilderMapper.builderMapper(SplitInvoiceResponse.class, billSplit);
+ }
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/contract/InvoiceServiceContract.java b/src/main/java/diegosneves/github/rachapedido/service/contract/InvoiceServiceContract.java
new file mode 100644
index 0000000..e9931dc
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/contract/InvoiceServiceContract.java
@@ -0,0 +1,13 @@
+package diegosneves.github.rachapedido.service.contract;
+
+import diegosneves.github.rachapedido.enums.DiscountType;
+import diegosneves.github.rachapedido.model.BillSplit;
+import diegosneves.github.rachapedido.model.Person;
+
+import java.util.List;
+
+public interface InvoiceServiceContract {
+
+ BillSplit generateInvoice(List consumers, DiscountType discountType, Double discount, Double deliveryFee);
+
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/contract/OrderServiceContract.java b/src/main/java/diegosneves/github/rachapedido/service/contract/OrderServiceContract.java
new file mode 100644
index 0000000..9ea78db
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/contract/OrderServiceContract.java
@@ -0,0 +1,27 @@
+package diegosneves.github.rachapedido.service.contract;
+
+import diegosneves.github.rachapedido.exceptions.CloseOrderException;
+import diegosneves.github.rachapedido.model.Order;
+import diegosneves.github.rachapedido.model.Person;
+import diegosneves.github.rachapedido.service.OrderService;
+
+import java.util.List;
+
+/**
+ * A interface {@link OrderServiceContract} representa o contrato para a classe {@link OrderService}.
+ * Fornece um método para fechar o {@link Order pedido} para cada {@link Person consumidor} fornecido.
+ *
+ * @author diegosneves
+ */
+public interface OrderServiceContract {
+
+ /**
+ * Fecha o {@link Order pedido} para cada {@link Person consumidor} fornecido.
+ *
+ * @param allConsumers A lista de todos os {@link Person consumidores} para os quais o {@link Order pedido} precisa ser fechado.
+ * @return A lista contendo os {@link Order pedidos} fechados, com o valor total consumido por cada consumidor.
+ * @throws CloseOrderException se ocorrer algum erro durante o processo de finalização do pedido.
+ */
+ List closeOrder(List allConsumers) throws CloseOrderException;
+
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/contract/PersonServiceContract.java b/src/main/java/diegosneves/github/rachapedido/service/contract/PersonServiceContract.java
new file mode 100644
index 0000000..ea761ae
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/contract/PersonServiceContract.java
@@ -0,0 +1,27 @@
+package diegosneves.github.rachapedido.service.contract;
+
+import diegosneves.github.rachapedido.dto.PersonDTO;
+import diegosneves.github.rachapedido.exceptions.NullBuyerException;
+import diegosneves.github.rachapedido.model.Person;
+
+import java.util.List;
+
+/**
+ * A interface {@link PersonServiceContract} estabelece o contrato para a classe PersonService.
+ * Ela provê um método para recuperar uma lista de {@link Person consumidores}, baseando-se nos dados do {@link PersonDTO comprador} e nos {@link PersonDTO participantes da divisão} fornecidos.
+ *
+ * @author diegosneves
+ */
+public interface PersonServiceContract {
+
+ /**
+ * Recupera a lista de consumidores com base no {@link PersonDTO comprador} e nos {@link PersonDTO consumidores} fornecidos.
+ *
+ * @param buyer As informações do {@link PersonDTO comprador}.
+ * @param consumers A lista de {@link PersonDTO consumidores}.
+ * @return A lista com todos os {@link Person consumidores}.
+ * @throws NullBuyerException Se o comprador for nulo.
+ */
+ List getConsumers(PersonDTO buyer, List consumers) throws NullBuyerException;
+
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/service/contract/SplitInvoiceServiceContract.java b/src/main/java/diegosneves/github/rachapedido/service/contract/SplitInvoiceServiceContract.java
new file mode 100644
index 0000000..ac9377d
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/service/contract/SplitInvoiceServiceContract.java
@@ -0,0 +1,10 @@
+package diegosneves.github.rachapedido.service.contract;
+
+import diegosneves.github.rachapedido.request.SplitInvoiceRequest;
+import diegosneves.github.rachapedido.response.SplitInvoiceResponse;
+
+public interface SplitInvoiceServiceContract {
+
+ SplitInvoiceResponse splitInvoice(SplitInvoiceRequest request);
+
+}
diff --git a/src/main/java/diegosneves/github/rachapedido/utils/RoundUtil.java b/src/main/java/diegosneves/github/rachapedido/utils/RoundUtil.java
new file mode 100644
index 0000000..e0acdd9
--- /dev/null
+++ b/src/main/java/diegosneves/github/rachapedido/utils/RoundUtil.java
@@ -0,0 +1,24 @@
+package diegosneves.github.rachapedido.utils;
+
+/**
+ * Esta classe fornece um conjunto de métodos de utilidade para arredondar números decimais.
+ * Os métodos de arredondamento presente aqui realizam um arredondamento para duas casas decimais.
+ * Como é uma classe utilitária, seu construtor é privado para prevenir a criação de instâncias.
+ *
+ * @author diegosneves
+ */
+public final class RoundUtil {
+
+ private RoundUtil(){}
+
+ /**
+ * Este método realiza o arredondamento do valor decimal fornecido para duas casas decimais.
+ *
+ * @param value o valor decimal a ser arredondado.
+ * @return O valor arredondado até duas casas decimais.
+ */
+ public static Double round(Double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/diegosneves/github/rachapedido/RachaPedidoApplicationTests.java b/src/test/java/diegosneves/github/rachapedido/RachaPedidoApplicationTests.java
deleted file mode 100644
index e77a6ee..0000000
--- a/src/test/java/diegosneves/github/rachapedido/RachaPedidoApplicationTests.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package diegosneves.github.rachapedido;
-
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
-@SpringBootTest
-class RachaPedidoApplicationTests {
-
- @Test
- void contextLoads() {
- }
-
-}
diff --git a/src/test/java/diegosneves/github/rachapedido/service/InvoiceServiceTest.java b/src/test/java/diegosneves/github/rachapedido/service/InvoiceServiceTest.java
new file mode 100644
index 0000000..10d0784
--- /dev/null
+++ b/src/test/java/diegosneves/github/rachapedido/service/InvoiceServiceTest.java
@@ -0,0 +1,269 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.dto.InvoiceDTO;
+import diegosneves.github.rachapedido.enums.DiscountType;
+import diegosneves.github.rachapedido.exceptions.CalculateInvoiceException;
+import diegosneves.github.rachapedido.model.*;
+import diegosneves.github.rachapedido.service.contract.OrderServiceContract;
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(SpringExtension.class)
+class InvoiceServiceTest {
+
+ private static final String CALCULATION_ERROR_MESSAGE = "Houve um problema ao calcular o valor total do pedido.";
+ private static final String NULL_PARAMETER_ERROR_MESSAGE = "Um dos parâmetros necessários para a operação de cálculo da fatura está ausente ou nulo.";
+
+ @InjectMocks
+ private InvoiceService service;
+
+ @Mock
+ private OrderServiceContract orderService;
+
+ private Person consumerI;
+ private Person consumerII;
+
+ private Item itemI;
+ private Item itemII;
+ private Item itemIII;
+
+ private Order orderI;
+ private Order orderII;
+
+ List orders;
+ @BeforeEach
+ void setUp() {
+ this.itemI = Item.builder()
+ .name("Hamburguer")
+ .price(40.0)
+ .build();
+
+ this.itemII = Item.builder()
+ .name("Sobremesa")
+ .price(2.0)
+ .build();
+
+ this.itemIII = Item.builder()
+ .name("Sanduíche")
+ .price(8.0)
+ .build();
+
+ this.consumerI = Person.builder()
+ .isBuyer(Boolean.TRUE)
+ .personName("Fulano")
+ .email("fulano@gmail.com")
+ .items(List.of(this.itemI, this.itemII))
+ .build();
+
+ this.consumerII = Person.builder()
+ .personName("Amigo")
+ .email("amigo@gmail.com")
+ .items(List.of(this.itemIII))
+ .build();
+
+ this.orderI = Order.builder()
+ .consumerName("Fulano")
+ .valueConsumed(42.0)
+ .build();
+
+ this.orderII = Order.builder()
+ .consumerName("Amigo")
+ .valueConsumed(8.0)
+ .build();
+
+ this.orders = List.of(this.orderI, this.orderII);
+ }
+
+ @Test
+ void whenReceiveInvoiceDataThenReturnBillSplit() {
+ when(orderService.closeOrder(List.of(this.consumerI, this.consumerII))).thenReturn(orders);
+
+ BillSplit actual = this.service.generateInvoice(List.of(this.consumerI, this.consumerII), DiscountType.CASH, 20.0, 8.0);
+
+// assertNull(actual);
+ assertNotNull(actual);
+// assertEquals(2, actual.getInvoices().size());
+// assertEquals("Fulano", actual.getInvoices().get(0).getConsumerName());
+// assertEquals(42.0, actual.getInvoices().get(0).getValueConsumed());
+// assertEquals(31.92, actual.getInvoices().get(0).getTotalPayable());
+// assertEquals(84.0, actual.getInvoices().get(0).getPercentageConsumedTotalBill());
+// assertEquals("link", actual.getInvoices().get(0).getPaymentLink());
+// assertEquals("Amigo", actual.getInvoices().get(1).getConsumerName());
+// assertEquals(8.0, actual.getInvoices().get(1).getValueConsumed());
+// assertEquals(6.08, actual.getInvoices().get(1).getTotalPayable());
+// assertEquals(16.0, actual.getInvoices().get(1).getPercentageConsumedTotalBill());
+// assertEquals("link", actual.getInvoices().get(1).getPaymentLink());
+// assertEquals(38.0, actual.getTotalPayable());
+ }
+
+
+ @Test
+ @SneakyThrows
+ void whenConvertToInvoiceReceiveOrderThenReturnInvoice() {
+ Method method = this.service.getClass().getDeclaredMethod("convertToInvoiceDTO", Order.class);
+ method.setAccessible(true);
+
+ InvoiceDTO actual = (InvoiceDTO) method.invoke(this.service, this.orderI);
+
+ assertNotNull(actual);
+ assertEquals("Fulano", actual.getConsumerName());
+ assertEquals(42.0, actual.getValueConsumed());
+ assertNull(actual.getTotalPayable());
+ assertNull(actual.getPercentageConsumedTotalBill());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenCalculateDiscountReceiveInvoiceDataAndDiscountTypeCashThenApplyDiscount() {
+ List invoices = List.of(this.orderI, this.orderII);
+
+ Method method = this.service.getClass().getDeclaredMethod("calculateDiscount", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ List actual = (List) method.invoke(this.service, invoices, DiscountType.CASH, 20.0, 8.0);
+
+ assertEquals("Fulano", actual.get(0).getConsumerName());
+ assertEquals(42.0, actual.get(0).getValueConsumed());
+ assertEquals(31.92, actual.get(0).getTotalPayable());
+ assertEquals(0.84, actual.get(0).getPercentageConsumedTotalBill());
+ assertNull(actual.get(0).getPaymentLink());
+ assertEquals("Amigo", actual.get(1).getConsumerName());
+ assertEquals(8.0, actual.get(1).getValueConsumed());
+ assertEquals(6.08, actual.get(1).getTotalPayable());
+ assertEquals(0.16, actual.get(1).getPercentageConsumedTotalBill());
+ assertNull(actual.get(1).getPaymentLink());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenCalculateDiscountReceiveInvoiceDataAndDiscountTypePercentageThenApplyDiscount() {
+ List invoices = List.of(this.orderI, this.orderII);
+
+ Method method = this.service.getClass().getDeclaredMethod("calculateDiscount", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ List actual = (List) method.invoke(this.service, invoices, DiscountType.PERCENTAGE, 10.0, 8.0);
+
+ assertEquals("Fulano", actual.get(0).getConsumerName());
+ assertEquals(42.0, actual.get(0).getValueConsumed());
+ assertEquals(44.52, actual.get(0).getTotalPayable());
+ assertEquals(0.84, actual.get(0).getPercentageConsumedTotalBill());
+ assertNull(actual.get(0).getPaymentLink());
+ assertEquals("Amigo", actual.get(1).getConsumerName());
+ assertEquals(8.0, actual.get(1).getValueConsumed());
+ assertEquals(8.48, actual.get(1).getTotalPayable());
+ assertEquals(0.16, actual.get(1).getPercentageConsumedTotalBill());
+ assertNull(actual.get(1).getPaymentLink());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenCalculateDiscountReceiveInvoiceDataAndDiscountTypeNoDiscountThenNotApplyDiscount() {
+ List invoices = List.of(this.orderI, this.orderII);
+
+ Method method = this.service.getClass().getDeclaredMethod("calculateDiscount", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ List actual = (List) method.invoke(this.service, invoices, DiscountType.NO_DISCOUNT, 10.0, 8.0);
+
+ assertEquals("Fulano", actual.get(0).getConsumerName());
+ assertEquals(42.0, actual.get(0).getValueConsumed());
+ assertEquals(48.72, actual.get(0).getTotalPayable());
+ assertEquals(0.84, actual.get(0).getPercentageConsumedTotalBill());
+ assertNull(actual.get(0).getPaymentLink());
+ assertEquals("Amigo", actual.get(1).getConsumerName());
+ assertEquals(8.0, actual.get(1).getValueConsumed());
+ assertEquals(9.28, actual.get(1).getTotalPayable());
+ assertEquals(0.16, actual.get(1).getPercentageConsumedTotalBill());
+ assertNull(actual.get(1).getPaymentLink());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenCalculateDiscountReceiveInvoiceDataWithValueConsumedNullThenThrowsCalculateInvoiceException() {
+ this.orderI.setValueConsumed(null);
+ List invoices = List.of(this.orderI, this.orderII);
+
+ Method method = this.service.getClass().getDeclaredMethod("calculateDiscount", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ InvocationTargetException exception = assertThrows(InvocationTargetException.class, () -> method.invoke(this.service, invoices, DiscountType.NO_DISCOUNT, 10.0, 8.0));
+
+ assertInstanceOf(CalculateInvoiceException.class, exception.getTargetException());
+ assertEquals(CalculateInvoiceException.ERROR.errorMessage(CALCULATION_ERROR_MESSAGE), exception.getTargetException().getMessage());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenValidateParametersReceiveInvoiceDataWithConsumerListNullThenThrowsCalculateInvoiceException() {
+
+ Method method = this.service.getClass().getDeclaredMethod("validateParameters", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ InvocationTargetException exception = assertThrows(InvocationTargetException.class, () -> method.invoke(this.service, null, DiscountType.NO_DISCOUNT, 10.0, 8.0));
+
+ assertInstanceOf(CalculateInvoiceException.class, exception.getTargetException());
+ assertEquals(CalculateInvoiceException.ERROR.errorMessage(NULL_PARAMETER_ERROR_MESSAGE), exception.getTargetException().getMessage());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenValidateParametersReceiveInvoiceDataWithDiscountTypeNullThenThrowsCalculateInvoiceException() {
+
+ Method method = this.service.getClass().getDeclaredMethod("validateParameters", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ InvocationTargetException exception = assertThrows(InvocationTargetException.class, () -> method.invoke(this.service, this.orders, null, 10.0, 8.0));
+
+ assertInstanceOf(CalculateInvoiceException.class, exception.getTargetException());
+ assertEquals(CalculateInvoiceException.ERROR.errorMessage(NULL_PARAMETER_ERROR_MESSAGE), exception.getTargetException().getMessage());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenValidateParametersReceiveInvoiceDataWithDiscountNullThenThrowsCalculateInvoiceException() {
+
+ Method method = this.service.getClass().getDeclaredMethod("validateParameters", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ InvocationTargetException exception = assertThrows(InvocationTargetException.class, () -> method.invoke(this.service, this.orders, DiscountType.CASH, null, 8.0));
+
+ assertInstanceOf(CalculateInvoiceException.class, exception.getTargetException());
+ assertEquals(CalculateInvoiceException.ERROR.errorMessage(NULL_PARAMETER_ERROR_MESSAGE), exception.getTargetException().getMessage());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenValidateParametersReceiveInvoiceDataWithDeliveryFeeNullThenThrowsCalculateInvoiceException() {
+
+ Method method = this.service.getClass().getDeclaredMethod("validateParameters", List.class, DiscountType.class, Double.class, Double.class);
+ method.setAccessible(true);
+
+ InvocationTargetException exception = assertThrows(InvocationTargetException.class, () -> method.invoke(this.service, this.orders, DiscountType.CASH, 10.0, null));
+
+ assertInstanceOf(CalculateInvoiceException.class, exception.getTargetException());
+ assertEquals(CalculateInvoiceException.ERROR.errorMessage(NULL_PARAMETER_ERROR_MESSAGE), exception.getTargetException().getMessage());
+
+ }
+
+}
diff --git a/src/test/java/diegosneves/github/rachapedido/service/OrderServiceTest.java b/src/test/java/diegosneves/github/rachapedido/service/OrderServiceTest.java
new file mode 100644
index 0000000..4366b78
--- /dev/null
+++ b/src/test/java/diegosneves/github/rachapedido/service/OrderServiceTest.java
@@ -0,0 +1,116 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.exceptions.CloseOrderException;
+import diegosneves.github.rachapedido.model.Item;
+import diegosneves.github.rachapedido.model.Order;
+import diegosneves.github.rachapedido.model.Person;
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(SpringExtension.class)
+class OrderServiceTest {
+
+ private static final String NULL_CONSTANT = "A lista de consumidores está nula, verifique se foram adicionados consumidores à lista.";
+ private static final String ORDER_CLOSE_FAILURE_MESSAGE = "Ao processar os cálculos do pedido, ocorreu um erro.";
+
+ @InjectMocks
+ private OrderService service;
+
+ private Person personI;
+ private Person personII;
+ private Item itemI;
+ private Item itemII;
+ private Item itemIII;
+
+ @BeforeEach
+ void setUp() {
+ this.itemI = Item.builder()
+ .name("Item I")
+ .price(40.0)
+ .build();
+
+ this.itemII = Item.builder()
+ .name("Item II")
+ .price(2.0)
+ .build();
+
+ this.itemIII = Item.builder()
+ .name("Item III")
+ .price(8.0)
+ .build();
+
+ this.personI = Person.builder()
+ .personName("Buyer - Person I")
+ .isBuyer(Boolean.TRUE)
+ .email("buyer@teste.com")
+ .items(List.of(this.itemI, this.itemII))
+ .build();
+
+ this.personII = Person.builder()
+ .personName("Consumer - Person II")
+ .email("consumer@teste.com")
+ .items(List.of(this.itemIII))
+ .build();
+ }
+
+ @Test
+ void whenCloseOrderReceiveConsumerListThenReturnOrderListWithTotalValueConsumedPerConsumer(){
+ List orders = this.service.closeOrder(List.of(this.personI, this.personII));
+
+ assertNotNull(orders);
+ assertEquals(2, orders.size());
+ assertEquals("Buyer - Person I", orders.get(0).getConsumerName());
+ assertEquals("Consumer - Person II", orders.get(1).getConsumerName());
+ assertEquals(42.0, orders.get(0).getValueConsumed());
+ assertEquals(8.0, orders.get(1).getValueConsumed());
+ }
+
+ @Test
+ @SneakyThrows
+ void whentakeOrdersPerConsumersReceivePersonThenReturnOrderWithTotalValueConsumedAndConsumerName(){
+ Method method = this.service.getClass().getDeclaredMethod("takeOrdersPerConsumers", Person.class);
+ method.setAccessible(true);
+
+ Order order = (Order) method.invoke(this.service, this.personI);
+
+ assertNotNull(order);
+ assertEquals("Buyer - Person I", order.getConsumerName());
+ assertEquals(42.0, order.getValueConsumed());
+ }
+
+ @Test
+ @SneakyThrows
+ void whentakeOrdersPerConsumersReceivePersonWithItemPriceNullThenThrowsNullPriceException(){
+ this.itemI.setPrice(null);
+
+ Method method = this.service.getClass().getDeclaredMethod("takeOrdersPerConsumers", Person.class);
+ method.setAccessible(true);
+
+
+ InvocationTargetException exception = assertThrows(InvocationTargetException.class, () -> method.invoke(this.service, this.personI));
+ Throwable realException = exception.getTargetException();
+
+ assertInstanceOf(CloseOrderException.class, realException);
+ assertEquals(CloseOrderException.ERROR.errorMessage(ORDER_CLOSE_FAILURE_MESSAGE), realException.getMessage());
+ }
+
+ @Test
+ void whenCloseOrderReceiveConsumerListNullThenThrowsCloseOrderException(){
+
+ Exception exception = assertThrows(CloseOrderException.class, () -> this.service.closeOrder(null));
+
+ assertInstanceOf(CloseOrderException.class, exception);
+ assertEquals(CloseOrderException.ERROR.errorMessage(NULL_CONSTANT), exception.getMessage());
+ }
+
+}
diff --git a/src/test/java/diegosneves/github/rachapedido/service/PersonServiceTest.java b/src/test/java/diegosneves/github/rachapedido/service/PersonServiceTest.java
new file mode 100644
index 0000000..edad00b
--- /dev/null
+++ b/src/test/java/diegosneves/github/rachapedido/service/PersonServiceTest.java
@@ -0,0 +1,162 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.dto.PersonDTO;
+import diegosneves.github.rachapedido.exceptions.NullBuyerException;
+import diegosneves.github.rachapedido.model.Item;
+import diegosneves.github.rachapedido.model.Person;
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(SpringExtension.class)
+class PersonServiceTest {
+
+ @InjectMocks
+ private PersonService service;
+
+ private PersonDTO personI;
+ private PersonDTO personII;
+ private Item itemI;
+ private Item itemII;
+ private Item itemIII;
+
+ @BeforeEach
+ void setUp() {
+ this.itemI = Item.builder()
+ .name("Item I")
+ .price(40.0)
+ .build();
+
+ this.itemII = Item.builder()
+ .name("Item II")
+ .price(2.0)
+ .build();
+
+ this.itemIII = Item.builder()
+ .name("Item III")
+ .price(8.0)
+ .build();
+
+ this.personI = PersonDTO.builder()
+ .personName("Buyer - Person I")
+ .email("buyer@teste.com")
+ .items(List.of(this.itemI, this.itemII))
+ .build();
+
+ this.personII = PersonDTO.builder()
+ .personName("Consumer - Person II")
+ .email("consumer@teste.com")
+ .items(List.of(this.itemIII))
+ .build();
+ }
+
+ @Test
+ @SneakyThrows
+ void whenConvertBuyerToPersonReceivePersonDTOThenPersonBuyerMustBeReturn() {
+ String personNameExpect = "Buyer - Person I";
+ String emailExpect = "buyer@teste.com";
+
+
+ Method method = this.service.getClass().getDeclaredMethod("convertBuyerToPerson", PersonDTO.class);
+ method.setAccessible(true);
+
+ Person actual = (Person) method.invoke(this.service, this.personI);
+
+ assertNotNull(actual);
+ assertEquals(personNameExpect, actual.getPersonName());
+ assertEquals(emailExpect, actual.getEmail());
+ assertEquals(2, actual.getItems().size());
+ assertEquals(this.itemI, actual.getItems().get(0));
+ assertEquals(this.itemII, actual.getItems().get(1));
+ assertTrue(actual.getIsBuyer());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenConvertToPersonReceivePersonDTOThenPersonConsumerMustBeReturn() {
+ String personNameExpect = "Consumer - Person II";
+ String emailExpect = "consumer@teste.com";
+
+
+ Method method = this.service.getClass().getDeclaredMethod("convertToPerson", PersonDTO.class);
+ method.setAccessible(true);
+
+ Person actual = (Person) method.invoke(this.service, this.personII);
+
+ assertNotNull(actual);
+ assertEquals(personNameExpect, actual.getPersonName());
+ assertEquals(emailExpect, actual.getEmail());
+ assertEquals(1, actual.getItems().size());
+ assertEquals(this.itemIII, actual.getItems().get(0));
+ assertFalse(actual.getIsBuyer());
+
+ }
+
+ @Test
+ @SneakyThrows
+ void whenConvertAllToPersonReceivePersonDTOListThenPersonListMustBeReturn() {
+ List personsToBeInvoked = new ArrayList<>();
+ personsToBeInvoked.add(this.personI);
+ personsToBeInvoked.add(this.personII);
+
+ Method method = this.service.getClass().getDeclaredMethod("convertAllConsumersToPerson", List.class);
+ method.setAccessible(true);
+
+ List actual = (List) method.invoke(this.service, personsToBeInvoked);
+
+ assertNotNull(actual);
+ assertEquals(2, actual.size());
+ assertFalse(actual.get(0).getIsBuyer());
+ assertFalse(actual.get(1).getIsBuyer());
+ }
+
+ @Test
+ void whenGetConsumersReceivePersonDTOBuyerAndConsumersListThenPersonListMustBeReturn() {
+ List consumers = service.getConsumers(personI, List.of(personII));
+
+ assertEquals(2, consumers.size());
+ assertEquals("Buyer - Person I", consumers.get(0).getPersonName());
+ assertEquals("Consumer - Person II", consumers.get(1).getPersonName());
+ assertEquals("buyer@teste.com", consumers.get(0).getEmail());
+ assertEquals("consumer@teste.com", consumers.get(1).getEmail());
+ assertEquals(2, consumers.get(0).getItems().size());
+ assertEquals(1, consumers.get(1).getItems().size());
+ assertEquals(itemI, consumers.get(0).getItems().get(0));
+ assertEquals(itemII, consumers.get(0).getItems().get(1));
+ assertEquals(itemIII, consumers.get(1).getItems().get(0));
+ assertTrue(consumers.get(0).getIsBuyer());
+ assertFalse(consumers.get(1).getIsBuyer());
+ }
+
+ @Test
+ void whenGetConsumersReceivePersonDTOBuyerAndConsumersListNullThenPersonListMustBeReturnWithAConsumer() {
+ List consumers = service.getConsumers(personI, null);
+
+ assertEquals(1, consumers.size());
+ assertEquals("Buyer - Person I", consumers.get(0).getPersonName());
+ assertEquals("buyer@teste.com", consumers.get(0).getEmail());
+ assertEquals(2, consumers.get(0).getItems().size());
+ assertEquals(itemI, consumers.get(0).getItems().get(0));
+ assertEquals(itemII, consumers.get(0).getItems().get(1));
+ assertTrue(consumers.get(0).getIsBuyer());
+ }
+
+ @Test
+ void whenGetConsumersReceivePersonDTOBuyerNullThenThrowNullBuyerException() {
+ Exception exception = assertThrows(NullBuyerException.class, () -> service.getConsumers(null, List.of(personII)));
+
+ assertInstanceOf(NullBuyerException.class, exception);
+ assertEquals(NullBuyerException.ERROR.errorMessage(PersonDTO.class.getSimpleName()), exception.getMessage());
+ }
+
+}
diff --git a/src/test/java/diegosneves/github/rachapedido/service/SplitInvoiceServiceTest.java b/src/test/java/diegosneves/github/rachapedido/service/SplitInvoiceServiceTest.java
new file mode 100644
index 0000000..09ed47a
--- /dev/null
+++ b/src/test/java/diegosneves/github/rachapedido/service/SplitInvoiceServiceTest.java
@@ -0,0 +1,133 @@
+package diegosneves.github.rachapedido.service;
+
+import diegosneves.github.rachapedido.dto.PersonDTO;
+import diegosneves.github.rachapedido.enums.DiscountType;
+import diegosneves.github.rachapedido.model.*;
+import diegosneves.github.rachapedido.request.SplitInvoiceRequest;
+import diegosneves.github.rachapedido.response.SplitInvoiceResponse;
+import diegosneves.github.rachapedido.service.contract.InvoiceServiceContract;
+import diegosneves.github.rachapedido.service.contract.PersonServiceContract;
+import net.bytebuddy.implementation.bind.annotation.IgnoreForBinding;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(SpringExtension.class)
+class SplitInvoiceServiceTest {
+
+ @InjectMocks
+ private SplitInvoiceService service;
+ @Mock
+ private InvoiceServiceContract invoiceService;
+ @Mock
+ private PersonServiceContract personService;
+
+ private SplitInvoiceRequest request;
+ private Item item;
+ private PersonDTO buyer;
+ private PersonDTO friend;
+
+ private Person consumerI;
+ private Person consumerII;
+
+ private BillSplit billSplit;
+ private Invoice invoiceI;
+ private Invoice invoiceII;
+
+ @BeforeEach
+ void setUp() {
+ this.buyer = PersonDTO.builder()
+ .personName("Fulano")
+ .email("fulano@gmail.com")
+ .items(List.of(new Item("Hamburguer", 40.0), new Item("Sobremesa", 2.0)))
+ .build();
+
+ this.friend = PersonDTO.builder()
+ .personName("Amigo")
+ .email("amigo@gmail.com")
+ .items(List.of(new Item("Sanduíche", 8.0)))
+ .build();
+
+ this.request = SplitInvoiceRequest.builder()
+ .buyer(this.buyer)
+ .splitInvoiceWith(List.of(this.friend))
+ .deliveryFee(8.0)
+ .discount(20.0)
+ .discountType(DiscountType.CASH)
+ .build();
+
+ this.consumerI = Person.builder()
+ .isBuyer(Boolean.TRUE)
+ .personName("Fulano")
+ .email("fulano@gmail.com")
+ .items(List.of(new Item("Hamburguer", 40.0), new Item("Sobremesa", 2.0)))
+ .build();
+
+ this.consumerII = Person.builder()
+ .personName("Amigo")
+ .email("amigo@gmail.com")
+ .items(List.of(new Item("Sanduíche", 8.0)))
+ .build();
+
+ this.invoiceI = Invoice.builder()
+ .consumerName("Fulano")
+ .valueConsumed(42.0)
+ .totalPayable(31.92)
+ .percentageConsumedTotalBill(84.0)
+ .paymentLink("n/a")
+ .build();
+
+ this.invoiceII = Invoice.builder()
+ .consumerName("Amigo")
+ .valueConsumed(8.0)
+ .totalPayable(6.08)
+ .percentageConsumedTotalBill(16.0)
+ .paymentLink("link")
+ .build();
+
+ this.billSplit = BillSplit.builder()
+ .invoices(List.of(this.invoiceI, this.invoiceII))
+ .totalPayable(38.0)
+ .build();
+
+ }
+
+ @Test
+ void whenReceivingInvoiceThenDivisionMustBeCarriedOut() {
+ when(this.personService.getConsumers(this.buyer, List.of(this.friend))).thenReturn(List.of(this.consumerI, this.consumerII));
+ when(this.invoiceService.generateInvoice(anyList(), eq(DiscountType.CASH), eq(20.0), eq(8.0))).thenReturn(this.billSplit);
+
+ SplitInvoiceResponse response = this.service.splitInvoice(this.request);
+
+ verify(personService, times(1)).getConsumers(eq(buyer), eq(List.of(friend)));
+ verify(invoiceService, times(1)).generateInvoice(eq(List.of(consumerI, consumerII)), eq(DiscountType.CASH), eq(20.0), eq(8.0));
+
+ assertNotNull(response);
+ Invoice buyerInvoice = response.getInvoices().stream().filter(p -> p.getConsumerName().equals(this.buyer.getPersonName())).findFirst().orElse(null);
+ Invoice friendInvoice = response.getInvoices().stream().filter(p -> p.getConsumerName().equals(this.friend.getPersonName())).findFirst().orElse(null);
+ assertNotNull(buyerInvoice);
+ assertNotNull(friendInvoice);
+ assertEquals(2, response.getInvoices().size());
+ assertEquals(42.0,buyerInvoice.getValueConsumed());
+ assertEquals(31.92,buyerInvoice.getTotalPayable());
+ assertEquals(84.0, buyerInvoice.getPercentageConsumedTotalBill());
+ assertEquals("n/a", buyerInvoice.getPaymentLink());
+ assertEquals(8.0, friendInvoice.getValueConsumed());
+ assertEquals(6.08, friendInvoice.getTotalPayable());
+ assertEquals(16.0, friendInvoice.getPercentageConsumedTotalBill());
+ assertEquals("link", friendInvoice.getPaymentLink());
+ assertEquals(38.0, response.getTotalPayable());
+ }
+
+}