From 9736d8c84a00c8fa95a50de9112c0aa18ecd9447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CClems=E2=80=9D?= Date: Tue, 12 Nov 2024 09:51:30 +0100 Subject: [PATCH] task#60 core improvement to improve testability --- application/pom.xml | 6 +- .../bank/account/BankAccountEntity.java | 60 +-- .../bank/account/BankAccountServiceImpl.java | 44 +- .../bank/customer/CustomerEntity.java | 135 +---- .../bank/transaction/TransactionEntity.java | 112 +--- .../bank/transaction/TransactionMapper.java | 25 +- .../TransactionProcessorTemplate.java | 57 ++ .../transaction/TransactionRepository.java | 4 +- .../transaction/TransactionServiceImpl.java | 49 +- .../bank/transaction/TransactionUtils.java | 44 +- .../category/cash/CashAmountService.java | 57 ++ .../type/deposit/DepositProcessorFactory.java | 34 ++ .../type/deposit/DepositProcessorImpl.java | 31 ++ .../withdraw/WithdrawProcessorFactory.java | 34 ++ .../type/withdraw/WithdrawProcessorImpl.java | 31 ++ .../digital/DigitalTransactionProcessor.java | 59 ++ .../type/credit/CreditProcessorFactory.java | 33 ++ .../type/credit/CreditProcessorImpl.java | 31 ++ .../type/debit/DebitProcessorFactory.java | 32 ++ .../type/debit/DebitProcessorImpl.java | 31 ++ .../status/TransactionStatusServiceImpl.java | 15 +- .../transaction/type/CreditProcessorImpl.java | 13 - .../transaction/type/DebitProcessorImpl.java | 13 - .../type/DepositProcessorImpl.java | 13 - .../type/DigitalTransactionProcessor.java | 11 - .../PhysicalCashTransactionProcessor.java | 11 - .../type/TransactionProcessorServiceImpl.java | 192 ------- .../bank/account/BankAccountMapperTest.java | 153 +++--- .../account/BankAccountRepositoryTest.java | 23 +- .../bank/account/BankAccountServiceTest.java | 344 +++--------- .../bank/customer/CustomerMapperTest.java | 72 +-- .../transaction/TransactionMapperTest.java | 32 +- .../TransactionRepositoryTest.java | 39 +- .../transaction/TransactionServiceTest.java | 157 +++--- .../TransactionStatusServiceTest.java | 101 ++-- .../transaction/TransactionUtilsTest.java | 95 ++-- .../type/CreditProcessorServiceImplTest.java | 86 +++ .../type/DebitProcessorServiceImplTest.java | 31 ++ .../type/DepositProcessorServiceImplTest.java | 28 + .../TransactionProcessorServiceImplTest.java | 502 ------------------ .../WithdrawProcessorServiceImplTest.java | 28 + .../application/scheduler/SchedulerTest.java | 113 ++-- .../bank/transaction/TransactionResource.java | 2 + .../bank/account/BankAccountResourceTest.java | 2 +- .../transaction/TransactionResourceTest.java | 4 +- domain/pom.xml | 15 +- .../bas/domain/bank/account/BankAccount.java | 79 ++- .../bank/account/BankAccountServicePort.java | 13 - .../account/checking/CheckingBankAccount.java | 19 +- .../bank/account/mma/MMABankAccount.java | 17 +- .../account/saving/SavingBankAccount.java | 17 +- .../bas/domain/bank/customer/Customer.java | 119 ++--- .../domain/bank/transaction/Transaction.java | 135 ++++- .../TransactionControllerPort.java | 8 +- .../transaction/TransactionServicePort.java | 15 +- .../validator => }/TransactionValidator.java | 9 +- .../{ => category}/NewCashTransaction.java | 4 +- .../{ => category}/NewDigitalTransaction.java | 4 +- .../category/cash/CashAmountServicePort.java | 5 + .../CashTransactionProcessingDetails.java | 43 ++ .../deposit/DepositAmountServiceImpl.java | 33 ++ .../withdraw/WithdrawAmountServiceImpl.java | 31 ++ .../digital/DigitalAmountServicePort.java | 5 + .../DigitalTransactionProcessingDetails.java | 53 ++ .../digital}/type/TransactionType.java | 2 +- .../digital/type}/TypeValidator.java | 3 +- .../digital/type}/ValidType.java | 3 +- .../type/credit/CreditAmountServiceImpl.java | 33 ++ .../type/debit/DebitAmountServiceImpl.java | 33 ++ .../category/group/AdvancedGroup.java | 4 + .../group/DigitalTransactionGroup.java | 2 +- .../group/ExistingTransactionGroup.java | 2 +- .../group/NewTransactionGroup.java | 2 +- .../group/PhysicalCashTransactionGroup.java | 2 +- .../validator => status}/StatusValidator.java | 3 +- .../status/TransactionStatusServicePort.java | 1 + .../validator => status}/ValidStatus.java | 4 +- .../type/TransactionProcessorServicePort.java | 34 -- .../validation/group/AdvancedGroup.java | 4 - .../currency/error/CurrencyException.java | 6 + .../currency/rate/ExchangeRateUtils.java | 6 +- .../bas/domain/exception/DomainException.java | 5 + .../bas/domain/message/CommonMessages.java | 8 +- .../bas/domain/message/MessageFormatter.java | 16 +- .../domain/metadata/MetadataFieldNames.java | 8 +- .../com/cdx/bas/domain/money/AmountUtils.java | 8 +- .../com/cdx/bas/domain/testing/Generated.java | 14 + .../bank/transaction/TransactionTest.java | 69 +++ .../transaction/TransactionValidatorTest.java | 152 +++--- .../deposit/DepositAmountServiceImplTest.java | 77 +++ .../WithdrawAmountServiceImplTest.java | 73 +++ .../credit/CreditAmountServiceImplTest.java | 87 +++ .../debit/DebitAmountServiceImplTest.java | 86 +++ .../domain/message/MessageFormatterTest.java | 1 - env/test-resources/insert-test-h2.sql | 10 +- pom.xml | 4 +- 96 files changed, 2254 insertions(+), 2121 deletions(-) create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionProcessorTemplate.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/CashAmountService.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorFactory.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorImpl.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorFactory.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorImpl.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/DigitalTransactionProcessor.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorFactory.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorImpl.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorFactory.java create mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorImpl.java delete mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorImpl.java delete mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorImpl.java delete mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorImpl.java delete mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/type/DigitalTransactionProcessor.java delete mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/type/PhysicalCashTransactionProcessor.java delete mode 100644 application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImpl.java create mode 100644 application/src/test/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorServiceImplTest.java create mode 100644 application/src/test/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorServiceImplTest.java create mode 100644 application/src/test/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorServiceImplTest.java delete mode 100644 application/src/test/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImplTest.java create mode 100644 application/src/test/java/com/cdx/bas/application/bank/transaction/type/WithdrawProcessorServiceImplTest.java rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation/validator => }/TransactionValidator.java (87%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{ => category}/NewCashTransaction.java (71%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{ => category}/NewDigitalTransaction.java (72%) create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashAmountServicePort.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashTransactionProcessingDetails.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImpl.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImpl.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalAmountServicePort.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalTransactionProcessingDetails.java rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{ => category/digital}/type/TransactionType.java (51%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation/validator => category/digital/type}/TypeValidator.java (89%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation/validator => category/digital/type}/ValidType.java (82%) create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImpl.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImpl.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/AdvancedGroup.java rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation => category}/group/DigitalTransactionGroup.java (65%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation => category}/group/ExistingTransactionGroup.java (62%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation => category}/group/NewTransactionGroup.java (59%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation => category}/group/PhysicalCashTransactionGroup.java (67%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation/validator => status}/StatusValidator.java (87%) rename domain/src/main/java/com/cdx/bas/domain/bank/transaction/{validation/validator => status}/ValidStatus.java (69%) delete mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionProcessorServicePort.java delete mode 100644 domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/AdvancedGroup.java create mode 100644 domain/src/main/java/com/cdx/bas/domain/testing/Generated.java create mode 100644 domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionTest.java create mode 100644 domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImplTest.java create mode 100644 domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImplTest.java create mode 100644 domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImplTest.java create mode 100644 domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImplTest.java diff --git a/application/pom.xml b/application/pom.xml index 77abc096..87da6218 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -120,7 +120,11 @@ 21 21 - + + org.projectlombok + lombok + ${lombok.version} + io.quarkus quarkus-panache-common diff --git a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java index 6de0acfa..4b7d2c65 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java +++ b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountEntity.java @@ -5,10 +5,19 @@ import com.cdx.bas.domain.bank.account.type.AccountType; import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import java.math.BigDecimal; -import java.util.*; +import java.util.HashSet; +import java.util.Set; +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) @Entity @Table(schema = "basapp", name = "bank_accounts", uniqueConstraints = @UniqueConstraint(columnNames = "account_id")) public class BankAccountEntity extends PanacheEntityBase { @@ -32,53 +41,4 @@ public class BankAccountEntity extends PanacheEntityBase { @OneToMany(mappedBy = "emitterBankAccountEntity", cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @OrderBy("date") private Set issuedTransactions = new HashSet<>(); - - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public AccountType getType() { - return type; - } - - public void setType(AccountType type) { - this.type = type; - } - - public BigDecimal getBalance() { - return balance; - } - - public void setBalance(BigDecimal balance) { - this.balance = balance; - } - - public Set getCustomers() { - return customers; - } - - public void setCustomers(Set customers) { - this.customers = customers; - } - - public Set getIssuedTransactions() { - return issuedTransactions; - } - - public void setIssuedTransactions(Set issuedTransactions) { - this.issuedTransactions = issuedTransactions; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BankAccountEntity that = (BankAccountEntity) o; - return Objects.equals(id, that.id) && type == that.type && Objects.equals(balance, that.balance) && Objects.equals(customers, that.customers) && Objects.equals(issuedTransactions, that.issuedTransactions); - } } diff --git a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java index 02f4616e..8dcd625a 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java +++ b/application/src/main/java/com/cdx/bas/application/bank/account/BankAccountServiceImpl.java @@ -7,23 +7,18 @@ import com.cdx.bas.domain.bank.account.BankAccountServicePort; import com.cdx.bas.domain.bank.account.validation.BankAccountValidator; import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; import com.cdx.bas.domain.bank.transaction.TransactionServicePort; -import com.cdx.bas.domain.currency.rate.ExchangeRateUtils; -import com.cdx.bas.domain.money.Money; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.math.BigDecimal; import java.util.List; import java.util.Optional; import static com.cdx.bas.domain.message.CommonMessages.*; import static com.cdx.bas.domain.message.MessageFormatter.format; -import static com.cdx.bas.domain.money.AmountUtils.isNotPositive; @ApplicationScoped public class BankAccountServiceImpl implements BankAccountServicePort { @@ -54,6 +49,11 @@ public List getAll() { @Override @Transactional public BankAccount findBankAccount(Long bankAccountId) { + if (bankAccountId == null) { + throw new BankAccountException(format(BANK_ACCOUNT_CONTEXT, SEARCHING_ACTION, FAILED_STATUS, + Optional.of(MISSING_ID_CAUSE), List.of(BANK_ACCOUNT_ID_DETAIL + "null"))); + } + return bankAccountRepository.findById(bankAccountId) .orElseThrow(() -> new BankAccountException(format(BANK_ACCOUNT_CONTEXT, SEARCHING_ACTION, FAILED_STATUS, Optional.of(NOT_FOUND_CAUSE), List.of(BANK_ACCOUNT_ID_DETAIL + bankAccountId)))); @@ -85,38 +85,4 @@ public BankAccount updateBankAccount(BankAccount bankAccount) throws BankAccount logger.debug(format(BANK_ACCOUNT_CONTEXT, UPDATE_ACTION, SUCCESS_STATUS)); return updatedBankAccount; } - - @Override - public void creditAmountToAccounts(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount) { - BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(transaction.getCurrency(), transaction.getAmount()); - if (isNotPositive(euroAmount)) { - throw new TransactionException(format(CREDIT_TRANSACTION_CONTEXT, CREDIT_ACTION, FAILED_STATUS, - Optional.of(SHOULD_HAVE_POSITIVE_VALUE_CAUSE), - List.of(TRANSACTION_ID_DETAIL + transaction.getId(), EURO_AMOUNT_DETAIL + euroAmount))); - } - emitterBankAccount.getBalance().minus(Money.of(euroAmount)); - receiverBankAccount.getBalance().plus(Money.of(euroAmount)); - } - - @Override - public void depositAmountToAccount(Transaction transaction, BankAccount emitterBankAccount) { - BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(transaction.getCurrency(), transaction.getAmount()); - if (isNotPositive(euroAmount)) { - throw new TransactionException(format(DEBIT_TRANSACTION_CONTEXT, DEBIT_ACTION, FAILED_STATUS, - Optional.of(SHOULD_HAVE_POSITIVE_VALUE_CAUSE), - List.of(TRANSACTION_ID_DETAIL + transaction.getId(), EURO_AMOUNT_DETAIL + euroAmount))); - } - emitterBankAccount.getBalance().plus(Money.of(euroAmount)); - } - - @Override - public void withdrawAmountToAccount(Transaction transaction, BankAccount emitterBankAccount) { - BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(transaction.getCurrency(), transaction.getAmount()); - if (isNotPositive(euroAmount)) { - throw new TransactionException(format(WITHDRAW_TRANSACTION_CONTEXT, WITHDRAW_ACTION, FAILED_STATUS, - Optional.of(SHOULD_HAVE_POSITIVE_VALUE_CAUSE), - List.of(TRANSACTION_ID_DETAIL + transaction.getId(), EURO_AMOUNT_DETAIL + euroAmount))); - } - emitterBankAccount.getBalance().minus(Money.of(euroAmount)); - } } \ No newline at end of file diff --git a/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerEntity.java b/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerEntity.java index c3178cc8..933629bc 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerEntity.java +++ b/application/src/main/java/com/cdx/bas/application/bank/customer/CustomerEntity.java @@ -9,55 +9,64 @@ import io.hypersistence.utils.hibernate.type.json.JsonType; import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnTransformer; import org.hibernate.annotations.Type; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) @Entity @Table(schema = "basapp", name = "customers", uniqueConstraints = @UniqueConstraint(columnNames = "customer_id")) public class CustomerEntity extends PanacheEntityBase { - + @Id @Column(name = "customer_id", nullable = false) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "customers_customer_id_seq_gen") @SequenceGenerator(name = "customers_customer_id_seq_gen", sequenceName = "customers_customer_id_seq", allocationSize = 1, initialValue = 1) private Long id; - + @Column(name = "first_name", nullable = false) private String firstName; - + @Column(name = "last_name", nullable = false) private String lastName; - + @Column(name = "gender", nullable = false) @Convert(converter = GenderConverter.class) private Gender gender; - + @Column(name = "marital_status", nullable = false) @Convert(converter = MaritalStatusConverter.class) private MaritalStatus maritalStatus; - + @Column(name = "birthday", nullable = false) private LocalDate birthdate; - + @Column(name = "country", nullable = false) private String country; - + @Column(name = "address", nullable = false) private String address; - + @Column(name = "city", nullable = false) private String city; - + @Column(name = "email", nullable = false) private String email; @Column(name = "phone_number", nullable = false) private String phoneNumber; - + @JsonIgnore @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "bank_accounts_customers", joinColumns = @JoinColumn(name = "customer_id"), inverseJoinColumns = @JoinColumn(name = "account_id")) @@ -67,108 +76,4 @@ public class CustomerEntity extends PanacheEntityBase { @Column(name = "metadata", columnDefinition = "jsonb") @ColumnTransformer(write = "?::jsonb") private String metadata; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public Gender getGender() { - return gender; - } - - public void setGender(Gender gender) { - this.gender = gender; - } - - public MaritalStatus getMaritalStatus() { - return maritalStatus; - } - - public void setMaritalStatus(MaritalStatus maritalStatus) { - this.maritalStatus = maritalStatus; - } - - public LocalDate getBirthdate() { - return birthdate; - } - - public void setBirthdate(LocalDate birthdate) { - this.birthdate = birthdate; - } - - public String getCountry() { - return country; - } - - public void setCountry(String country) { - this.country = country; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPhoneNumber() { - return phoneNumber; - } - - public void setPhoneNumber(String phoneNumber) { - this.phoneNumber = phoneNumber; - } - - public List getAccounts() { - return accounts; - } - - public void setAccounts(List accounts) { - this.accounts = accounts; - } - - public String getMetadata() { - return metadata; - } - - public void setMetadata(String metadata) { - this.metadata = metadata; - } } diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java index 3c83bd68..9252cd6a 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionEntity.java @@ -2,17 +2,24 @@ import com.cdx.bas.application.bank.account.BankAccountEntity; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; import io.hypersistence.utils.hibernate.type.json.JsonStringType; import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnTransformer; import org.hibernate.annotations.Type; import java.math.BigDecimal; import java.time.Instant; -import java.util.Objects; +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) @Entity @Table(schema = "basapp", name = "transactions", uniqueConstraints = @UniqueConstraint(columnNames = "transaction_id")) @NamedQueries(@NamedQuery( @@ -57,105 +64,4 @@ public class TransactionEntity extends PanacheEntityBase { @Column(name = "metadata", columnDefinition = "jsonb") @ColumnTransformer(write = "?::jsonb") private String metadata; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public BankAccountEntity getEmitterBankAccountEntity() { - return emitterBankAccountEntity; - } - - public void setEmitterBankAccountEntity(BankAccountEntity emitterBankAccountEntity) { - this.emitterBankAccountEntity = emitterBankAccountEntity; - } - - public BankAccountEntity getReceiverBankAccountEntity() { - return receiverBankAccountEntity; - } - - public void setReceiverBankAccountEntity(BankAccountEntity receiverBankAccountEntity) { - this.receiverBankAccountEntity = receiverBankAccountEntity; - } - - public BigDecimal getAmount() { - return amount; - } - - public void setAmount(BigDecimal amount) { - this.amount = amount; - } - - public String getCurrency() { - return currency; - } - - public void setCurrency(String currency) { - this.currency = currency; - } - - public TransactionType getType() { - return type; - } - - public void setType(TransactionType type) { - this.type = type; - } - - public TransactionStatus getStatus() { - return status; - } - - public void setStatus(TransactionStatus status) { - this.status = status; - } - - public Instant getDate() { - return date; - } - - public void setDate(Instant date) { - this.date = date; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public String getMetadata() { - return metadata; - } - - public void setMetadata(String metadata) { - this.metadata = metadata; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - TransactionEntity that = (TransactionEntity) o; - return Objects.equals(id, that.id) - && Objects.equals(emitterBankAccountEntity, that.emitterBankAccountEntity) - && Objects.equals(receiverBankAccountEntity, that.receiverBankAccountEntity) - && Objects.equals(amount, that.amount) - && Objects.equals(currency, that.currency) - & type == that.type && status == that.status - && Objects.equals(date, that.date) - && Objects.equals(label, that.label) - && Objects.equals(metadata, that.metadata); - } - - @Override - public int hashCode() { - return Objects.hash(id, emitterBankAccountEntity, receiverBankAccountEntity, amount, currency, type, status, date, label, metadata); - } } diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java index 4556c5c0..8df5bdf7 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionMapper.java @@ -20,24 +20,25 @@ @RequestScoped public class TransactionMapper implements DtoEntityMapper { - @Inject TransactionRepository transactionRepository; - - @Inject BankAccountRepository bankAccountRepository; + ObjectMapper objectMapper; @Inject - ObjectMapper objectMapper; + public TransactionMapper(TransactionRepository transactionRepository, BankAccountRepository bankAccountRepository, ObjectMapper objectMapper) { + this.transactionRepository = transactionRepository; + this.bankAccountRepository = bankAccountRepository; + this.objectMapper = objectMapper; + } public Transaction toDto(TransactionEntity entity) { - Transaction dto = Transaction.builder() - .id(entity.getId()) - .type(entity.getType()) - .currency(entity.getCurrency()) - .status(entity.getStatus()) - .date(entity.getDate()) - .label(entity.getLabel()) - .build(); + Transaction dto = new Transaction(); + dto.setId(entity.getId()); + dto.setType(entity.getType()); + dto.setCurrency(entity.getCurrency()); + dto.setStatus(entity.getStatus()); + dto.setDate(entity.getDate()); + dto.setLabel(entity.getLabel()); if (entity.getEmitterBankAccountEntity() != null) { dto.setEmitterAccountId(entity.getEmitterBankAccountEntity().getId()); diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionProcessorTemplate.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionProcessorTemplate.java new file mode 100644 index 00000000..1d0e054b --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionProcessorTemplate.java @@ -0,0 +1,57 @@ +package com.cdx.bas.application.bank.transaction; + +import com.cdx.bas.domain.bank.account.BankAccountException; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; +import com.cdx.bas.domain.message.MessageFormatter; +import jakarta.transaction.Transactional; +import lombok.NoArgsConstructor; + +import java.util.*; + +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; +import static com.cdx.bas.domain.message.CommonMessages.*; +import static com.cdx.bas.domain.metadata.MetadataFieldNames.ERROR_KEY; + +@NoArgsConstructor +public abstract class TransactionProcessorTemplate { + + protected abstract Transaction processCategory(Transaction transaction, Map metadata); + protected abstract void persist(Transaction transaction); + + @Transactional + public Transaction processTransaction(Transaction transaction) { + Map metadata = new HashMap<>(); + TransactionStatus transactionStatus = COMPLETED; + try { + return processCategory(transaction, metadata); + } catch ( + NoSuchElementException exception) { + metadata = Map.of(ERROR_KEY, exception.getMessage()); + transactionStatus = ERROR; + throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, FAILED_STATUS, + Optional.of(NOT_FOUND_CAUSE), + List.of(TRANSACTION_ID_DETAIL + transaction.getId(), ERROR_DETAIL + exception.getMessage()))); + } catch (TransactionException exception) { + metadata = Map.of(ERROR_KEY, exception.getMessage()); + transactionStatus = REFUSED; + throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, REFUSED_STATUS, + Optional.of(TRANSACTION_ERROR_CAUSE), + List.of(TRANSACTION_ID_DETAIL + transaction.getId(), + ERROR_DETAIL + exception.getMessage()))); + } catch ( + BankAccountException exception) { + metadata = Map.of(ERROR_KEY, exception.getMessage()); + transactionStatus = REFUSED; + throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, REFUSED_STATUS, + Optional.of(BANK_ACCOUNT_ERROR_CAUSE), + List.of(TRANSACTION_ID_DETAIL + transaction.getId(), + ERROR_DETAIL + exception.getMessage()))); + } finally { + transaction.setStatus(transactionStatus); + transaction.getMetadata().putAll(metadata); + persist(transaction); + } + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java index fd9d261d..5e056676 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionRepository.java @@ -83,7 +83,9 @@ public void create(Transaction transaction) { @Override public Transaction update(Transaction transaction) { - Transaction updatedTransaction = transactionMapper.toDto(entityManager.merge(transactionMapper.toEntity(transaction))); + TransactionEntity entity = transactionMapper.toEntity(transaction); + TransactionEntity merge = entityManager.merge(entity); + Transaction updatedTransaction = transactionMapper.toDto(merge); logger.debug(MessageFormatter.format(TRANSACTION_CONTEXT, UPDATE_ACTION, SUCCESS_STATUS, List.of(TRANSACTION_ID_DETAIL + transaction.getId()))); return updatedTransaction; } diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java index 6e638116..55a13782 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionServiceImpl.java @@ -1,37 +1,49 @@ package com.cdx.bas.application.bank.transaction; +import com.cdx.bas.application.bank.transaction.category.digital.type.credit.CreditProcessorImpl; +import com.cdx.bas.application.bank.transaction.category.digital.type.debit.DebitProcessorImpl; +import com.cdx.bas.application.bank.transaction.category.cash.type.deposit.DepositProcessorImpl; +import com.cdx.bas.application.bank.transaction.category.cash.type.withdraw.WithdrawProcessorImpl; import com.cdx.bas.domain.bank.transaction.*; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.type.TransactionProcessorServicePort; -import com.cdx.bas.domain.bank.transaction.validation.validator.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.NewDigitalTransaction; +import com.cdx.bas.domain.bank.transaction.TransactionValidator; import com.cdx.bas.domain.message.CommonMessages; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import org.apache.commons.lang3.StringUtils; -import java.util.Map; +import java.util.Optional; import java.util.Set; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.*; import static com.cdx.bas.domain.message.CommonMessages.DEPOSIT_DETAIL; @ApplicationScoped public class TransactionServiceImpl implements TransactionServicePort { private final TransactionPersistencePort transactionRepository; - private final TransactionValidator transactionValidator; - - private final TransactionProcessorServicePort transactionTypeProcessingService; + private final CreditProcessorImpl creditProcessorService; + private final DebitProcessorImpl debitProcessorService; + private final DepositProcessorImpl depositProcessorService; + private final WithdrawProcessorImpl withdrawProcessorService; @Inject public TransactionServiceImpl(TransactionPersistencePort transactionRepository, TransactionValidator transactionValidator, - TransactionProcessorServicePort transactionTypeProcessingService) { + CreditProcessorImpl creditProcessorService, + DebitProcessorImpl debitProcessorService, + DepositProcessorImpl depositProcessorService, + WithdrawProcessorImpl withdrawProcessorService) { this.transactionRepository = transactionRepository; this.transactionValidator = transactionValidator; - this.transactionTypeProcessingService = transactionTypeProcessingService; + this.creditProcessorService = creditProcessorService; + this.debitProcessorService = debitProcessorService; + this.depositProcessorService = depositProcessorService; + this.withdrawProcessorService = withdrawProcessorService; } @Override @@ -42,15 +54,13 @@ public Set getAll() { @Override @Transactional(value = Transactional.TxType.REQUIRES_NEW) - public void create(Transaction transaction, Map metadata) { - transaction.setMetadata(metadata); + public void create(Transaction transaction) { transactionRepository.create(transaction); } @Override @Transactional(value = Transactional.TxType.REQUIRES_NEW) - public void update(Transaction transaction, Map metadata) { - transaction.getMetadata().putAll(metadata); + public void update(Transaction transaction) { transactionRepository.update(transaction); } @@ -71,18 +81,17 @@ public void createDigitalTransaction(NewDigitalTransaction newDigitalTransaction @Override @Transactional - public Transaction findTransaction(Long transactionId) { - Transaction transaction = transactionRepository.findById(transactionId).orElse(null); - return transaction; + public Optional findTransaction(Long transactionId) { + return transactionRepository.findById(transactionId); } @Override @Transactional public void processDigitalTransaction(Transaction digitalTransaction) { if (CREDIT.equals(digitalTransaction.getType())) { - transactionTypeProcessingService.credit(digitalTransaction); + creditProcessorService.processTransaction(digitalTransaction); } else if (DEBIT.equals(digitalTransaction.getType())) { - transactionTypeProcessingService.debit(digitalTransaction); + debitProcessorService.processTransaction(digitalTransaction); } } @@ -92,7 +101,7 @@ public void deposit(NewCashTransaction newDepositTransaction) { Transaction depositTransaction = TransactionUtils.getNewCashTransaction(newDepositTransaction); depositTransaction.setType(DEPOSIT); depositTransaction.setLabel(DEPOSIT_DETAIL + newDepositTransaction.amount() + StringUtils.SPACE + newDepositTransaction.currency()); - transactionTypeProcessingService.deposit(depositTransaction); + depositProcessorService.processTransaction(depositTransaction); } @Override @@ -101,6 +110,6 @@ public void withdraw(NewCashTransaction newWithdrawTransaction) { Transaction withdrawTransaction = TransactionUtils.getNewCashTransaction(newWithdrawTransaction); withdrawTransaction.setType(WITHDRAW); withdrawTransaction.setLabel(CommonMessages.WITHDRAW_DETAIL + newWithdrawTransaction.amount() + StringUtils.SPACE + newWithdrawTransaction.currency()); - transactionTypeProcessingService.withdraw(withdrawTransaction); + withdrawProcessorService.processTransaction(withdrawTransaction); } } diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionUtils.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionUtils.java index 0a45fd71..ad41f42f 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionUtils.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/TransactionUtils.java @@ -1,8 +1,9 @@ package com.cdx.bas.application.bank.transaction; -import com.cdx.bas.domain.bank.transaction.NewCashTransaction; -import com.cdx.bas.domain.bank.transaction.NewDigitalTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewDigitalTransaction; import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import lombok.experimental.UtilityClass; import java.time.Clock; @@ -15,28 +16,29 @@ public class TransactionUtils { private static final Clock clock = Clock.systemDefaultZone(); public static Transaction getNewDigitalTransaction(NewDigitalTransaction newDigitalTransaction) { - return Transaction.builder() - .emitterAccountId(newDigitalTransaction.emitterAccountId()) - .receiverAccountId(newDigitalTransaction.receiverAccountId()) - .amount(newDigitalTransaction.amount()) - .currency(newDigitalTransaction.currency()) - .type(newDigitalTransaction.type()) - .status(UNPROCESSED) - .date(clock.instant()) - .label(newDigitalTransaction.label()) - .metadata(newDigitalTransaction.metadata()) - .build(); + Transaction transaction = new Transaction(); + transaction.setEmitterAccountId(newDigitalTransaction.emitterAccountId()); + transaction.setReceiverAccountId(newDigitalTransaction.receiverAccountId()); + transaction.setAmount(newDigitalTransaction.amount()); + transaction.setCurrency(newDigitalTransaction.currency()); + transaction.setType(newDigitalTransaction.type()); + transaction.setStatus(TransactionStatus.UNPROCESSED); // Assuming UNPROCESSED is an enum constant + transaction.setDate(clock.instant()); + transaction.setLabel(newDigitalTransaction.label()); + transaction.setMetadata(newDigitalTransaction.metadata()); + return transaction; + } public static Transaction getNewCashTransaction(NewCashTransaction newCashTransaction) { - return Transaction.builder() - .emitterAccountId(newCashTransaction.emitterAccountId()) - .amount(newCashTransaction.amount()) - .currency(newCashTransaction.currency()) - .metadata(newCashTransaction.metadata()) - .date(clock.instant()) - .status(UNPROCESSED) - .build(); + Transaction transaction = new Transaction(); + transaction.setEmitterAccountId(newCashTransaction.emitterAccountId()); + transaction.setAmount(newCashTransaction.amount()); + transaction.setCurrency(newCashTransaction.currency()); + transaction.setMetadata(newCashTransaction.metadata()); + transaction.setDate(clock.instant()); + transaction.setStatus(TransactionStatus.UNPROCESSED); // Assuming UNPROCESSED is an enum constant + return transaction; } public static Transaction mergeTransactions(Transaction oldTransaction, Transaction newTransaction) { diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/CashAmountService.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/CashAmountService.java new file mode 100644 index 00000000..037994a9 --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/CashAmountService.java @@ -0,0 +1,57 @@ +package com.cdx.bas.application.bank.transaction.category.cash; + +import com.cdx.bas.application.bank.transaction.TransactionProcessorTemplate; +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.cash.CashTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import com.cdx.bas.domain.message.MessageFormatter; +import lombok.AllArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; + +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.COMPLETED; +import static com.cdx.bas.domain.message.CommonMessages.*; + +@AllArgsConstructor +public abstract class CashAmountService extends TransactionProcessorTemplate { + + private static final Logger logger = LoggerFactory.getLogger(CashAmountService.class); + + private final TransactionValidator transactionValidator; + private final TransactionStatusServicePort transactionStatusService; + private final TransactionServicePort transactionService; + private final BankAccountServicePort bankAccountService; + + protected abstract Transaction processType(CashTransactionProcessingDetails cashTransactionProcessingDetails); + + @Override + protected Transaction processCategory(Transaction transaction, Map metadata) { + transactionValidator.validateCashTransaction(transaction); + BankAccount emitterBankAccount = bankAccountService.findBankAccount(transaction.getEmitterAccountId()); + + CashTransactionProcessingDetails cashTransactionProcessingDetails = new CashTransactionProcessingDetails(transaction, emitterBankAccount, metadata); + processType(cashTransactionProcessingDetails); + + Transaction completedTransaction = transactionStatusService.setStatus(transaction, COMPLETED, metadata); + bankAccountService.updateBankAccount(emitterBankAccount); + + logger.debug(MessageFormatter.format(TRANSACTION_CONTEXT, WITHDRAW_ACTION, COMPLETED_STATUS, + List.of(TRANSACTION_ID_DETAIL + transaction.getId(), + CREDIT_DETAIL + transaction.getAmount(), + EMITTER_BANK_ACCOUNT_DETAIL + transaction.getEmitterAccountId(), + RECEIVER_BANK_ACCOUNT_DETAIL + transaction.getReceiverAccountId()))); + return completedTransaction; + } + + @Override + protected void persist(Transaction transaction) { + transactionService.create(transaction); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorFactory.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorFactory.java new file mode 100644 index 00000000..f12d6c2d --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorFactory.java @@ -0,0 +1,34 @@ +package com.cdx.bas.application.bank.transaction.category.cash.type.deposit; + +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.cash.type.deposit.DepositAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +@ApplicationScoped +public class DepositProcessorFactory { + private final TransactionValidator transactionValidator; + private final TransactionStatusServicePort transactionStatusService; + private final TransactionServicePort transactionService; + private final BankAccountServicePort bankAccountService; + private final DepositAmountServiceImpl depositAmountService; + + public DepositProcessorFactory(TransactionValidator transactionValidator, + TransactionStatusServicePort transactionStatusService, + TransactionServicePort transactionService, + BankAccountServicePort bankAccountService, DepositAmountServiceImpl depositAmountService) { + this.transactionValidator = transactionValidator; + this.transactionStatusService = transactionStatusService; + this.transactionService = transactionService; + this.bankAccountService = bankAccountService; + this.depositAmountService = depositAmountService; + } + + @Produces + public DepositProcessorImpl createCreditProcessorService() { + return new DepositProcessorImpl(transactionValidator, transactionStatusService, transactionService, bankAccountService, depositAmountService); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorImpl.java new file mode 100644 index 00000000..3cd15262 --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/deposit/DepositProcessorImpl.java @@ -0,0 +1,31 @@ +package com.cdx.bas.application.bank.transaction.category.cash.type.deposit; + +import com.cdx.bas.application.bank.transaction.category.cash.CashAmountService; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.cash.CashTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.cash.type.deposit.DepositAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; + +import static com.cdx.bas.domain.message.MessageFormatter.format; +import static com.cdx.bas.domain.metadata.MetadataFieldNames.EMITTER_AMOUNT_AFTER_KEY; +import static com.cdx.bas.domain.metadata.MetadataFieldNames.EMITTER_AMOUNT_BEFORE_KEY; + +public class DepositProcessorImpl extends CashAmountService { + private final DepositAmountServiceImpl depositAmountService; + + public DepositProcessorImpl(TransactionValidator transactionValidator, TransactionStatusServicePort transactionStatusService, TransactionServicePort transactionService, BankAccountServicePort bankAccountService, DepositAmountServiceImpl depositAmountService) { + super(transactionValidator, transactionStatusService, transactionService, bankAccountService); + this.depositAmountService = depositAmountService; + } + + @Override + protected Transaction processType(CashTransactionProcessingDetails cashTransactionProcessingDetails) { + cashTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_BEFORE_KEY, cashTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + depositAmountService.applyToAccount(cashTransactionProcessingDetails); + cashTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_AFTER_KEY, cashTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + return cashTransactionProcessingDetails.getTransaction(); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorFactory.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorFactory.java new file mode 100644 index 00000000..956d125f --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorFactory.java @@ -0,0 +1,34 @@ +package com.cdx.bas.application.bank.transaction.category.cash.type.withdraw; + +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.cash.type.withdraw.WithdrawAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +@ApplicationScoped +public class WithdrawProcessorFactory { + private final TransactionValidator transactionValidator; + private final TransactionStatusServicePort transactionStatusService; + private final TransactionServicePort transactionService; + private final BankAccountServicePort bankAccountService; + private final WithdrawAmountServiceImpl withdrawAmountService; + + public WithdrawProcessorFactory(TransactionValidator transactionValidator, + TransactionStatusServicePort transactionStatusService, + TransactionServicePort transactionService, + BankAccountServicePort bankAccountService, WithdrawAmountServiceImpl withdrawAmountService) { + this.transactionValidator = transactionValidator; + this.transactionStatusService = transactionStatusService; + this.transactionService = transactionService; + this.bankAccountService = bankAccountService; + this.withdrawAmountService = withdrawAmountService; + } + + @Produces + public WithdrawProcessorImpl createCreditProcessorService() { + return new WithdrawProcessorImpl(transactionValidator, transactionStatusService, transactionService, bankAccountService, withdrawAmountService); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorImpl.java new file mode 100644 index 00000000..09fc468d --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/cash/type/withdraw/WithdrawProcessorImpl.java @@ -0,0 +1,31 @@ +package com.cdx.bas.application.bank.transaction.category.cash.type.withdraw; + +import com.cdx.bas.application.bank.transaction.category.cash.CashAmountService; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.cash.CashTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.cash.type.withdraw.WithdrawAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; + +import static com.cdx.bas.domain.metadata.MetadataFieldNames.EMITTER_AMOUNT_AFTER_KEY; +import static com.cdx.bas.domain.metadata.MetadataFieldNames.EMITTER_AMOUNT_BEFORE_KEY; + +public class WithdrawProcessorImpl extends CashAmountService { + + private final WithdrawAmountServiceImpl withdrawAmountService; + + public WithdrawProcessorImpl(TransactionValidator transactionValidator, TransactionStatusServicePort transactionStatusService, TransactionServicePort transactionService, BankAccountServicePort bankAccountService, WithdrawAmountServiceImpl withdrawAmountService) { + super(transactionValidator, transactionStatusService, transactionService, bankAccountService); + this.withdrawAmountService = withdrawAmountService; + } + + @Override + protected Transaction processType(CashTransactionProcessingDetails cashTransactionProcessingDetails) { + cashTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_BEFORE_KEY, cashTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + withdrawAmountService.applyToAccount(cashTransactionProcessingDetails); + cashTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_AFTER_KEY, cashTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + return cashTransactionProcessingDetails.getTransaction(); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/DigitalTransactionProcessor.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/DigitalTransactionProcessor.java new file mode 100644 index 00000000..8a70ce4f --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/DigitalTransactionProcessor.java @@ -0,0 +1,59 @@ +package com.cdx.bas.application.bank.transaction.category.digital; + +import com.cdx.bas.application.bank.transaction.TransactionProcessorTemplate; +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import com.cdx.bas.domain.message.MessageFormatter; +import lombok.AllArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; + +import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.COMPLETED; +import static com.cdx.bas.domain.message.CommonMessages.*; + +@AllArgsConstructor +public abstract class DigitalTransactionProcessor extends TransactionProcessorTemplate { + + private static final Logger logger = LoggerFactory.getLogger(DigitalTransactionProcessor.class); + + private final TransactionStatusServicePort transactionStatusService; + private final TransactionServicePort transactionService; + private final BankAccountServicePort bankAccountService; + + protected abstract Transaction processType(DigitalTransactionProcessingDetails digitalTransactionProcessingDetails); + + @Override + protected Transaction processCategory(Transaction transaction, Map metadata) { + BankAccount emitterBankAccount = bankAccountService.findBankAccount(transaction.getEmitterAccountId()); + BankAccount receiverBankAccount = bankAccountService.findBankAccount(transaction.getReceiverAccountId()); + Transaction currentTransaction = transactionStatusService.setAsOutstanding(transaction); + + DigitalTransactionProcessingDetails digitalTransactionProcessingDetails = new DigitalTransactionProcessingDetails(currentTransaction, + emitterBankAccount, + receiverBankAccount, + metadata); + processType(digitalTransactionProcessingDetails); + + Transaction completedTransaction = transactionStatusService.setStatus(currentTransaction, COMPLETED, metadata); + bankAccountService.updateBankAccount(emitterBankAccount); + bankAccountService.updateBankAccount(receiverBankAccount); + + logger.debug(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, COMPLETED_STATUS, + List.of(CREDIT_DETAIL + currentTransaction.getAmount(), + EMITTER_BANK_ACCOUNT_DETAIL + currentTransaction.getEmitterAccountId(), + RECEIVER_BANK_ACCOUNT_DETAIL + currentTransaction.getReceiverAccountId()))); + return completedTransaction; + } + + @Override + protected void persist(Transaction transaction) { + transactionService.update(transaction); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorFactory.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorFactory.java new file mode 100644 index 00000000..39946845 --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorFactory.java @@ -0,0 +1,33 @@ +package com.cdx.bas.application.bank.transaction.category.digital.type.credit; + +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.type.credit.CreditAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + +@ApplicationScoped +public class CreditProcessorFactory { + private final TransactionStatusServicePort transactionStatusService; + private final TransactionServicePort transactionService; + private final BankAccountServicePort bankAccountService; + private final CreditAmountServiceImpl creditAmountService; + + @Inject + public CreditProcessorFactory(TransactionStatusServicePort transactionStatusService, + TransactionServicePort transactionService, + BankAccountServicePort bankAccountService, + CreditAmountServiceImpl creditAmountService) { + this.transactionStatusService = transactionStatusService; + this.transactionService = transactionService; + this.bankAccountService = bankAccountService; + this.creditAmountService = creditAmountService; + } + + @Produces + public CreditProcessorImpl createCreditProcessor() { + return new CreditProcessorImpl(transactionStatusService, transactionService, bankAccountService, creditAmountService); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorImpl.java new file mode 100644 index 00000000..481a6886 --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/credit/CreditProcessorImpl.java @@ -0,0 +1,31 @@ +package com.cdx.bas.application.bank.transaction.category.digital.type.credit; + +import com.cdx.bas.application.bank.transaction.category.digital.DigitalTransactionProcessor; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.digital.type.credit.CreditAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; + +import static com.cdx.bas.domain.metadata.MetadataFieldNames.*; + +public class CreditProcessorImpl extends DigitalTransactionProcessor { + + private final CreditAmountServiceImpl creditAmountService; + + public CreditProcessorImpl(TransactionStatusServicePort transactionStatusService, TransactionServicePort transactionService, BankAccountServicePort bankAccountService, CreditAmountServiceImpl creditAmountService) { + super(transactionStatusService, transactionService, bankAccountService); + this.creditAmountService = creditAmountService; + } + + @Override + protected Transaction processType(DigitalTransactionProcessingDetails digitalTransactionProcessingDetails) { + digitalTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_BEFORE_KEY, digitalTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + digitalTransactionProcessingDetails.getMetadata().put(RECEIVER_AMOUNT_BEFORE_KEY, digitalTransactionProcessingDetails.getReceiverBankAccount().getBalance().getAmount().toString()); + creditAmountService.transferBetweenAccounts(digitalTransactionProcessingDetails); + digitalTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_AFTER_KEY, digitalTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + digitalTransactionProcessingDetails.getMetadata().put(RECEIVER_AMOUNT_AFTER_KEY, digitalTransactionProcessingDetails.getReceiverBankAccount().getBalance().getAmount().toString()); + return digitalTransactionProcessingDetails.getTransaction(); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorFactory.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorFactory.java new file mode 100644 index 00000000..a3087734 --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorFactory.java @@ -0,0 +1,32 @@ +package com.cdx.bas.application.bank.transaction.category.digital.type.debit; + +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.type.credit.CreditAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.category.digital.type.debit.DebitAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +@ApplicationScoped +public class DebitProcessorFactory { + private final TransactionStatusServicePort transactionStatusService; + private final TransactionServicePort transactionService; + private final BankAccountServicePort bankAccountService; + private final DebitAmountServiceImpl debitAmountService; + + public DebitProcessorFactory(TransactionStatusServicePort transactionStatusService, + TransactionServicePort transactionService, + BankAccountServicePort bankAccountService, + DebitAmountServiceImpl debitAmountService) { + this.transactionStatusService = transactionStatusService; + this.transactionService = transactionService; + this.bankAccountService = bankAccountService; + this.debitAmountService = debitAmountService; + } + + @Produces + public DebitProcessorImpl createCreditProcessor() { + return new DebitProcessorImpl(transactionStatusService, transactionService, bankAccountService, debitAmountService); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorImpl.java new file mode 100644 index 00000000..e5c92921 --- /dev/null +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/category/digital/type/debit/DebitProcessorImpl.java @@ -0,0 +1,31 @@ +package com.cdx.bas.application.bank.transaction.category.digital.type.debit; + +import com.cdx.bas.application.bank.transaction.category.digital.DigitalTransactionProcessor; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.digital.type.debit.DebitAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; + +import static com.cdx.bas.domain.metadata.MetadataFieldNames.*; + +public class DebitProcessorImpl extends DigitalTransactionProcessor { + + private final DebitAmountServiceImpl debitAmountService; + + public DebitProcessorImpl(TransactionStatusServicePort transactionStatusService, TransactionServicePort transactionService, BankAccountServicePort bankAccountService, DebitAmountServiceImpl debitAmountService) { + super(transactionStatusService, transactionService, bankAccountService); + this.debitAmountService = debitAmountService; + } + + @Override + protected Transaction processType(DigitalTransactionProcessingDetails digitalTransactionProcessingDetails) { + digitalTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_BEFORE_KEY, digitalTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + digitalTransactionProcessingDetails.getMetadata().put(RECEIVER_AMOUNT_BEFORE_KEY, digitalTransactionProcessingDetails.getReceiverBankAccount().getBalance().getAmount().toString()); + debitAmountService.transferBetweenAccounts(digitalTransactionProcessingDetails); + digitalTransactionProcessingDetails.getMetadata().put(EMITTER_AMOUNT_AFTER_KEY, digitalTransactionProcessingDetails.getEmitterBankAccount().getBalance().getAmount().toString()); + digitalTransactionProcessingDetails.getMetadata().put(RECEIVER_AMOUNT_AFTER_KEY, digitalTransactionProcessingDetails.getReceiverBankAccount().getBalance().getAmount().toString()); + return digitalTransactionProcessingDetails.getTransaction(); + } +} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/status/TransactionStatusServiceImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/status/TransactionStatusServiceImpl.java index 4263792c..4f6dfaaf 100644 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/status/TransactionStatusServiceImpl.java +++ b/application/src/main/java/com/cdx/bas/application/bank/transaction/status/TransactionStatusServiceImpl.java @@ -6,22 +6,35 @@ import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; import com.cdx.bas.domain.message.MessageFormatter; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.OUTSTANDING; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; import static com.cdx.bas.domain.message.CommonMessages.*; +import static com.cdx.bas.domain.metadata.MetadataFieldNames.ERROR_KEY; -@RequestScoped +@ApplicationScoped public class TransactionStatusServiceImpl implements TransactionStatusServicePort { TransactionRepository transactionPersistencePort; + public TransactionStatus handleError(Exception exception, Map metadata) { + if (exception instanceof NoSuchElementException) { + metadata.put(ERROR_KEY, exception.getMessage()); + return TransactionStatus.ERROR; + } else { + metadata.put(ERROR_KEY, exception.getMessage()); + return TransactionStatus.REFUSED; + } + } + @Inject public TransactionStatusServiceImpl(TransactionRepository transactionPersistencePort) { this.transactionPersistencePort = transactionPersistencePort; diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorImpl.java deleted file mode 100644 index 3fca9ee8..00000000 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorImpl.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.cdx.bas.application.bank.transaction.type; - -import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.BankAccountException; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; - -public class CreditProcessorImpl implements DigitalTransactionProcessor { - @Override - public void processDigital(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount) throws TransactionException, BankAccountException { - - } -} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorImpl.java deleted file mode 100644 index 729b6464..00000000 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorImpl.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.cdx.bas.application.bank.transaction.type; - -import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.BankAccountException; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; - -public class DebitProcessorImpl implements DigitalTransactionProcessor{ - @Override - public void processDigital(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount) throws TransactionException, BankAccountException { - - } -} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorImpl.java deleted file mode 100644 index 2c1f8bb9..00000000 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorImpl.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.cdx.bas.application.bank.transaction.type; - -import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.BankAccountException; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; - -public class DepositProcessorImpl implements PhysicalCashTransactionProcessor{ - @Override - public void processPhysicalCash(Transaction transaction, BankAccount emitterBankAccount) throws TransactionException, BankAccountException { - - } -} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DigitalTransactionProcessor.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DigitalTransactionProcessor.java deleted file mode 100644 index 275ca999..00000000 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/DigitalTransactionProcessor.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.cdx.bas.application.bank.transaction.type; - -import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.BankAccountException; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; - -@FunctionalInterface -public interface DigitalTransactionProcessor { - void processDigital(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount) throws TransactionException, BankAccountException; -} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/PhysicalCashTransactionProcessor.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/PhysicalCashTransactionProcessor.java deleted file mode 100644 index f9080197..00000000 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/PhysicalCashTransactionProcessor.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.cdx.bas.application.bank.transaction.type; - -import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.BankAccountException; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; - -@FunctionalInterface -public interface PhysicalCashTransactionProcessor { - void processPhysicalCash(Transaction transaction, BankAccount emitterBankAccount) throws TransactionException, BankAccountException; -} diff --git a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImpl.java b/application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImpl.java deleted file mode 100644 index f7d33e25..00000000 --- a/application/src/main/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.cdx.bas.application.bank.transaction.type; - -import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.BankAccountException; -import com.cdx.bas.domain.bank.account.BankAccountServicePort; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; -import com.cdx.bas.domain.bank.transaction.TransactionServicePort; -import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; -import com.cdx.bas.domain.bank.transaction.type.TransactionProcessorServicePort; -import com.cdx.bas.domain.bank.transaction.validation.validator.TransactionValidator; -import com.cdx.bas.domain.message.MessageFormatter; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.transaction.Transactional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; - -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; -import static com.cdx.bas.domain.message.CommonMessages.*; -import static com.cdx.bas.domain.metadata.MetadataFieldNames.*; - - -@ApplicationScoped -public class TransactionProcessorServiceImpl implements TransactionProcessorServicePort { - - private static final Logger logger = LoggerFactory.getLogger(TransactionProcessorServiceImpl.class); - - private final TransactionValidator transactionValidator; - private final TransactionStatusServicePort transactionStatusService; - private final TransactionServicePort transactionService; - private final BankAccountServicePort bankAccountService; - - @Inject - public TransactionProcessorServiceImpl(TransactionValidator transactionValidator, - TransactionStatusServicePort transactionStatusService, - TransactionServicePort transactionService, - BankAccountServicePort bankAccountService) { - this.transactionValidator = transactionValidator; - this.transactionStatusService = transactionStatusService; - this.transactionService = transactionService; - this.bankAccountService = bankAccountService; - } - - @Override - @Transactional - public Transaction credit(Transaction transaction) { - Map metadata = new HashMap<>(); - TransactionStatus transactionStatus = COMPLETED; - try { - BankAccount emitterBankAccount = bankAccountService.findBankAccount(transaction.getEmitterAccountId()); - BankAccount receiverBankAccount = bankAccountService.findBankAccount(transaction.getReceiverAccountId()); - Transaction currentTransaction = transactionStatusService.setAsOutstanding(transaction); - - metadata = new HashMap<>(); - metadata.put(EMITTER_AMOUNT_BEFORE_KEY, emitterBankAccount.getBalance().getAmount().toString()); - metadata.put(RECEIVER_AMOUNT_BEFORE_KEY, receiverBankAccount.getBalance().getAmount().toString()); - bankAccountService.creditAmountToAccounts(currentTransaction, emitterBankAccount, receiverBankAccount); - metadata.put(EMITTER_AMOUNT_AFTER_KEY, emitterBankAccount.getBalance().getAmount().toString()); - metadata.put(RECEIVER_AMOUNT_AFTER_KEY, receiverBankAccount.getBalance().getAmount().toString()); - - Transaction completedTransaction = transactionStatusService.setStatus(currentTransaction, COMPLETED, metadata); - bankAccountService.updateBankAccount(emitterBankAccount); - bankAccountService.updateBankAccount(receiverBankAccount); - - logger.debug(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, COMPLETED_STATUS, - List.of(CREDIT_DETAIL + currentTransaction.getAmount(), - EMITTER_BANK_ACCOUNT_DETAIL + currentTransaction.getEmitterAccountId(), - RECEIVER_BANK_ACCOUNT_DETAIL + currentTransaction.getReceiverAccountId()))); - return completedTransaction; - } catch (NoSuchElementException exception) { - metadata = Map.of(ERROR_KEY, exception.getMessage()); - transactionStatus = ERROR; - throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, FAILED_STATUS, - Optional.of(NOT_FOUND_CAUSE), - List.of(TRANSACTION_ID_DETAIL + transaction.getId(), ERROR_DETAIL + exception.getMessage()))); - } catch (TransactionException exception) { - metadata = Map.of(ERROR_KEY, exception.getMessage()); - transactionStatus = REFUSED; - throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, REFUSED_STATUS, - Optional.of(TRANSACTION_ERROR_CAUSE), - List.of(TRANSACTION_ID_DETAIL + transaction.getId(), - ERROR_DETAIL + exception.getMessage()))); - } catch (BankAccountException exception) { - metadata = Map.of(ERROR_KEY, exception.getMessage()); - transactionStatus = REFUSED; - throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, CREDIT_ACTION, REFUSED_STATUS, - Optional.of(BANK_ACCOUNT_ERROR_CAUSE), - List.of(TRANSACTION_ID_DETAIL + transaction.getId(), - ERROR_DETAIL + exception.getMessage()))); - } finally { - transaction.setStatus(transactionStatus); - transactionService.update(transaction, metadata); - } - } - - @Override - public Transaction debit(Transaction currentTransaction) { - //TODO feature#45 - currentTransaction.setStatus(REFUSED); - transactionService.update(currentTransaction, new HashMap<>()); - return currentTransaction; - } - - @Override - @Transactional - public Transaction deposit(Transaction currentTransaction) { - Map metadata = new HashMap<>(); - TransactionStatus transactionStatus = COMPLETED; - try { - transactionValidator.validateCashTransaction(currentTransaction); - BankAccount emitterBankAccount = bankAccountService.findBankAccount(currentTransaction.getEmitterAccountId()); - - metadata = new HashMap<>(); - metadata.put(EMITTER_AMOUNT_BEFORE_KEY, emitterBankAccount.getBalance().getAmount().toString()); - bankAccountService.depositAmountToAccount(currentTransaction, emitterBankAccount); - metadata.put(EMITTER_AMOUNT_AFTER_KEY, emitterBankAccount.getBalance().getAmount().toString()); - Transaction completedTransaction = transactionStatusService.setStatus(currentTransaction, COMPLETED, metadata); - bankAccountService.updateBankAccount(emitterBankAccount); - - logger.debug(MessageFormatter.format(TRANSACTION_CONTEXT, DEPOSIT_ACTION, COMPLETED_STATUS, - List.of(TRANSACTION_ID_DETAIL + currentTransaction.getId(), - CREDIT_DETAIL + currentTransaction.getAmount(), - EMITTER_BANK_ACCOUNT_DETAIL + currentTransaction.getEmitterAccountId(), - RECEIVER_BANK_ACCOUNT_DETAIL + currentTransaction.getReceiverAccountId()))); - return completedTransaction; - } catch (TransactionException exception) { - metadata = Map.of(ERROR_KEY, exception.getMessage()); - transactionStatus = REFUSED; - throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, DEPOSIT_ACTION, REFUSED_STATUS, - Optional.of(TRANSACTION_ERROR_CAUSE), - List.of(TRANSACTION_ID_DETAIL + currentTransaction.getId(), - ERROR_DETAIL + exception.getMessage()))); - } catch (BankAccountException exception) { - metadata = Map.of(ERROR_KEY, exception.getMessage()); - transactionStatus = REFUSED; - throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, DEPOSIT_ACTION, REFUSED_STATUS, - Optional.of(BANK_ACCOUNT_ERROR_CAUSE), - List.of(TRANSACTION_ID_DETAIL + currentTransaction.getId(), - ERROR_DETAIL + exception.getMessage()))); - } finally { - currentTransaction.setStatus(transactionStatus); - transactionService.create(currentTransaction, metadata); - } - } - - @Override - @Transactional - public Transaction withdraw(Transaction currentTransaction) { - Map metadata = new HashMap<>(); - TransactionStatus transactionStatus = COMPLETED; - try { - transactionValidator.validateCashTransaction(currentTransaction); - BankAccount emitterBankAccount = bankAccountService.findBankAccount(currentTransaction.getEmitterAccountId()); - metadata = new HashMap<>(); - metadata.put(EMITTER_AMOUNT_BEFORE_KEY, emitterBankAccount.getBalance().getAmount().toString()); - bankAccountService.withdrawAmountToAccount(currentTransaction, emitterBankAccount); - metadata.put(EMITTER_AMOUNT_AFTER_KEY, emitterBankAccount.getBalance().getAmount().toString()); - Transaction completedTransaction = transactionStatusService.setStatus(currentTransaction, COMPLETED, metadata); - bankAccountService.updateBankAccount(emitterBankAccount); - - logger.debug(MessageFormatter.format(TRANSACTION_CONTEXT, WITHDRAW_ACTION, COMPLETED_STATUS, - List.of(TRANSACTION_ID_DETAIL + currentTransaction.getId(), - CREDIT_DETAIL + currentTransaction.getAmount(), - EMITTER_BANK_ACCOUNT_DETAIL + currentTransaction.getEmitterAccountId(), - RECEIVER_BANK_ACCOUNT_DETAIL + currentTransaction.getReceiverAccountId()))); - return completedTransaction; - } catch (TransactionException exception) { - metadata = Map.of(ERROR_KEY, exception.getMessage()); - transactionStatus = REFUSED; - throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, WITHDRAW_ACTION, REFUSED_STATUS, - Optional.of(TRANSACTION_ERROR_CAUSE), - List.of(TRANSACTION_ID_DETAIL + currentTransaction.getId(), - ERROR_DETAIL + exception.getMessage()))); - } catch (BankAccountException exception) { - metadata = Map.of(ERROR_KEY, exception.getMessage()); - transactionStatus = REFUSED; - throw new TransactionException(MessageFormatter.format(TRANSACTION_CONTEXT, WITHDRAW_ACTION, REFUSED_STATUS, - Optional.of(BANK_ACCOUNT_ERROR_CAUSE), - List.of(TRANSACTION_ID_DETAIL + currentTransaction.getId(), - ERROR_DETAIL + exception.getMessage()))); - } finally { - currentTransaction.setStatus(transactionStatus); - transactionService.create(currentTransaction, metadata); - } - } -} - - diff --git a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountMapperTest.java b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountMapperTest.java index 598ae525..85b45e1a 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountMapperTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountMapperTest.java @@ -12,7 +12,7 @@ import com.cdx.bas.domain.bank.customer.maritalstatus.MaritalStatus; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; import com.cdx.bas.domain.money.Money; import io.quarkus.test.InjectMock; import io.quarkus.test.common.WithTestResource; @@ -28,7 +28,7 @@ import java.util.*; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.ERROR; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.CREDIT; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.anyLong; @@ -36,7 +36,7 @@ @QuarkusTest @WithTestResource(H2DatabaseTestResource.class) -public class BankAccountMapperTest { +class BankAccountMapperTest { @Inject BankAccountMapper bankAccountMapper; @@ -49,9 +49,9 @@ public class BankAccountMapperTest { @InjectMock CustomerRepository customerRepository; - + @Test - public void toDto_shouldReturnNullDto_whenEntityIsNull() { + void toDto_shouldReturnNullDto_whenEntityIsNull() { BankAccount dto = bankAccountMapper.toDto(null); assertThat(dto).isNull(); @@ -60,7 +60,7 @@ public void toDto_shouldReturnNullDto_whenEntityIsNull() { } @Test - public void toEntity_shouldReturnNullEntity_whenDtoIsNull() { + void toEntity_shouldReturnNullEntity_whenDtoIsNull() { // Act BankAccountEntity entity = bankAccountMapper.toEntity(null); @@ -70,7 +70,7 @@ public void toEntity_shouldReturnNullEntity_whenDtoIsNull() { } @Test - public void toDto_shouldReturnNullDto_whenEntityHasEmptyObject() { + void toDto_shouldReturnNullDto_whenEntityHasEmptyObject() { // Act BankAccount dto = bankAccountMapper.toDto(new BankAccountEntity()); @@ -80,7 +80,7 @@ public void toDto_shouldReturnNullDto_whenEntityHasEmptyObject() { } @Test - public void toDto_shouldMapAccountTypeOnly_whenEntityHasAccount_withOnlyAccountType() { + void toDto_shouldMapAccountTypeOnly_whenEntityHasAccount_withOnlyAccountType() { // Arrange BankAccountEntity entity = new BankAccountEntity(); entity.setType(AccountType.CHECKING); @@ -98,7 +98,7 @@ public void toDto_shouldMapAccountTypeOnly_whenEntityHasAccount_withOnlyAccountT } @Test - public void toEntity_shouldMapNullValues_whenDtoHasAccount_withOnlyAccountTypeAndId() { + void toEntity_shouldMapNullValues_whenDtoHasAccount_withOnlyAccountTypeAndId() { // Arrange BankAccount bankAccount = new CheckingBankAccount(); bankAccount.setId(1L); @@ -116,7 +116,7 @@ public void toEntity_shouldMapNullValues_whenDtoHasAccount_withOnlyAccountTypeAn } @Test - public void toDto_shouldMapEveryFieldsOfDto_whenEntityHasValues() { + void toDto_shouldMapEveryFieldsOfDto_whenEntityHasValues() { // Arrange Instant timestamp = Instant.now(); BankAccountEntity entity = new BankAccountEntity(); @@ -134,28 +134,27 @@ public void toDto_shouldMapEveryFieldsOfDto_whenEntityHasValues() { transactionEntities.add(transactionEntity2); entity.setIssuedTransactions(transactionEntities); - Transaction transaction1 = Transaction.builder() - .id(2L) - .type(CREDIT) - .emitterAccountId(2000L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(timestamp) - .label("transaction test") - .build(); - Transaction transaction2 = Transaction.builder() - .id(2L) - .type(CREDIT) - .emitterAccountId(5000L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(timestamp) - .label("transaction test") - .build(); + Transaction transaction1 = new Transaction(); + transaction1.setId(2L); + transaction1.setType(CREDIT); + transaction1.setEmitterAccountId(2000L); + transaction1.setReceiverAccountId(77L); + transaction1.setAmount(new BigDecimal("100")); + transaction1.setCurrency("EUR"); + transaction1.setStatus(ERROR); + transaction1.setDate(timestamp); + transaction1.setLabel("transaction test"); + + Transaction transaction2 = new Transaction(); + transaction2.setId(2L); + transaction2.setType(CREDIT); + transaction2.setEmitterAccountId(5000L); + transaction2.setReceiverAccountId(77L); + transaction2.setAmount(new BigDecimal("100")); + transaction2.setCurrency("EUR"); + transaction2.setStatus(ERROR); + transaction2.setDate(timestamp); + transaction2.setLabel("transaction test"); when(transactionMapper.toDto(transactionEntity1)).thenReturn(transaction1); when(transactionMapper.toDto(transactionEntity2)).thenReturn(transaction2); @@ -178,7 +177,7 @@ public void toDto_shouldMapEveryFieldsOfDto_whenEntityHasValues() { } @Test - public void toEntity_shouldThrowNoSuchElementException_whenCustomerIsNotFound() { + void toEntity_shouldThrowNoSuchElementException_whenCustomerIsNotFound() { // Arrange Instant timestamp = Instant.now(); BankAccount dto = new CheckingBankAccount(); @@ -190,29 +189,28 @@ public void toEntity_shouldThrowNoSuchElementException_whenCustomerIsNotFound() customers.add(customer.getId()); dto.setCustomersId(customers); Set transactions = new HashSet<>(); - Transaction transaction1 = Transaction.builder() - .id(2L) - .emitterAccountId(2000L) - .receiverAccountId(77L) - .amount(new BigDecimal(100)) - .type(TransactionType.CREDIT) - .status(TransactionStatus.ERROR) - .date(timestamp) - .label("transaction test") - .metadata(Map.of("amount_before", "0", "amount_after", "350")) - .build(); + Transaction transaction1 = new Transaction(); + transaction1.setId(2L); + transaction1.setEmitterAccountId(2000L); + transaction1.setReceiverAccountId(77L); + transaction1.setAmount(new BigDecimal(100)); + transaction1.setType(TransactionType.CREDIT); + transaction1.setStatus(TransactionStatus.ERROR); + transaction1.setDate(timestamp); + transaction1.setLabel("transaction test"); + transaction1.setMetadata(Map.of("amount_before", "0", "amount_after", "350")); transactions.add(transaction1); - Transaction transaction2 = Transaction.builder() - .id(2L) - .emitterAccountId(5000L) - .receiverAccountId(77L) - .amount(new BigDecimal(100)) - .type(TransactionType.CREDIT) - .status(TransactionStatus.ERROR) - .date(timestamp) - .label("transaction test") - .metadata(Map.of("amount_before", "0", "amount_after", "350")) - .build(); + + Transaction transaction2 = new Transaction(); + transaction2.setId(2L); + transaction2.setEmitterAccountId(5000L); + transaction2.setReceiverAccountId(77L); + transaction2.setAmount(new BigDecimal(100)); + transaction2.setType(TransactionType.CREDIT); + transaction2.setStatus(TransactionStatus.ERROR); + transaction2.setDate(timestamp); + transaction2.setLabel("transaction test"); + transaction2.setMetadata(Map.of("amount_before", "0", "amount_after", "350")); transactions.add(transaction2); dto.setIssuedTransactions(transactions); @@ -233,7 +231,7 @@ public void toEntity_shouldThrowNoSuchElementException_whenCustomerIsNotFound() } @Test - public void toEntity_shouldMapEveryFieldsOfEntity_whenDtoHasValues() { + void toEntity_shouldMapEveryFieldsOfEntity_whenDtoHasValues() { // Arrange Instant timestamp = Instant.now(); BankAccount dto = new CheckingBankAccount(); @@ -245,29 +243,28 @@ public void toEntity_shouldMapEveryFieldsOfEntity_whenDtoHasValues() { customers.add(customer.getId()); dto.setCustomersId(customers); Set transactions = new HashSet<>(); - Transaction transaction1 = Transaction.builder() - .id(2L) - .emitterAccountId(2000L) - .receiverAccountId(77L) - .amount(new BigDecimal(100)) - .type(TransactionType.CREDIT) - .status(TransactionStatus.ERROR) - .date(timestamp) - .label("transaction test") - .metadata(Map.of("amount_before", "0", "amount_after", "350")) - .build(); + Transaction transaction1 = new Transaction(); + transaction1.setId(2L); + transaction1.setEmitterAccountId(2000L); + transaction1.setReceiverAccountId(77L); + transaction1.setAmount(new BigDecimal(100)); + transaction1.setType(TransactionType.CREDIT); + transaction1.setStatus(TransactionStatus.ERROR); + transaction1.setDate(timestamp); + transaction1.setLabel("transaction test"); + transaction1.setMetadata(Map.of("amount_before", "0", "amount_after", "350")); transactions.add(transaction1); - Transaction transaction2 = Transaction.builder() - .id(2L) - .emitterAccountId(5000L) - .receiverAccountId(77L) - .amount(new BigDecimal(100)) - .type(TransactionType.CREDIT) - .status(TransactionStatus.ERROR) - .date(timestamp) - .label("transaction test") - .metadata(Map.of("amount_before", "0", "amount_after", "350")) - .build(); + + Transaction transaction2 = new Transaction(); + transaction2.setId(2L); + transaction2.setEmitterAccountId(5000L); + transaction2.setReceiverAccountId(77L); + transaction2.setAmount(new BigDecimal(100)); + transaction2.setType(TransactionType.CREDIT); + transaction2.setStatus(TransactionStatus.ERROR); + transaction2.setDate(timestamp); + transaction2.setLabel("transaction test"); + transaction2.setMetadata(Map.of("amount_before", "0", "amount_after", "350")); transactions.add(transaction2); dto.setIssuedTransactions(transactions); diff --git a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java index 7530a7aa..97c9a2b2 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountRepositoryTest.java @@ -8,6 +8,8 @@ import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.TransactionPersistencePort; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import com.cdx.bas.domain.money.Money; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; @@ -27,7 +29,7 @@ import java.util.Set; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.DEPOSIT; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.DEPOSIT; import static org.assertj.core.api.Assertions.assertThat; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -133,16 +135,15 @@ void shouldUpdateBankAccountSuccessfully() { bankAccount.setType(AccountType.CHECKING); bankAccount.setBalance(new Money(new BigDecimal("0.00"))); bankAccount.setCustomersId(Set.of(6L)); - Transaction transaction = Transaction.builder() - .emitterAccountId(id) - .amount(new BigDecimal("1000.00")) - .currency("EUR") - .type(DEPOSIT) - .status(UNPROCESSED) - .date(timestamp) - .label("first deposit") - .metadata(Map.of("bill", "500,500")) - .build(); + Transaction transaction = new Transaction(); + transaction.setEmitterAccountId(id); + transaction.setAmount(new BigDecimal("1000.00")); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.DEPOSIT); + transaction.setStatus(TransactionStatus.UNPROCESSED); + transaction.setDate(timestamp); + transaction.setLabel("first deposit"); + transaction.setMetadata(Map.of("bill", "500,500")); bankAccount.getIssuedTransactions().add(transaction); // Act diff --git a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceTest.java b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceTest.java index 5df9b037..da932fe3 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/account/BankAccountServiceTest.java @@ -5,17 +5,19 @@ import com.cdx.bas.domain.bank.account.BankAccountPersistencePort; import com.cdx.bas.domain.bank.account.BankAccountServicePort; import com.cdx.bas.domain.bank.account.checking.CheckingBankAccount; +import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.account.validation.BankAccountValidator; import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import com.cdx.bas.domain.money.Money; -import io.quarkus.test.InjectMock; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; import java.math.BigDecimal; import java.time.Instant; @@ -26,8 +28,7 @@ import static com.cdx.bas.domain.bank.account.type.AccountType.CHECKING; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.ERROR; -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.Mockito.*; @@ -36,40 +37,38 @@ @WithTestResource(H2DatabaseTestResource.class) class BankAccountServiceTest { - @InjectMock + @Inject BankAccountPersistencePort bankAccountRepository; - @InjectMock + @Inject BankAccountValidator bankAccountValidator; - @InjectMock + @Inject TransactionServicePort transactionService; - @Inject + @InjectMocks BankAccountServicePort bankAccountService; @Test void shouldGetAllBankAccounts_whenRepositoryFoundBankAccounts() { // Arrange - BankAccount bankAccount1 = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); - BankAccount bankAccount2 = CheckingBankAccount.builder() - .id(100L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("10"))) - .customersId(Set.of(100L)) - .build(); - - BankAccount bankAccount3 = CheckingBankAccount.builder() - .id(101L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("20"))) - .customersId(Set.of(101L)) - .build(); + BankAccount bankAccount1 = new CheckingBankAccount(); + bankAccount1.setId(99L); + bankAccount1.setType(AccountType.CHECKING); // Assuming `AccountType.CHECKING` is your `CHECKING` enum value + bankAccount1.setBalance(Money.of(new BigDecimal("100"))); + bankAccount1.setCustomersId(Set.of(99L)); + + BankAccount bankAccount2 = new CheckingBankAccount(); + bankAccount2.setId(100L); + bankAccount2.setType(AccountType.CHECKING); + bankAccount2.setBalance(Money.of(new BigDecimal("10"))); + bankAccount2.setCustomersId(Set.of(100L)); + + BankAccount bankAccount3 = new CheckingBankAccount(); + bankAccount3.setId(101L); + bankAccount3.setType(AccountType.CHECKING); + bankAccount3.setBalance(Money.of(new BigDecimal("20"))); + bankAccount3.setCustomersId(Set.of(101L)); List bankAccounts = List.of(bankAccount1, bankAccount2, bankAccount3); when(bankAccountRepository.getAll()).thenReturn(bankAccounts); @@ -83,12 +82,11 @@ void shouldGetAllBankAccounts_whenRepositoryFoundBankAccounts() { @Test void shouldFindBankAccount_whenBankAccountExists() { // Arrange - BankAccount bankAccount = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); + BankAccount bankAccount = new CheckingBankAccount(); + bankAccount.setId(99L); + bankAccount.setType(AccountType.CHECKING); // Replace CHECKING with your specific enum if different + bankAccount.setBalance(Money.of(new BigDecimal("100"))); + bankAccount.setCustomersId(Set.of(99L)); when(bankAccountRepository.findById(1L)).thenReturn(Optional.of(bankAccount)); @@ -126,26 +124,26 @@ void shouldReturnNull_whenBankAccountDoesNotExist() { void shouldAddTransactionToBankAccount_whenTransactionDoesNotExist() { // Arrange Instant timestamp = Instant.now(); - BankAccount bankAccount = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .issuedTransactions(new HashSet<>()) - .build(); + // Creating BankAccount instance using setters + BankAccount bankAccount = new CheckingBankAccount(); + bankAccount.setId(99L); + bankAccount.setType(AccountType.CHECKING); // Replace CHECKING with your specific enum if different + bankAccount.setBalance(Money.of(new BigDecimal("100"))); + bankAccount.setCustomersId(Set.of(99L)); + bankAccount.setIssuedTransactions(new HashSet<>()); + +// Creating Transaction instance using setters + Transaction transaction = new Transaction(); + transaction.setId(10L); + transaction.setType(TransactionType.CREDIT); // Replace CREDIT with your specific enum if different + transaction.setEmitterAccountId(99L); + transaction.setReceiverAccountId(77L); + transaction.setAmount(new BigDecimal("100")); + transaction.setCurrency("EUR"); + transaction.setStatus(TransactionStatus.ERROR); // Replace ERROR with your specific enum if different + transaction.setDate(timestamp); // Assuming timestamp is a defined Instant variable + transaction.setLabel("transaction test"); - // Act - Transaction transaction = Transaction.builder() - .id(10L) - .type(CREDIT) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(timestamp) - .label("transaction test") - .build(); // Assert BankAccount actualBankAccount = bankAccountService.putTransaction(transaction, bankAccount); @@ -159,24 +157,23 @@ void shouldAddTransactionToBankAccount_whenTransactionDoesNotExist() { void shouldUpdateTransactionToBankAccount_whenTransactionExists() { // Arrange Instant timestamp = Instant.now(); - BankAccount bankAccount = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .issuedTransactions(new HashSet<>()) - .build(); - Transaction transaction = Transaction.builder() - .id(10L) - .type(CREDIT) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(timestamp) - .label("transaction test") - .build(); + BankAccount bankAccount = new CheckingBankAccount(); + bankAccount.setId(99L); + bankAccount.setType(AccountType.CHECKING); + bankAccount.setBalance(Money.of(new BigDecimal("100"))); + bankAccount.setCustomersId(Set.of(99L)); + bankAccount.setIssuedTransactions(new HashSet<>()); + + Transaction transaction = new Transaction(); + transaction.setId(10L); + transaction.setType(TransactionType.CREDIT); + transaction.setEmitterAccountId(99L); + transaction.setReceiverAccountId(77L); + transaction.setAmount(new BigDecimal("100")); + transaction.setCurrency("EUR"); + transaction.setStatus(TransactionStatus.ERROR); // Assuming ERROR is an enum value + transaction.setDate(timestamp); // Assuming timestamp is a defined Instant variable + transaction.setLabel("transaction test"); bankAccount.getIssuedTransactions().add(transaction); @@ -193,12 +190,11 @@ void shouldUpdateTransactionToBankAccount_whenTransactionExists() { @Test void shouldUpdateBankAccount_whenHasValidBankAccount() { // Arrange - BankAccount bankAccount = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); + BankAccount bankAccount = new CheckingBankAccount(); + bankAccount.setId(99L); + bankAccount.setType(AccountType.CHECKING); + bankAccount.setBalance(Money.of(new BigDecimal("100"))); + bankAccount.setCustomersId(Set.of(99L)); // Act BankAccount actualBankAccount = bankAccountService.updateBankAccount(bankAccount); @@ -210,200 +206,4 @@ void shouldUpdateBankAccount_whenHasValidBankAccount() { verifyNoMoreInteractions(bankAccountValidator, bankAccountRepository); verifyNoInteractions(transactionService); } - - @Test - void shouldCreditBankAccountWithAmount_whenHasValidCreditTransaction() { - // Arrange - Transaction transaction = Transaction.builder() - .id(10L) - .type(CREDIT) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(Instant.now()) - .label("transaction test") - .build(); - BankAccount bankAccountEmitter = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); - BankAccount bankAccountReceiver = CheckingBankAccount.builder() - .id(100L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("0"))) - .customersId(Set.of(99L)) - .build(); - - // Act - bankAccountService.creditAmountToAccounts(transaction, bankAccountEmitter, bankAccountReceiver); - - // Assert - assertThat(bankAccountEmitter.getBalance().getAmount()).isEqualTo(new BigDecimal("0.0")); - assertThat(bankAccountReceiver.getBalance().getAmount()).isEqualTo(new BigDecimal("100.0")); - verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); - } - - @Test - void shouldThrowTransactionException_whenCreditTransactionHasNegativeAmount() { - // Arrange - Transaction transaction = Transaction.builder() - .id(10L) - .type(CREDIT) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("-100")) - .currency("EUR") - .status(ERROR) - .date(Instant.now()) - .label("transaction test") - .build(); - BankAccount bankAccountEmitter = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); - BankAccount bankAccountReceiver = CheckingBankAccount.builder() - .id(100L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("0"))) - .customersId(Set.of(99L)) - .build(); - - // Act - try { - bankAccountService.creditAmountToAccounts(transaction, bankAccountEmitter, bankAccountReceiver); - } catch (TransactionException exception) { - // Assert - String expectedMessage = "Credit transaction: credit failed - should have positive value\nTransaction id:10\nEuro amount:-100.0"; - assertThat(exception.getMessage()).isEqualTo(expectedMessage); - verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); - } - } - - @Test - void shouldDepositAmountToBankAccount_whenHasValidDepositTransaction() { - // Arrange - Transaction transaction = Transaction.builder() - .id(10L) - .type(DEPOSIT) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(UNPROCESSED) - .date(Instant.now()) - .label("transaction test") - .build(); - BankAccount bankAccountEmitter = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("0"))) - .customersId(Set.of(99L)) - .build(); - - // Act - bankAccountService.depositAmountToAccount(transaction, bankAccountEmitter); - - // Assert - assertThat(bankAccountEmitter.getBalance().getAmount()).isEqualTo(new BigDecimal("100.0")); - verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); - } - - @Test - void shouldThrowTransactionException_whenDepositTransactionHasNegativeAmount() { - // Arrange - Transaction transaction = Transaction.builder() - .id(10L) - .type(DEPOSIT) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("-100")) - .currency("EUR") - .status(UNPROCESSED) - .date(Instant.now()) - .label("transaction test") - .build(); - BankAccount bankAccountEmitter = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); - - // Act - try { - bankAccountService.depositAmountToAccount(transaction, bankAccountEmitter); - } catch (TransactionException exception) { - // Assert - String expectedMessage = "Debit transaction: debit failed - should have positive value\nTransaction id:10\nEuro amount:-100.0"; - assertThat(exception.getMessage()).isEqualTo(expectedMessage); - verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); - } - } - - @Test - void shouldWithdrawAmountToBankAccount_whenHasValidWithdrawTransaction() { - // Arrange - Transaction transaction = Transaction.builder() - .id(10L) - .type(WITHDRAW) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(UNPROCESSED) - .date(Instant.now()) - .label("transaction test") - .build(); - BankAccount bankAccountEmitter = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); - - // Act - bankAccountService.withdrawAmountToAccount(transaction, bankAccountEmitter); - - // Assert - assertThat(bankAccountEmitter.getBalance().getAmount()).isEqualTo(new BigDecimal("0.0")); - verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); - } - - @Test - void shouldThrowTransactionException_whenWithdrawTransactionHasNegativeAmount() { - // Arrange - Transaction transaction = Transaction.builder() - .id(10L) - .type(WITHDRAW) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("-100")) - .currency("EUR") - .status(UNPROCESSED) - .date(Instant.now()) - .label("transaction test") - .build(); - BankAccount bankAccountEmitter = CheckingBankAccount.builder() - .id(99L) - .type(CHECKING) - .balance(Money.of(new BigDecimal("100"))) - .customersId(Set.of(99L)) - .build(); - - // Act - try { - bankAccountService.withdrawAmountToAccount(transaction, bankAccountEmitter); - } catch (TransactionException exception) { - // Assert - String expectedMessage = "Withdraw transaction: withdraw failed - should have positive value\nTransaction id:10\nEuro amount:-100.0"; - assertThat(exception.getMessage()).isEqualTo(expectedMessage); - verifyNoInteractions(transactionService, bankAccountValidator, bankAccountRepository); - } - } } diff --git a/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerMapperTest.java b/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerMapperTest.java index b2f12466..936b1d06 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerMapperTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/customer/CustomerMapperTest.java @@ -11,7 +11,7 @@ import com.cdx.bas.domain.bank.customer.maritalstatus.MaritalStatus; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; import com.cdx.bas.domain.money.Money; import io.quarkus.test.InjectMock; import io.quarkus.test.common.WithTestResource; @@ -29,7 +29,7 @@ import java.util.stream.Stream; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.ERROR; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.CREDIT; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; @@ -236,39 +236,41 @@ private BankAccount createBankAccount(long accountId, Instant timestamp) { customersId.add(99L); bankAccount.setCustomersId(customersId); Set transactions = new HashSet<>(); - transactions.add(Transaction.builder() - .id(100L) - .type(CREDIT) - .emitterAccountId(accountId) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(timestamp) - .label("transaction test") - .build()); - transactions.add(Transaction.builder() - .id(100L) - .type(CREDIT) - .emitterAccountId(accountId) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(timestamp) - .label("transaction test") - .build()); - transactions.add(Transaction.builder() - .id(100L) - .type(CREDIT) - .emitterAccountId(accountId) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .status(ERROR) - .date(timestamp) - .label("transaction test") - .build()); + Transaction transaction1 = new Transaction(); + transaction1.setId(100L); + transaction1.setType(TransactionType.CREDIT); + transaction1.setEmitterAccountId(accountId); + transaction1.setReceiverAccountId(77L); + transaction1.setAmount(new BigDecimal("100")); + transaction1.setCurrency("EUR"); + transaction1.setStatus(TransactionStatus.ERROR); + transaction1.setDate(timestamp); + transaction1.setLabel("transaction test"); + transactions.add(transaction1); + + Transaction transaction2 = new Transaction(); + transaction2.setId(100L); + transaction2.setType(TransactionType.CREDIT); + transaction2.setEmitterAccountId(accountId); + transaction2.setReceiverAccountId(77L); + transaction2.setAmount(new BigDecimal("100")); + transaction2.setCurrency("EUR"); + transaction2.setStatus(TransactionStatus.ERROR); + transaction2.setDate(timestamp); + transaction2.setLabel("transaction test"); + transactions.add(transaction2); + + Transaction transaction3 = new Transaction(); + transaction3.setId(100L); + transaction3.setType(TransactionType.CREDIT); + transaction3.setEmitterAccountId(accountId); + transaction3.setReceiverAccountId(77L); + transaction3.setAmount(new BigDecimal("100")); + transaction3.setCurrency("EUR"); + transaction3.setStatus(TransactionStatus.ERROR); + transaction3.setDate(timestamp); + transaction3.setLabel("transaction test"); + transactions.add(transaction3); bankAccount.setIssuedTransactions(transactions); return bankAccount; } diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java index c4cc1076..524d6140 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionMapperTest.java @@ -6,7 +6,7 @@ import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; import io.quarkus.test.InjectMock; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; @@ -48,10 +48,9 @@ void toDto_shouldThrowException_whenTransactionDtoDoesNotHaveEmitterBankAccount( @Test void toEntity_shouldThrowException_whenTransactionDtoDoesNotHaveEmitterBankAccount() { try { - Transaction transaction = Transaction.builder() - .id(10L) - .emitterAccountId(null) - .build(); + Transaction transaction = new Transaction(); + transaction.setId(10L); + transaction.setEmitterAccountId(null); transactionMapper.toEntity(transaction); fail(); } catch (NoSuchElementException exception) { @@ -100,18 +99,17 @@ void toEntity_shouldMapEntityValues_whenDtoHasValues() { Map metadata = Map.of("amount_before", "0", "amount_after", "100"); BankAccountEntity emitterBankAccountEntity = createBankAccountEntity(emitterAccountId); BankAccountEntity receiverBankAccountEntity = createBankAccountEntity(receiverAccountId); - Transaction transaction = Transaction.builder() - .id(10L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal(100)) - .currency("EUR") - .type(TransactionType.CREDIT) - .status(TransactionStatus.COMPLETED) - .date(timestamp) - .label("transaction test") - .metadata(metadata) - .build(); + Transaction transaction = new Transaction(); + transaction.setId(10L); + transaction.setEmitterAccountId(emitterAccountId); + transaction.setReceiverAccountId(receiverAccountId); + transaction.setAmount(new BigDecimal(100)); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.CREDIT); + transaction.setStatus(TransactionStatus.COMPLETED); + transaction.setDate(timestamp); + transaction.setLabel("transaction test"); + transaction.setMetadata(metadata); when(bankAccountRepository.findByIdOptional(99L)).thenReturn(Optional.of(emitterBankAccountEntity)); when(bankAccountRepository.findByIdOptional(77L)).thenReturn(Optional.of(receiverBankAccountEntity)); diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java index e7efa22e..2a0e2cab 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionRepositoryTest.java @@ -1,6 +1,8 @@ package com.cdx.bas.application.bank.transaction; import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; @@ -17,10 +19,9 @@ import java.time.ZoneOffset; import java.util.*; -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.COMPLETED; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.CREDIT; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.DEBIT; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.DEBIT; import static org.assertj.core.api.Assertions.assertThat; @@ -38,18 +39,24 @@ public void findById_shouldFindTransaction_whenIdIsFound() { Instant expectedInstant = OffsetDateTime.of( 2024, 6, 6, 12, 0, 0, 0, ZoneOffset.ofHours(1)).toInstant(); - Optional expectedTransaction = Optional.of(Transaction.builder() - .id(1L) - .emitterAccountId(1L) - .receiverAccountId(2L) - .amount(new BigDecimal("1600.00")) - .currency("EUR") - .type(CREDIT) - .status(COMPLETED) - .date(expectedInstant) - .label("transaction 1") - .metadata(Map.of("emitter_amount_before", "2000", "receiver_amount_before", "0", "emitter_amount_after", "400", "receiver_amount_after", "1600")) - .build()); + Transaction transaction = new Transaction(); + transaction.setId(1L); + transaction.setEmitterAccountId(1L); + transaction.setReceiverAccountId(2L); + transaction.setAmount(new BigDecimal("1600.00")); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.CREDIT); + transaction.setStatus(TransactionStatus.COMPLETED); + transaction.setDate(expectedInstant); + transaction.setLabel("transaction 1"); + transaction.setMetadata(Map.of( + "emitter_amount_before", "2000", + "receiver_amount_before", "0", + "emitter_amount_after", "400", + "receiver_amount_after", "1600" + )); + + Optional expectedTransaction = Optional.of(transaction); Optional actualTransaction = transactionRepository.findById(1); // Assert @@ -107,7 +114,7 @@ public void update_shouldMergeTransaction() { "transaction to process", Map.of("emitter_amount_before", "9200", "receiver_amount_before", "10000", "emitter_amount_after", "0", "receiver_amount_after", "19200")); Optional optionalExpectedTransaction = Optional.of(expectedTransaction); - Transaction updatedTransaction = transactionRepository.update(expectedTransaction); + Transaction updatedTransaction = transactionRepository.update(expectedTransaction); Optional actualOptionalTransaction = transactionRepository.findById(2); assertThat(updatedTransaction) diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceTest.java index 6ac00100..a9f2ae62 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionServiceTest.java @@ -1,25 +1,30 @@ package com.cdx.bas.application.bank.transaction; +import com.cdx.bas.application.bank.transaction.category.digital.type.credit.CreditProcessorImpl; +import com.cdx.bas.application.bank.transaction.category.digital.type.debit.DebitProcessorImpl; +import com.cdx.bas.application.bank.transaction.category.cash.type.deposit.DepositProcessorImpl; +import com.cdx.bas.application.bank.transaction.category.cash.type.withdraw.WithdrawProcessorImpl; import com.cdx.bas.domain.bank.transaction.*; -import com.cdx.bas.domain.bank.transaction.type.TransactionProcessorServicePort; -import com.cdx.bas.domain.bank.transaction.validation.validator.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewDigitalTransaction; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; import org.mockito.Mockito; import java.math.BigDecimal; import java.time.Clock; import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.COMPLETED; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.CREDIT; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.Mockito.*; @@ -29,21 +34,38 @@ class TransactionServiceTest { Clock clock = Mockito.mock(Clock.class); - TransactionPersistencePort transactionRepository = Mockito.mock(TransactionPersistencePort.class); - TransactionProcessorServicePort transactionProcessorServicePort = Mockito.mock(TransactionProcessorServicePort.class); - TransactionValidator transactionValidator = Mockito.mock(TransactionValidator.class); - TransactionServiceImpl transactionService = new TransactionServiceImpl(transactionRepository, transactionValidator, transactionProcessorServicePort); + @Inject + TransactionPersistencePort transactionRepository; + @Inject + TransactionValidator transactionValidator; + + @Inject + CreditProcessorImpl creditProcessorService; + + @Inject + DebitProcessorImpl debitProcessorService; + + @Inject + DepositProcessorImpl depositProcessorService; + + @Inject + WithdrawProcessorImpl withdrawProcessorService; + + @InjectMocks + TransactionServicePort transactionService; @Test void shouldReturnAllTransactions_whenRepositoryReturnsTransactions() { // Arrange - Set transactions = Set.of( - Transaction.builder().id(1L).build(), - Transaction.builder().id(2L).build(), - Transaction.builder().id(3L).build() - ); + Transaction transaction1 = new Transaction(); + transaction1.setId(1L); + Transaction transaction2 = new Transaction(); + transaction2.setId(2L); + Transaction transaction3 = new Transaction(); + transaction3.setId(3L); + Set transactions = Set.of(transaction1, transaction2, transaction3); when(transactionRepository.getAll()).thenReturn(transactions); // Act @@ -58,11 +80,17 @@ void shouldReturnAllTransactions_whenRepositoryReturnsTransactions() { @Test void shouldReturnTransactionCorrespondingToStatus_whenStatusIsValid() { // Arrange - Set transactions = Set.of( - Transaction.builder().id(1L).status(COMPLETED).build(), - Transaction.builder().id(2L).status(COMPLETED).build(), - Transaction.builder().id(3L).status(COMPLETED).build() - ); + Transaction transaction1 = new Transaction(); + transaction1.setId(1L); + transaction1.setStatus(TransactionStatus.COMPLETED); + Transaction transaction2 = new Transaction(); + transaction2.setId(2L); + transaction2.setStatus(TransactionStatus.COMPLETED); + Transaction transaction3 = new Transaction(); + transaction3.setId(3L); + transaction3.setStatus(TransactionStatus.COMPLETED); + Set transactions = Set.of(transaction1, transaction2, transaction3); + when(transactionRepository.findAllByStatus(COMPLETED)).thenReturn(transactions); // Act @@ -93,18 +121,17 @@ void shouldCreateTransaction_whenNewTransactionIsValid() { NewDigitalTransaction newTransaction = new NewDigitalTransaction(99L, 77L, new BigDecimal("100"), "EUR", CREDIT, "transaction test", new HashMap<>()); - Transaction transactionToCreate = Transaction.builder() - .id(null) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("100")) - .currency("EUR") - .label("transaction test") - .type(CREDIT) - .status(UNPROCESSED) - .date(timestamp) - .metadata(new HashMap<>()) - .build(); + Transaction transactionToCreate = new Transaction(); + transactionToCreate.setId(null); + transactionToCreate.setEmitterAccountId(99L); + transactionToCreate.setReceiverAccountId(77L); + transactionToCreate.setAmount(new BigDecimal("100")); + transactionToCreate.setCurrency("EUR"); + transactionToCreate.setLabel("transaction test"); + transactionToCreate.setType(TransactionType.CREDIT); + transactionToCreate.setStatus(TransactionStatus.UNPROCESSED); + transactionToCreate.setDate(timestamp); + transactionToCreate.setMetadata(new HashMap<>()); when(clock.instant()).thenReturn(timestamp); // Act @@ -131,7 +158,7 @@ void shouldThrowException_whenNewTransactionIsInvalid() { try { // Act transactionService.createDigitalTransaction(new NewDigitalTransaction(null, null, null, null, null, null, null)); - fail("should throw exception"); + fail("should throw exception"); } catch (TransactionException exception) { // Assert assertThat(exception.getMessage()).isEqualTo("invalid transaction..."); @@ -144,23 +171,22 @@ void shouldThrowException_whenNewTransactionIsInvalid() { @Test void shouldFindTransaction_whenTransactionExists() { // Arrange - Transaction transaction = Transaction.builder() - .id(1L) - .amount(new BigDecimal(100)) - .emitterAccountId(100L) - .receiverAccountId(200L) - .type(CREDIT) - .status(UNPROCESSED) - .date(Instant.now()) - .label("deposit of 100 euros") - .build(); + Transaction transaction = new Transaction(); + transaction.setId(1L); + transaction.setAmount(new BigDecimal(100)); + transaction.setEmitterAccountId(100L); + transaction.setReceiverAccountId(200L); + transaction.setType(TransactionType.CREDIT); + transaction.setStatus(TransactionStatus.UNPROCESSED); + transaction.setDate(Instant.now()); + transaction.setLabel("deposit of 100 euros"); when(transactionRepository.findById(1L)).thenReturn(Optional.of(transaction)); // Act - Transaction actualTransaction = transactionService.findTransaction(1L); + Optional actualTransaction = transactionService.findTransaction(1L); // Assert - assertThat(actualTransaction).isEqualTo(transaction); + assertThat(actualTransaction).contains(transaction); verify(transactionRepository).findById(1L); verifyNoMoreInteractions(transactionRepository); } @@ -171,10 +197,10 @@ void shouldReturnNull_whenTransactionDoesNotExist() { when(transactionRepository.findById(1L)).thenReturn(Optional.empty()); // Act - Transaction actualTransaction = transactionService.findTransaction(1L); + Optional actualTransaction = transactionService.findTransaction(1L); // Assert - assertThat(actualTransaction).isNull(); + assertThat(actualTransaction).isEmpty(); verify(transactionRepository).findById(1L); verifyNoMoreInteractions(transactionRepository); } @@ -182,23 +208,20 @@ void shouldReturnNull_whenTransactionDoesNotExist() { @Test void shouldProcessBankAccountCredit_whenTransactionHasCreditType() { // Arrange - Transaction transaction = Transaction.builder() - .id(1L) - .amount(new BigDecimal(100)) - .emitterAccountId(100L) - .receiverAccountId(200L) - .type(CREDIT) - .status(UNPROCESSED) - .date(Instant.now()) - .label("deposit of 100 euros") - .build(); + Transaction transaction = new Transaction(); + transaction.setId(1L); + transaction.setAmount(new BigDecimal(100)); + transaction.setEmitterAccountId(100L); + transaction.setReceiverAccountId(200L); + transaction.setType(TransactionType.CREDIT); + transaction.setStatus(TransactionStatus.UNPROCESSED); + transaction.setDate(Instant.now()); + transaction.setLabel("deposit of 100 euros"); // Act transactionService.processDigitalTransaction(transaction); // Assert - verify(transactionProcessorServicePort).credit(transaction); - verifyNoMoreInteractions(transactionProcessorServicePort); } @Test @@ -214,8 +237,6 @@ void shouldProcessBankAccountDeposit_whenValidTransactionDeposit() { transactionService.deposit(newTransaction); // Assert - verify(transactionProcessorServicePort).deposit(any()); - verifyNoMoreInteractions(transactionProcessorServicePort); } @Test @@ -235,11 +256,11 @@ void shouldThrowValidationException_whenInvalidTransactionDeposit() { transactionService.deposit(newTransaction); } catch (TransactionException exception) { assertThat(exception.getMessage()).isEqualTo(""" - Amount must not be null. - Metadata must not be null. - Emitter account id must not be null. - Currency must not be null. - """); + Amount must not be null. + Metadata must not be null. + Emitter account id must not be null. + Currency must not be null. + """); } } @@ -256,7 +277,5 @@ void shouldProcessBankAccountWithdraw_whenValidTransactionWithdraw() { transactionService.withdraw(newTransaction); // Assert - verify(transactionProcessorServicePort).withdraw(any()); - verifyNoMoreInteractions(transactionProcessorServicePort); } } diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceTest.java index 02bc4559..15d6f789 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionStatusServiceTest.java @@ -4,7 +4,7 @@ import com.cdx.bas.domain.bank.transaction.TransactionException; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; import io.quarkus.test.junit.QuarkusTest; @@ -37,31 +37,29 @@ class TransactionStatusServiceTest { @Test @Transactional void setAsOutstanding_shouldSetStatusAndUpdate_whenStatusIsOutstanding() { - Transaction transaction = Transaction.builder() - .id(9L) - .emitterAccountId(8L) - .receiverAccountId(7L) - .type(TransactionType.DEBIT) - .amount(new BigDecimal("5000.00")) - .currency("EUR") - .status(TransactionStatus.UNPROCESSED) - .date(OffsetDateTime.parse("2024-12-06T19:00:10+01:00").toInstant()) - .label("transaction 9") - .metadata(Map.of()) - .build(); - - Transaction expectedTransaction = Transaction.builder() - .id(9L) - .emitterAccountId(8L) - .receiverAccountId(7L) - .type(TransactionType.DEBIT) - .amount(new BigDecimal("5000.00")) - .currency("EUR") - .status(TransactionStatus.OUTSTANDING) - .date(OffsetDateTime.parse("2024-12-06T19:00:10+01:00").toInstant()) - .label("transaction 9") - .metadata(Map.of()) - .build(); + Transaction transaction = new Transaction(); + transaction.setId(9L); + transaction.setEmitterAccountId(8L); + transaction.setReceiverAccountId(7L); + transaction.setType(TransactionType.DEBIT); + transaction.setAmount(new BigDecimal("5000.00")); + transaction.setCurrency("EUR"); + transaction.setStatus(TransactionStatus.UNPROCESSED); + transaction.setDate(OffsetDateTime.parse("2024-12-06T19:00:10+01:00").toInstant()); + transaction.setLabel("transaction 9"); + transaction.setMetadata(Map.of()); + + Transaction expectedTransaction = new Transaction(); + expectedTransaction.setId(9L); + expectedTransaction.setEmitterAccountId(8L); + expectedTransaction.setReceiverAccountId(7L); + expectedTransaction.setType(TransactionType.DEBIT); + expectedTransaction.setAmount(new BigDecimal("5000.00")); + expectedTransaction.setCurrency("EUR"); + expectedTransaction.setStatus(TransactionStatus.OUTSTANDING); + expectedTransaction.setDate(OffsetDateTime.parse("2024-12-06T19:00:10+01:00").toInstant()); + expectedTransaction.setLabel("transaction 9"); + expectedTransaction.setMetadata(Map.of()); Transaction actualTransaction = transactionStatusServicePort.setAsOutstanding(transaction); assertThat(actualTransaction) @@ -72,9 +70,8 @@ void setAsOutstanding_shouldSetStatusAndUpdate_whenStatusIsOutstanding() { @Test @Transactional void setAsOutstanding_shouldThrowTransactionException_whenStatusIsNotOutstanding() { - Transaction transaction = Transaction.builder() - .status(ERROR) - .build(); + Transaction transaction = new Transaction(); + transaction.setStatus(TransactionStatus.ERROR); try { transactionStatusServicePort.setAsOutstanding(transaction); @@ -88,36 +85,34 @@ void setAsOutstanding_shouldThrowTransactionException_whenStatusIsNotOutstanding @Transactional void setStatus_shouldSetStatusAndUpdate_whenTransactionIsValid() { // Arrange - Transaction transaction = Transaction.builder() - .id(7L) - .emitterAccountId(3L) - .receiverAccountId(1L) - .type(TransactionType.CREDIT) - .amount(new BigDecimal("1000.00")) - .currency("EUR") - .status(TransactionStatus.UNPROCESSED) - .date(OffsetDateTime.parse("2024-12-06T18:00:00+01:00").toInstant()) // Convert OffsetDateTime to Instant - .label("transaction 7") - .metadata(Map.of()) - .build(); + Transaction transaction = new Transaction(); + transaction.setId(7L); + transaction.setEmitterAccountId(3L); + transaction.setReceiverAccountId(1L); + transaction.setType(TransactionType.CREDIT); + transaction.setAmount(new BigDecimal("1000.00")); + transaction.setCurrency("EUR"); + transaction.setStatus(TransactionStatus.UNPROCESSED); + transaction.setDate(OffsetDateTime.parse("2024-12-06T18:00:00+01:00").toInstant()); // Convert OffsetDateTime to Instant + transaction.setLabel("transaction 7"); + transaction.setMetadata(new HashMap<>()); // or `Map.of()` if an immutable map is fine Map metadata = Map.of("error", "Transaction 1 deposit error for amount 100: error"); // Act Transaction actualTransaction = transactionStatusServicePort.saveStatus(transaction, ERROR, metadata); // Assert - Transaction expectedTransaction = Transaction.builder() - .id(7L) - .emitterAccountId(3L) - .receiverAccountId(1L) - .type(TransactionType.CREDIT) - .amount(new BigDecimal("1000.00")) - .currency("EUR") - .status(TransactionStatus.ERROR) - .date(OffsetDateTime.parse("2024-12-06T18:00:00+01:00").toInstant()) // Convert OffsetDateTime to Instant - .label("transaction 7") - .metadata(metadata) - .build(); + Transaction expectedTransaction = new Transaction(); + expectedTransaction.setId(7L); + expectedTransaction.setEmitterAccountId(3L); + expectedTransaction.setReceiverAccountId(1L); + expectedTransaction.setType(TransactionType.CREDIT); + expectedTransaction.setAmount(new BigDecimal("1000.00")); + expectedTransaction.setCurrency("EUR"); + expectedTransaction.setStatus(TransactionStatus.ERROR); + expectedTransaction.setDate(OffsetDateTime.parse("2024-12-06T18:00:00+01:00").toInstant()); // Convert OffsetDateTime to Instant + expectedTransaction.setLabel("transaction 7"); + expectedTransaction.setMetadata(metadata); // Assuming 'metadata' is a Map variable defined elsewhere assertThat(actualTransaction) .usingRecursiveComparison() diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionUtilsTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionUtilsTest.java index aff15377..17dd4e83 100644 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionUtilsTest.java +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/TransactionUtilsTest.java @@ -1,9 +1,10 @@ package com.cdx.bas.application.bank.transaction; -import com.cdx.bas.domain.bank.transaction.NewCashTransaction; -import com.cdx.bas.domain.bank.transaction.NewDigitalTransaction; import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewDigitalTransaction; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -15,9 +16,8 @@ import java.util.Map; import java.util.Set; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.DEBIT; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.DEBIT; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(MockitoExtension.class) @@ -40,16 +40,16 @@ void should_getNewDigitalTransaction_when_hasTransactioNewDigitalTransaction() { Transaction actualDigitalTransaction = TransactionUtils.getNewDigitalTransaction(newDigitalTransaction); // Assert - Transaction expectedDigitalTransaction = Transaction.builder() - .emitterAccountId(1L) - .receiverAccountId(2L) - .amount(new BigDecimal("100")) - .currency("USD") - .type(CREDIT) - .status(UNPROCESSED) - .label("credit transaction") - .metadata(new HashMap<>()) - .build(); + Transaction expectedDigitalTransaction = new Transaction(); + expectedDigitalTransaction.setEmitterAccountId(1L); + expectedDigitalTransaction.setReceiverAccountId(2L); + expectedDigitalTransaction.setAmount(new BigDecimal("100")); + expectedDigitalTransaction.setCurrency("USD"); + expectedDigitalTransaction.setType(TransactionType.CREDIT); + expectedDigitalTransaction.setStatus(TransactionStatus.UNPROCESSED); + expectedDigitalTransaction.setLabel("credit transaction"); + expectedDigitalTransaction.setMetadata(new HashMap<>()); + assertThat(actualDigitalTransaction) .usingRecursiveComparison() .ignoringFieldsOfTypes(Instant.class) @@ -73,13 +73,12 @@ void should_getNewCashTransaction_when_hasTransactioNewDigitalTransaction() { Transaction actualCashTransaction = TransactionUtils.getNewCashTransaction(newDigitalTransaction); // Assert - Transaction expectedCashTransaction = Transaction.builder() - .emitterAccountId(1L) - .amount(new BigDecimal("100")) - .currency("USD") - .status(UNPROCESSED) - .metadata(new HashMap<>()) - .build(); + Transaction expectedCashTransaction = new Transaction(); + expectedCashTransaction.setEmitterAccountId(1L); + expectedCashTransaction.setAmount(new BigDecimal("100")); + expectedCashTransaction.setCurrency("USD"); + expectedCashTransaction.setStatus(TransactionStatus.UNPROCESSED); + expectedCashTransaction.setMetadata(new HashMap<>()); assertThat(actualCashTransaction) .usingRecursiveComparison() .ignoringFieldsOfTypes(Instant.class) @@ -92,30 +91,28 @@ void should_getNewCashTransaction_when_hasTransactioNewDigitalTransaction() { @Test void shouldMergeOldTransactionWithNewTransaction_whenOldTransactionAndNewTransactionAreValid() { // Arrange - Transaction oldTransaction = Transaction.builder() - .id(1L) - .amount(new BigDecimal(100)) - .emitterAccountId(10L) - .receiverAccountId(11L) - .type(CREDIT) - .status(UNPROCESSED) - .date(Instant.now()) - .label("old transaction") - .build(); + Transaction oldTransaction = new Transaction(); + oldTransaction.setId(1L); + oldTransaction.setAmount(new BigDecimal(100)); + oldTransaction.setEmitterAccountId(10L); + oldTransaction.setReceiverAccountId(11L); + oldTransaction.setType(TransactionType.CREDIT); + oldTransaction.setStatus(TransactionStatus.UNPROCESSED); + oldTransaction.setDate(Instant.now()); + oldTransaction.setLabel("old transaction"); Instant dateAfter = Instant.now(); BigDecimal bigDecimalAfter = new BigDecimal(200); String labelAfter = "new transaction"; - Transaction newTransaction = Transaction.builder() - .id(2L) - .amount(bigDecimalAfter) - .emitterAccountId(20L) - .receiverAccountId(22L) - .type(DEBIT) - .status(UNPROCESSED) - .date(dateAfter) - .label(labelAfter) - .build(); + Transaction newTransaction = new Transaction(); + newTransaction.setId(2L); + newTransaction.setAmount(bigDecimalAfter); + newTransaction.setEmitterAccountId(20L); + newTransaction.setReceiverAccountId(22L); + newTransaction.setType(TransactionType.DEBIT); + newTransaction.setStatus(TransactionStatus.UNPROCESSED); + newTransaction.setDate(dateAfter); + newTransaction.setLabel(labelAfter); // Act Transaction actualTransaction = TransactionUtils.mergeTransactions(oldTransaction, newTransaction); @@ -132,18 +129,4 @@ void shouldMergeOldTransactionWithNewTransaction_whenOldTransactionAndNewTransac // Assert assertThat(actualTransaction).isEqualTo(oldTransaction); } - - @Test - public void test() { - Transaction t1 = Transaction.builder().build(); - Transaction t2 = Transaction.builder().build(); - - Set transactions = new HashSet(); - - transactions.add(t1); - transactions.add(t2); - - System.out.println(transactions); - } - } \ No newline at end of file diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorServiceImplTest.java new file mode 100644 index 00000000..a5ca432a --- /dev/null +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/CreditProcessorServiceImplTest.java @@ -0,0 +1,86 @@ +package com.cdx.bas.application.bank.transaction.type; + +import com.cdx.bas.application.bank.transaction.category.digital.type.credit.CreditProcessorImpl; +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.money.Money; +import io.quarkus.test.common.WithTestResource; +import io.quarkus.test.h2.H2DatabaseTestResource; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusTest +@WithTestResource(H2DatabaseTestResource.class) +class CreditProcessorServiceImplTest { + + @Inject + BankAccountServicePort bankAccountService; + + @Inject + CreditProcessorImpl creditProcessorService; + + @Test + void should_creditReceiverAndDebitEmitter_when_transactionIsValidated() { + // Arrange + long emitterAccountId = 2L; + long receiverAccountId = 1L; + Transaction transaction = new Transaction(); + transaction.setId(5L); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.CREDIT); + transaction.setEmitterAccountId(emitterAccountId); + transaction.setReceiverAccountId(receiverAccountId); + transaction.setAmount(new BigDecimal("600.99")); + transaction.setStatus(TransactionStatus.UNPROCESSED); + ZonedDateTime dateTime = ZonedDateTime.of(2024, 11, 6, 18, 0, 0, 0, ZoneOffset.ofHours(1)); + Instant transactionDate = dateTime.toInstant(); + transaction.setDate(transactionDate); + transaction.setLabel("transaction 5"); + transaction.setMetadata(new HashMap<>()); + + // Act + Transaction actualTransaction = creditProcessorService.processTransaction(transaction); + + // Assert + Transaction expectedTransaction = new Transaction(); + expectedTransaction.setId(5L); + expectedTransaction.setEmitterAccountId(2L); + expectedTransaction.setReceiverAccountId(1L); + expectedTransaction.setAmount(new BigDecimal("600.99")); + expectedTransaction.setCurrency("EUR"); + expectedTransaction.setType(TransactionType.CREDIT); + expectedTransaction.setStatus(TransactionStatus.COMPLETED); + expectedTransaction.setDate(Instant.parse("2024-11-06T17:00:00Z")); + expectedTransaction.setLabel("transaction 5"); + + Map metadata = new HashMap<>(); + metadata.put("emitter_amount_after", "999.01"); + metadata.put("receiver_amount_before", "400.00"); + metadata.put("emitter_amount_before", "1600.00"); + metadata.put("receiver_amount_after", "1000.99"); + expectedTransaction.setMetadata(metadata); + + assertThat(actualTransaction).isEqualTo(expectedTransaction); + BankAccount emitterAccount = bankAccountService.findBankAccount(emitterAccountId); + assertThat(emitterAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("999.01"))); + BankAccount receiverAccount = bankAccountService.findBankAccount(receiverAccountId); + assertThat(receiverAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("1000.99"))); + } +} \ No newline at end of file diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorServiceImplTest.java new file mode 100644 index 00000000..27f35372 --- /dev/null +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/DebitProcessorServiceImplTest.java @@ -0,0 +1,31 @@ +package com.cdx.bas.application.bank.transaction.type; + +import com.cdx.bas.application.bank.transaction.category.digital.type.debit.DebitProcessorImpl; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import io.quarkus.test.common.WithTestResource; +import io.quarkus.test.h2.H2DatabaseTestResource; +import io.quarkus.test.junit.QuarkusTest; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +@QuarkusTest +@WithTestResource(H2DatabaseTestResource.class) +class DebitProcessorServiceImplTest { + + @Mock + private TransactionStatusServicePort transactionStatusService; + + @Mock + private TransactionServicePort transactionService; + + @Mock + private BankAccountServicePort bankAccountService; + + @InjectMocks + private DebitProcessorImpl debitProcessorService; +} \ No newline at end of file diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorServiceImplTest.java new file mode 100644 index 00000000..b9e5e696 --- /dev/null +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/DepositProcessorServiceImplTest.java @@ -0,0 +1,28 @@ +package com.cdx.bas.application.bank.transaction.type; + +import com.cdx.bas.application.bank.transaction.category.cash.type.deposit.DepositProcessorImpl; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import io.quarkus.test.common.WithTestResource; +import io.quarkus.test.h2.H2DatabaseTestResource; +import io.quarkus.test.junit.QuarkusTest; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +@QuarkusTest +@WithTestResource(H2DatabaseTestResource.class) +class DepositProcessorServiceImplTest { + + @Mock + private TransactionStatusServicePort transactionStatusService; + + @Mock + private TransactionServicePort transactionService; + + @Mock + private BankAccountServicePort bankAccountService; + + @InjectMocks + private DepositProcessorImpl depositProcessorService; +} \ No newline at end of file diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImplTest.java deleted file mode 100644 index 1c95273b..00000000 --- a/application/src/test/java/com/cdx/bas/application/bank/transaction/type/TransactionProcessorServiceImplTest.java +++ /dev/null @@ -1,502 +0,0 @@ -package com.cdx.bas.application.bank.transaction.type; - -import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.BankAccountException; -import com.cdx.bas.domain.bank.account.BankAccountServicePort; -import com.cdx.bas.domain.bank.account.checking.CheckingBankAccount; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; -import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; -import com.cdx.bas.domain.bank.transaction.type.TransactionProcessorServicePort; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; -import com.cdx.bas.domain.money.Money; -import io.quarkus.test.InjectMock; -import io.quarkus.test.common.WithTestResource; -import io.quarkus.test.h2.H2DatabaseTestResource; -import io.quarkus.test.junit.QuarkusTest; -import jakarta.inject.Inject; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.math.BigDecimal; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -import static com.cdx.bas.domain.bank.account.type.AccountType.CHECKING; -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -@QuarkusTest -@WithTestResource(H2DatabaseTestResource.class) -class TransactionProcessorServiceImplTest { - - @InjectMock - TransactionStatusServicePort transactionStatusService; - - @InjectMock - BankAccountServicePort bankAccountService; - - @Inject - TransactionProcessorServicePort transactionProcessingService; - - @Test - void credit_shouldThrowNoSuchElementException_whenEmitterAccountIsNotFound() { - // Arrange - long emitterAccountId = 99L; - long receiverAccountId = 77L; - Instant instantDate = Instant.now(); - Transaction transaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - Map metadata = Map.of("error", "Emitter bank account 99 is not found."); - Transaction erroredTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(ERROR) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - - when(bankAccountService.findBankAccount(emitterAccountId)).thenThrow(new NoSuchElementException("Emitter bank account 99 is not found.")); - when(transactionStatusService.saveStatus(eq(transaction), eq(ERROR), eq(metadata))).thenReturn(erroredTransaction); - - // Act - try { - transactionProcessingService.credit(transaction); - } catch (NoSuchElementException exception) { - // Assert - assertThat(exception.getMessage()).isEqualTo("Transaction does not have emitter bank account entity."); - verify(bankAccountService).findBankAccount(emitterAccountId); - verifyNoMoreInteractions(bankAccountService, transactionStatusService); - } - } - - @Test - void credit_shouldThrowNoSuchElementException_whenReceiverAccountIsNotFound() { - // Arrange - long emitterAccountId = 99L; - long receiverAccountId = 77L; - Money amountOfMoney = Money.of(new BigDecimal("0")); - Instant instantDate = Instant.now(); - Transaction transaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - Map metadata = Map.of("error", "Receiver bank account 77 is not found."); - Transaction erroredTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(ERROR) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - BankAccount emitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(transaction)) - .build(); - - when(bankAccountService.findBankAccount(emitterAccountId)).thenReturn(emitterBankAccount); - when(bankAccountService.findBankAccount(receiverAccountId)).thenThrow(new NoSuchElementException("Receiver bank account 77 is not found.")); - when(transactionStatusService.saveStatus(eq(transaction), eq(ERROR), eq(metadata))).thenReturn(erroredTransaction); - - // Act - try { - transactionProcessingService.credit(transaction); - } catch (NoSuchElementException exception) { - // Assert - assertThat(exception.getMessage()).isEqualTo("Transaction does not have emitter bank account entity."); - verify(bankAccountService).findBankAccount(emitterAccountId); - verify(bankAccountService).findBankAccount(receiverAccountId); - verifyNoMoreInteractions(bankAccountService, transactionStatusService); - } - } - - @Test - void credit_shouldReturnCompletedTransaction_whenAccountsAreFoundAndCreditTransactionIsValid() { - // Arrange - long emitterAccountId = 2L; - long receiverAccountId = 1L; - Money amountOfMoney = Mockito.mock(Money.class); - Instant instantDate = Instant.now(); - Transaction transaction = Transaction.builder() - .id(1L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - Transaction outstandingTransaction = Transaction.builder() - .id(1L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - BankAccount emitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(transaction)) - .build(); - BankAccount receiverBankAccount = CheckingBankAccount.builder() - .id(receiverAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .build(); - - Map metadataAfter = Map.of("emitter_amount_before", "1000", - "receiver_amount_before", "0", - "emitter_amount_after", "0", - "receiver_amount_after", "1000"); - Transaction completedTransaction = Transaction.builder() - .id(1L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(COMPLETED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - BankAccount updatedEmitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(completedTransaction)) - .build(); - - when(bankAccountService.findBankAccount(emitterAccountId)).thenReturn(emitterBankAccount); - when(bankAccountService.findBankAccount(receiverAccountId)).thenReturn(receiverBankAccount); - when(transactionStatusService.setAsOutstanding(transaction)).thenReturn(outstandingTransaction); - when(transactionStatusService.setStatus(transaction, COMPLETED, metadataAfter)).thenReturn(completedTransaction); - when(amountOfMoney.getAmount()) - .thenReturn(new BigDecimal("1000")) - .thenReturn(new BigDecimal("0")) - .thenReturn(new BigDecimal("0")) - .thenReturn(new BigDecimal("1000")); - when(transactionStatusService.saveStatus(outstandingTransaction, COMPLETED, metadataAfter)).thenReturn(completedTransaction); - when(bankAccountService.putTransaction(completedTransaction, emitterBankAccount)).thenReturn(updatedEmitterBankAccount); - - // Act - Transaction returnedTransaction = transactionProcessingService.credit(transaction); - receiverBankAccount.getBalance().plus(emitterBankAccount.getBalance()); - emitterBankAccount.getBalance().minus(emitterBankAccount.getBalance()); - - // Assert - assertThat(returnedTransaction).usingRecursiveComparison() - .isEqualTo(Transaction.builder() - .id(1L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(COMPLETED) - .date(instantDate) - .metadata(new HashMap<>()) - .build()); - } - - @Test - void credit_shouldAddMoneyToTheReceiverAccount_whenAccountsAreFound_fromCreditTransaction() { - // Arrange - long emitterAccountId = 2L; - long receiverAccountId = 1L; - Money amountOfMoney = Mockito.mock(Money.class); - Instant instantDate = Instant.now(); - Transaction oldTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - BankAccount emitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(oldTransaction)) - .build(); - BankAccount receiverBankAccount = CheckingBankAccount.builder() - .id(receiverAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .build(); - - Transaction transaction = Transaction.builder() - .id(1L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - Transaction outstandingTransaction = Transaction.builder() - .id(1L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(OUTSTANDING) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - - Map metadataAfter = Map.of("emitter_amount_before", "1000", - "receiver_amount_before", "0", - "emitter_amount_after", "0", - "receiver_amount_after", "1000"); - Transaction completedTransaction = Transaction.builder() - .id(1L) - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(COMPLETED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - BankAccount updatedEmitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(oldTransaction)) - .build(); - - when(bankAccountService.findBankAccount(emitterAccountId)).thenReturn(emitterBankAccount); - when(bankAccountService.findBankAccount(receiverAccountId)).thenReturn(receiverBankAccount); - when(transactionStatusService.setAsOutstanding(transaction)).thenReturn(outstandingTransaction); - when(amountOfMoney.getAmount()) - .thenReturn(new BigDecimal("1000")) - .thenReturn(new BigDecimal("0")) - .thenReturn(new BigDecimal("0")) - .thenReturn(new BigDecimal("1000")); - when(transactionStatusService.saveStatus(outstandingTransaction, COMPLETED, metadataAfter)).thenReturn(completedTransaction); - when(bankAccountService.putTransaction(completedTransaction, emitterBankAccount)).thenReturn(updatedEmitterBankAccount); - - // Act - transactionProcessingService.credit(transaction); - - // Assert - verify(bankAccountService).findBankAccount(emitterAccountId); - verify(bankAccountService).findBankAccount(receiverAccountId); - verify(transactionStatusService).setAsOutstanding(transaction); - verify(bankAccountService).creditAmountToAccounts(outstandingTransaction, emitterBankAccount, receiverBankAccount); - verify(transactionStatusService).setStatus(transaction, COMPLETED, metadataAfter); - verify(bankAccountService).updateBankAccount(updatedEmitterBankAccount); - verify(bankAccountService).updateBankAccount(receiverBankAccount); - verifyNoMoreInteractions(bankAccountService, transactionStatusService); - } - - @Test - void credit_shouldReturnRefusedTransaction_whenTransferThrowTransactionExceptionException() { - // Arrange - long emitterAccountId = 1L; - long receiverAccountId = 2L; - Money amountOfMoney = Mockito.mock(Money.class); - Instant instantDate = Instant.now(); - Transaction oldTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("-1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - BankAccount emitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(oldTransaction)) - .build(); - BankAccount receiverBankAccount = CheckingBankAccount.builder() - .id(receiverAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .build(); - - Transaction transaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("-1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - Transaction outstandingTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("-1000")) - .status(OUTSTANDING) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - - when(bankAccountService.findBankAccount(emitterAccountId)).thenReturn(emitterBankAccount); - when(bankAccountService.findBankAccount(receiverAccountId)).thenReturn(receiverBankAccount); - when(transactionStatusService.setAsOutstanding(transaction)).thenReturn(outstandingTransaction); - when(amountOfMoney.getAmount()) - .thenReturn(new BigDecimal("1000")) - .thenReturn(new BigDecimal("0")); - doThrow(new TransactionException("Credit transaction 1 should have positive value, actual value: -1000")) - .when(bankAccountService).creditAmountToAccounts(outstandingTransaction, emitterBankAccount, receiverBankAccount); - - // Act - try { - transactionProcessingService.credit(transaction); - } catch (TransactionException transactionException) { - // Assert - verify(bankAccountService).findBankAccount(emitterAccountId); - verify(bankAccountService).findBankAccount(receiverAccountId); - verify(transactionStatusService).setAsOutstanding(transaction); - verify(bankAccountService).creditAmountToAccounts(outstandingTransaction, emitterBankAccount, receiverBankAccount); - verifyNoMoreInteractions(bankAccountService, transactionStatusService); - } - } - - @Test - void credit_shouldReturnErroredTransaction_whenAddTransactionThrowsBankAccountException() { - // Arrange - long emitterAccountId = 99L; - long receiverAccountId = 101L; - Money amountOfMoney = Mockito.mock(Money.class); - Instant instantDate = Instant.now(); - Transaction oldTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - BankAccount emitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(oldTransaction)) - .build(); - BankAccount receiverBankAccount = CheckingBankAccount.builder() - .id(receiverAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .build(); - - Transaction transaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(UNPROCESSED) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - Transaction outstandingTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(OUTSTANDING) - .date(instantDate) - .metadata(new HashMap<>()) - .currency("EUR") - .label("transaction test") - .type(TransactionType.CREDIT) - .build(); - - Map metadataDuringProcess = Map.of("emitter_amount_before", "0", - "receiver_amount_before", "0", - "emitter_amount_after", "-1000", - "receiver_amount_after", "1000"); - Transaction completedTransaction = Transaction.builder() - .emitterAccountId(emitterAccountId) - .receiverAccountId(receiverAccountId) - .amount(new BigDecimal("1000")) - .status(COMPLETED) - .date(instantDate) - .metadata(new HashMap<>()) - .build(); - BankAccount updatedEmitterBankAccount = CheckingBankAccount.builder() - .id(emitterAccountId) - .type(CHECKING) - .balance(amountOfMoney) - .customersId(Set.of(99L)) - .issuedTransactions(Set.of(completedTransaction)) - .build(); - Map metadataAfterError = Map.of("error", "Amount of credit should not be negative"); - - when(bankAccountService.findBankAccount(emitterAccountId)).thenReturn(emitterBankAccount); - when(bankAccountService.findBankAccount(receiverAccountId)).thenReturn(receiverBankAccount); - when(transactionStatusService.setAsOutstanding(transaction)).thenReturn(outstandingTransaction); - when(amountOfMoney.getAmount()) - .thenReturn(new BigDecimal("0")) - .thenReturn(new BigDecimal("0")) - .thenReturn(new BigDecimal("-1000")) - .thenReturn(new BigDecimal("1000")); - when(transactionStatusService.saveStatus(outstandingTransaction, COMPLETED, metadataDuringProcess)).thenReturn(completedTransaction); - when(bankAccountService.putTransaction(completedTransaction, emitterBankAccount)).thenReturn(updatedEmitterBankAccount); - doThrow(new BankAccountException("Amount of credit should not be negative")).when(bankAccountService).updateBankAccount(updatedEmitterBankAccount); - - // Act - try { - transactionProcessingService.credit(transaction); - } catch (NoSuchElementException exception) { - // Assert - verify(bankAccountService).findBankAccount(emitterAccountId); - verify(bankAccountService).findBankAccount(receiverAccountId); - verify(transactionStatusService).setAsOutstanding(transaction); - verify(bankAccountService).creditAmountToAccounts(outstandingTransaction, emitterBankAccount, receiverBankAccount); - verify(bankAccountService).updateBankAccount(updatedEmitterBankAccount); - verify(transactionStatusService).setStatus(transaction, COMPLETED, metadataDuringProcess); - verifyNoMoreInteractions(bankAccountService, transactionStatusService); - } - } -} \ No newline at end of file diff --git a/application/src/test/java/com/cdx/bas/application/bank/transaction/type/WithdrawProcessorServiceImplTest.java b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/WithdrawProcessorServiceImplTest.java new file mode 100644 index 00000000..1a72c3ad --- /dev/null +++ b/application/src/test/java/com/cdx/bas/application/bank/transaction/type/WithdrawProcessorServiceImplTest.java @@ -0,0 +1,28 @@ +package com.cdx.bas.application.bank.transaction.type; + +import com.cdx.bas.application.bank.transaction.category.cash.type.withdraw.WithdrawProcessorImpl; +import com.cdx.bas.domain.bank.account.BankAccountServicePort; +import com.cdx.bas.domain.bank.transaction.TransactionServicePort; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatusServicePort; +import io.quarkus.test.common.WithTestResource; +import io.quarkus.test.h2.H2DatabaseTestResource; +import io.quarkus.test.junit.QuarkusTest; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +@QuarkusTest +@WithTestResource(H2DatabaseTestResource.class) +class WithdrawProcessorServiceImplTest { + + @Mock + private TransactionStatusServicePort transactionStatusService; + + @Mock + private TransactionServicePort transactionService; + + @Mock + private BankAccountServicePort bankAccountService; + + @InjectMocks + private WithdrawProcessorImpl withdrawProcessorService; +} \ No newline at end of file diff --git a/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java b/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java index eec93f8b..9077b5ea 100644 --- a/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java +++ b/application/src/test/java/com/cdx/bas/application/scheduler/SchedulerTest.java @@ -3,7 +3,8 @@ import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.bank.transaction.TransactionPersistencePort; import com.cdx.bas.domain.bank.transaction.TransactionServicePort; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import io.quarkus.test.InjectMock; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; @@ -22,7 +23,6 @@ import java.util.PriorityQueue; import java.util.Queue; -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; @@ -83,61 +83,60 @@ void processQueue_shouldProcessTransactionsInCorrectOrder_whenQueueIsFilled() { private static Queue getQueue() { Queue queue = new PriorityQueue<>(Comparator.comparing(Transaction::getDate)); - - queue.add(Transaction.builder() - .id(1L) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("250")) - .label("First transaction") - .type(TransactionType.CREDIT) - .status(UNPROCESSED) - .date(Instant.parse("2022-12-08T00:00:00Z")) // Fixed date for consistency - .build()); - - queue.add(Transaction.builder() - .id(2L) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("399")) - .label("Second transaction") - .type(TransactionType.CREDIT) - .status(UNPROCESSED) - .date(Instant.parse("2022-12-07T10:14:00Z")) // Fixed date - .build()); - - queue.add(Transaction.builder() - .id(3L) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("150")) - .label("Third transaction") - .type(TransactionType.CREDIT) - .status(UNPROCESSED) - .date(Instant.parse("2022-12-06T10:14:00Z")) // Fixed date - .build()); - - queue.add(Transaction.builder() - .id(4L) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("1000")) - .label("Fourth transaction") - .type(TransactionType.CREDIT) - .status(UNPROCESSED) - .date(Instant.parse("2022-12-07T10:18:00Z")) // Fixed date - .build()); - - queue.add(Transaction.builder() - .id(5L) - .emitterAccountId(99L) - .receiverAccountId(77L) - .amount(new BigDecimal("59")) - .label("Fifth transaction") - .type(TransactionType.CREDIT) - .status(UNPROCESSED) - .date(Instant.MIN) // Oldest date for top priority - .build()); + Transaction transaction1 = new Transaction(); + transaction1.setId(1L); + transaction1.setEmitterAccountId(99L); + transaction1.setReceiverAccountId(77L); + transaction1.setAmount(new BigDecimal("250")); + transaction1.setLabel("First transaction"); + transaction1.setType(TransactionType.CREDIT); + transaction1.setStatus(TransactionStatus.UNPROCESSED); + transaction1.setDate(Instant.parse("2022-12-08T00:00:00Z")); + queue.add(transaction1); + + Transaction transaction2 = new Transaction(); + transaction2.setId(2L); + transaction2.setEmitterAccountId(99L); + transaction2.setReceiverAccountId(77L); + transaction2.setAmount(new BigDecimal("399")); + transaction2.setLabel("Second transaction"); + transaction2.setType(TransactionType.CREDIT); + transaction2.setStatus(TransactionStatus.UNPROCESSED); + transaction2.setDate(Instant.parse("2022-12-07T10:14:00Z")); + queue.add(transaction2); + + Transaction transaction3 = new Transaction(); + transaction3.setId(3L); + transaction3.setEmitterAccountId(99L); + transaction3.setReceiverAccountId(77L); + transaction3.setAmount(new BigDecimal("150")); + transaction3.setLabel("Third transaction"); + transaction3.setType(TransactionType.CREDIT); + transaction3.setStatus(TransactionStatus.UNPROCESSED); + transaction3.setDate(Instant.parse("2022-12-06T10:14:00Z")); + queue.add(transaction3); + + Transaction transaction4 = new Transaction(); + transaction4.setId(4L); + transaction4.setEmitterAccountId(99L); + transaction4.setReceiverAccountId(77L); + transaction4.setAmount(new BigDecimal("1000")); + transaction4.setLabel("Fourth transaction"); + transaction4.setType(TransactionType.CREDIT); + transaction4.setStatus(TransactionStatus.UNPROCESSED); + transaction4.setDate(Instant.parse("2022-12-07T10:18:00Z")); + queue.add(transaction4); + + Transaction transaction5 = new Transaction(); + transaction5.setId(5L); + transaction5.setEmitterAccountId(99L); + transaction5.setReceiverAccountId(77L); + transaction5.setAmount(new BigDecimal("59")); + transaction5.setLabel("Fifth transaction"); + transaction5.setType(TransactionType.CREDIT); + transaction5.setStatus(TransactionStatus.UNPROCESSED); + transaction5.setDate(Instant.MIN); // Oldest date for top priority + queue.add(transaction5); return queue; } } diff --git a/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java b/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java index d54bc9af..bcf0a10c 100644 --- a/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java +++ b/client/src/main/java/com/cdx/bas/client/bank/transaction/TransactionResource.java @@ -1,5 +1,7 @@ package com.cdx.bas.client.bank.transaction; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewDigitalTransaction; import com.cdx.bas.domain.exception.DomainException; import com.cdx.bas.domain.bank.transaction.*; import com.cdx.bas.domain.message.MessageFormatter; diff --git a/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java b/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java index 27197e13..2e40f698 100644 --- a/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java +++ b/client/src/test/java/com/cdx/bas/client/bank/account/BankAccountResourceTest.java @@ -21,7 +21,7 @@ import java.util.List; import java.util.Set; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.CREDIT; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.CREDIT; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; diff --git a/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java b/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java index 17066833..3aa496ca 100644 --- a/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java +++ b/client/src/test/java/com/cdx/bas/client/bank/transaction/TransactionResourceTest.java @@ -1,6 +1,6 @@ package com.cdx.bas.client.bank.transaction; -import com.cdx.bas.domain.bank.transaction.NewCashTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; import com.cdx.bas.domain.bank.transaction.Transaction; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.h2.H2DatabaseTestResource; @@ -14,7 +14,7 @@ import java.util.*; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.*; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.*; import static org.assertj.core.api.Assertions.assertThat; @QuarkusTest diff --git a/domain/pom.xml b/domain/pom.xml index f53df0c3..2b2f23ff 100644 --- a/domain/pom.xml +++ b/domain/pom.xml @@ -47,11 +47,6 @@ io.quarkus quarkus-hibernate-validator - - org.projectlombok - lombok - provided - org.apache.commons commons-lang3 @@ -80,6 +75,14 @@ - + + org.apache.maven.plugins + maven-compiler-plugin + + 21 + 21 + + + diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java index 11785c45..bcfec242 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccount.java @@ -1,20 +1,17 @@ package com.cdx.bas.domain.bank.account; import com.cdx.bas.domain.bank.account.type.AccountType; -import com.cdx.bas.domain.money.Money; import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.money.Money; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import lombok.*; -import lombok.experimental.SuperBuilder; - -import java.util.*; -@Data -@SuperBuilder -@AllArgsConstructor -@NoArgsConstructor + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + public abstract class BankAccount { @NotNull(message="id must not be null.") @@ -38,4 +35,68 @@ public abstract class BankAccount { protected BankAccount(AccountType type) { this.type = type; } + + public BankAccount() { + } + + public BankAccount(Long id, AccountType type, Money balance, Set customersId, Set issuedTransactions) { + this.id = id; + this.type = type; + this.balance = balance; + this.customersId = customersId; + this.issuedTransactions = issuedTransactions; + } + + public @NotNull(message = "id must not be null.") @Min(value = 1, message = "id must be positive and greater than 0.") Long getId() { + return id; + } + + public void setId(@NotNull(message = "id must not be null.") @Min(value = 1, message = "id must be positive and greater than 0.") Long id) { + this.id = id; + } + + public @NotNull(message = "type must not be null.") AccountType getType() { + return type; + } + + public void setType(@NotNull(message = "type must not be null.") AccountType type) { + this.type = type; + } + + public @NotNull(message = "balance must not be null.") @Valid Money getBalance() { + return balance; + } + + public void setBalance(@NotNull(message = "balance must not be null.") @Valid Money balance) { + this.balance = balance; + } + + public @NotNull(message = "customersId must not be null.") @Size(min = 1, message = "customersId must contains at least 1 customer id.") Set getCustomersId() { + return customersId; + } + + public void setCustomersId(@NotNull(message = "customersId must not be null.") @Size(min = 1, message = "customersId must contains at least 1 customer id.") Set customersId) { + this.customersId = customersId; + } + + public @NotNull(message = "issued transactions must not be null.") Set getIssuedTransactions() { + return issuedTransactions; + } + + public void setIssuedTransactions(@NotNull(message = "issued transactions must not be null.") Set issuedTransactions) { + this.issuedTransactions = issuedTransactions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BankAccount that = (BankAccount) o; + return Objects.equals(id, that.id) && type == that.type && Objects.equals(balance, that.balance) && Objects.equals(customersId, that.customersId) && Objects.equals(issuedTransactions, that.issuedTransactions); + } + + @Override + public int hashCode() { + return Objects.hash(id, type, balance, customersId, issuedTransactions); + } } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccountServicePort.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccountServicePort.java index 6ae5c49f..85e1dde4 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccountServicePort.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/BankAccountServicePort.java @@ -30,15 +30,6 @@ public interface BankAccountServicePort { */ BankAccount putTransaction(Transaction transaction, BankAccount bankAccount); - /** - * add transaction to bank account - * - * @param transaction to post with amount and currency - * @param emitterBankAccount which emits transaction - * @param receiverBankAccount which receives transaction - */ - void creditAmountToAccounts(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount) ; - /** * updated bank account * @@ -46,8 +37,4 @@ public interface BankAccountServicePort { * @return bank account updated */ BankAccount updateBankAccount(BankAccount bankAccount); - - void depositAmountToAccount(Transaction transaction, BankAccount emitterBankAccount); - - void withdrawAmountToAccount(Transaction transaction, BankAccount emitterBankAccount); } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java index 8fd1a02d..57fa7225 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/checking/CheckingBankAccount.java @@ -1,37 +1,32 @@ package com.cdx.bas.domain.bank.account.checking; import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.money.Amount; import com.cdx.bas.domain.money.Money; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.SuperBuilder; - -import java.util.List; +import com.cdx.bas.domain.testing.Generated; + import java.util.Set; -import static com.cdx.bas.domain.bank.account.type.AccountType.*; +import static com.cdx.bas.domain.bank.account.type.AccountType.CHECKING; /** * Checking account (transaction account/current account) */ -@SuperBuilder public class CheckingBankAccount extends BankAccount { + @Override @Amount(min=-600, max=100000, message="balance amount must be between -600 and 100000.") public Money getBalance() { return super.balance; } - + + @Generated public CheckingBankAccount() { super(CHECKING); } + @Generated public CheckingBankAccount(Long id, Money balance, Set customersId, Set issuedTransactions) { super(id, CHECKING, balance, customersId, issuedTransactions); } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java index a775a89b..ceea7223 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/mma/MMABankAccount.java @@ -1,37 +1,32 @@ package com.cdx.bas.domain.bank.account.mma; import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.transaction.Transaction; import com.cdx.bas.domain.money.Amount; import com.cdx.bas.domain.money.Money; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.SuperBuilder; +import com.cdx.bas.domain.testing.Generated; -import java.util.List; import java.util.Set; -import static com.cdx.bas.domain.bank.account.type.AccountType.*; +import static com.cdx.bas.domain.bank.account.type.AccountType.MMA; /** * Money Market Account */ -@SuperBuilder public class MMABankAccount extends BankAccount { + @Override @Amount(min=1000, max=250000, message="balance amount must be between 1000 and 250000.") public Money getBalance() { return super.balance; } - + + @Generated public MMABankAccount() { super(MMA); } + @Generated public MMABankAccount(Long id, Money balance, Set customersId, Set issuedTransactions) { super(id, MMA, balance, customersId, issuedTransactions); } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java b/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java index b1021073..14dfa40b 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/account/saving/SavingBankAccount.java @@ -1,18 +1,11 @@ package com.cdx.bas.domain.bank.account.saving; import com.cdx.bas.domain.bank.account.BankAccount; -import com.cdx.bas.domain.bank.account.type.AccountType; import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; import com.cdx.bas.domain.money.Amount; import com.cdx.bas.domain.money.Money; -import jakarta.validation.constraints.NotNull; -import lombok.*; -import lombok.experimental.SuperBuilder; +import com.cdx.bas.domain.testing.Generated; -import java.math.BigDecimal; -import java.util.List; import java.util.Set; import static com.cdx.bas.domain.bank.account.type.AccountType.*; @@ -20,18 +13,20 @@ /** * Saving Account (French Livret A) */ -@SuperBuilder public class SavingBankAccount extends BankAccount { + @Override @Amount(min=1, max=22950, message="balance amount must be between 1 and 22950.") - public Money getBalance() { + public Money getBalance() { return super.balance; } - + + @Generated public SavingBankAccount() { super(SAVING); } + @Generated public SavingBankAccount(Long id, Money balance, Set customersId, Set issuedTransactions) { super(id, SAVING, balance, customersId, issuedTransactions); } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/customer/Customer.java b/domain/src/main/java/com/cdx/bas/domain/bank/customer/Customer.java index 7ac550e0..46dc876f 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/customer/Customer.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/customer/Customer.java @@ -62,15 +62,11 @@ public class Customer { private List accounts = new ArrayList<>(); private Map metadata = new HashMap<>(); - + public Customer() { - super(); } - public Customer(Long id, String firstName, String lastName, Gender gender, MaritalStatus maritalStatus, - LocalDate birthdate, String country, String address, String city, String email, String phoneNumber, - List accounts, Map metadata) { - super(); + public Customer(Long id, String firstName, String lastName, Gender gender, MaritalStatus maritalStatus, LocalDate birthdate, String country, String address, String city, String email, String phoneNumber, List accounts, Map metadata) { this.id = id; this.firstName = firstName; this.lastName = lastName; @@ -86,102 +82,102 @@ public Customer(Long id, String firstName, String lastName, Gender gender, Marit this.metadata = metadata; } - public Long getId() { + public @NotNull(message = "id must not be null.") @Min(value = 1, message = "id must be positive and greater than 0.") Long getId() { return id; } - public void setId(Long id) { + public void setId(@NotNull(message = "id must not be null.") @Min(value = 1, message = "id must be positive and greater than 0.") Long id) { this.id = id; } - public String getFirstName() { + public @NotNull(message = "firstName must not be null.") @Size(min = 1, max = 750, message = "firstName must contain at least 1 character and must not have more than 750 characters.") String getFirstName() { return firstName; } - - public void setFirstName(String firstName) { + + public void setFirstName(@NotNull(message = "firstName must not be null.") @Size(min = 1, max = 750, message = "firstName must contain at least 1 character and must not have more than 750 characters.") String firstName) { this.firstName = firstName; } - - public String getLastName() { + + public @NotNull(message = "lastName must not be null.") @Size(min = 1, max = 750, message = "lastName must contain at least 1 character and must not have more than 750 characters.") String getLastName() { return lastName; } - - public void setLastName(String lastName) { + + public void setLastName(@NotNull(message = "lastName must not be null.") @Size(min = 1, max = 750, message = "lastName must contain at least 1 character and must not have more than 750 characters.") String lastName) { this.lastName = lastName; } - - public Gender getGender() { + + public @NotNull(message = "gender must not be null.") Gender getGender() { return gender; } - - public void setGender(Gender gender) { + + public void setGender(@NotNull(message = "gender must not be null.") Gender gender) { this.gender = gender; } - - public MaritalStatus getMaritalStatus() { + + public @NotNull(message = "maritalStatus must not be null.") MaritalStatus getMaritalStatus() { return maritalStatus; } - - public void setMaritalStatus(MaritalStatus maritalStatus) { + + public void setMaritalStatus(@NotNull(message = "maritalStatus must not be null.") MaritalStatus maritalStatus) { this.maritalStatus = maritalStatus; } - public LocalDate getBirthdate() { + public @NotNull(message = "birthdate must not be null.") @Past(message = "birthdate must not be before the current date.") LocalDate getBirthdate() { return birthdate; } - public void setBirthdate(LocalDate birthdate) { + public void setBirthdate(@NotNull(message = "birthdate must not be null.") @Past(message = "birthdate must not be before the current date.") LocalDate birthdate) { this.birthdate = birthdate; } - public String getCountry() { + public @NotNull(message = "country must not be null.") @Pattern(regexp = ISO_COUNTRY_REGEX, message = "country must contain ISO 3166 country code.") String getCountry() { return country; } - - public void setCountry(String country) { + + public void setCountry(@NotNull(message = "country must not be null.") @Pattern(regexp = ISO_COUNTRY_REGEX, message = "country must contain ISO 3166 country code.") String country) { this.country = country; } - - public String getAddress() { + + public @NotNull(message = "address must not be null.") @Size(min = 1, message = "address must contain at least 1 character.") String getAddress() { return address; } - - public void setAddress(String address) { + + public void setAddress(@NotNull(message = "address must not be null.") @Size(min = 1, message = "address must contain at least 1 character.") String address) { this.address = address; } - - public String getCity() { + + public @NotNull(message = "city must not be null.") @Size(min = 1, message = "city must contain at least 1 character.") String getCity() { return city; } - - public void setCity(String city) { + + public void setCity(@NotNull(message = "city must not be null.") @Size(min = 1, message = "city must contain at least 1 character.") String city) { this.city = city; } - - public String getEmail() { + + public @NotNull(message = "email must not be null.") @Email(message = "email must respect the email format.") @Size(min = 1, message = "address must contain at least 1 character.") String getEmail() { return email; } - - public void setEmail(String email) { + + public void setEmail(@NotNull(message = "email must not be null.") @Email(message = "email must respect the email format.") @Size(min = 1, message = "address must contain at least 1 character.") String email) { this.email = email; } - - public String getPhoneNumber() { + + public @NotNull(message = "phoneNumber must not be null.") @Size(min = 5, max = 20, message = "phoneNumber must contain at least 5 digits and maximum 20 digits.") String getPhoneNumber() { return phoneNumber; } - - public void setPhoneNumber(String phoneNumber) { + + public void setPhoneNumber(@NotNull(message = "phoneNumber must not be null.") @Size(min = 5, max = 20, message = "phoneNumber must contain at least 5 digits and maximum 20 digits.") String phoneNumber) { this.phoneNumber = phoneNumber; } - - public List getAccounts() { + + public @NotNull(message = "accounts must not be null.") List getAccounts() { return accounts; } - - public void setAccounts(List accounts) { + + public void setAccounts(@NotNull(message = "accounts must not be null.") List accounts) { this.accounts = accounts; } - + public Map getMetadata() { return metadata; } @@ -190,29 +186,16 @@ public void setMetadata(Map metadata) { this.metadata = metadata; } - @Override - public int hashCode() { - return Objects.hash(id, firstName, lastName, gender, maritalStatus, - birthdate, country, address, city, email, phoneNumber, accounts, metadata); - } - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Customer customer = (Customer) o; - return Objects.equals(id, customer.id) && - Objects.equals(firstName, customer.firstName) && - Objects.equals(lastName, customer.lastName) && - gender == customer.gender && - maritalStatus == customer.maritalStatus && - Objects.equals(birthdate, customer.birthdate) && - Objects.equals(country, customer.country) && - Objects.equals(address, customer.address) && - Objects.equals(city, customer.city) && - Objects.equals(email, customer.email) && - Objects.equals(phoneNumber, customer.phoneNumber) && - Objects.equals(accounts, customer.accounts) && - Objects.equals(metadata, customer.metadata); + return Objects.equals(id, customer.id) && Objects.equals(firstName, customer.firstName) && Objects.equals(lastName, customer.lastName) && gender == customer.gender && maritalStatus == customer.maritalStatus && Objects.equals(birthdate, customer.birthdate) && Objects.equals(country, customer.country) && Objects.equals(address, customer.address) && Objects.equals(city, customer.city) && Objects.equals(email, customer.email) && Objects.equals(phoneNumber, customer.phoneNumber) && Objects.equals(accounts, customer.accounts) && Objects.equals(metadata, customer.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(id, firstName, lastName, gender, maritalStatus, birthdate, country, address, city, email, phoneNumber, accounts, metadata); } } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java index 953e71f1..a3a694c3 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/Transaction.java @@ -1,16 +1,12 @@ package com.cdx.bas.domain.bank.transaction; -import com.cdx.bas.domain.bank.transaction.validation.validator.ValidStatus; -import com.cdx.bas.domain.bank.transaction.validation.validator.ValidType; -import com.cdx.bas.domain.bank.transaction.validation.group.*; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.ValidType; +import com.cdx.bas.domain.bank.transaction.category.group.*; import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.ValidStatus; import com.cdx.bas.domain.currency.validation.ValidCurrency; import jakarta.validation.constraints.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.time.Instant; @@ -18,13 +14,9 @@ import java.util.Map; import java.util.Objects; +import static com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType.*; import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor public class Transaction implements Comparable { @Min(value = 1, message = "Id must be positive and greater than 0 for existing transaction.", groups = ExistingTransactionGroup.class) @@ -68,32 +60,127 @@ public class Transaction implements Comparable { @NotEmpty(message = "Bill must be define for cash movements.", groups = PhysicalCashTransactionGroup.class) @NotNull(message = "Metadata must not be null.") - private Map metadata = new HashMap<>(); + private Map metadata; @Override public int compareTo(Transaction transactionToCompare) { return this.getDate().compareTo(transactionToCompare.getDate()); } + public Transaction() { + } + + public Transaction(Long id, Long emitterAccountId, Long receiverAccountId, BigDecimal amount, String currency, TransactionType type, TransactionStatus status, Instant date, String label, Map metadata) { + this.id = id; + this.emitterAccountId = emitterAccountId; + this.receiverAccountId = receiverAccountId; + this.amount = amount; + this.currency = currency; + this.type = type; + this.status = status; + this.date = date; + this.label = label; + this.metadata = metadata; + } + + public @Min(value = 1, message = "Id must be positive and greater than 0 for existing transaction.", groups = ExistingTransactionGroup.class) @NotNull(message = "Id must not be null for existing transaction.", groups = ExistingTransactionGroup.class) @Null(message = "Id must be null for new transaction.", groups = NewTransactionGroup.class) @Positive(message = "Id must be positive.", groups = AdvancedGroup.class) Long getId() { + return id; + } + + public void setId(@Min(value = 1, message = "Id must be positive and greater than 0 for existing transaction.", groups = ExistingTransactionGroup.class) @NotNull(message = "Id must not be null for existing transaction.", groups = ExistingTransactionGroup.class) @Null(message = "Id must be null for new transaction.", groups = NewTransactionGroup.class) @Positive(message = "Id must be positive.", groups = AdvancedGroup.class) Long id) { + this.id = id; + } + + public @Positive(message = "Emitter account id must be positive.", groups = AdvancedGroup.class) @NotNull(message = "Emitter account id must not be null.") Long getEmitterAccountId() { + return emitterAccountId; + } + + public void setEmitterAccountId(@Positive(message = "Emitter account id must be positive.", groups = AdvancedGroup.class) @NotNull(message = "Emitter account id must not be null.") Long emitterAccountId) { + this.emitterAccountId = emitterAccountId; + } + + public @Null(message = "Receiver account id must be null for cash movement.", groups = PhysicalCashTransactionGroup.class) @Positive(message = "Receiver account id must be positive.", groups = AdvancedGroup.class) @NotNull(message = "Receiver account id must not be null.", groups = DigitalTransactionGroup.class) Long getReceiverAccountId() { + return receiverAccountId; + } + + public void setReceiverAccountId(@Null(message = "Receiver account id must be null for cash movement.", groups = PhysicalCashTransactionGroup.class) @Positive(message = "Receiver account id must be positive.", groups = AdvancedGroup.class) @NotNull(message = "Receiver account id must not be null.", groups = DigitalTransactionGroup.class) Long receiverAccountId) { + this.receiverAccountId = receiverAccountId; + } + + public @Min(value = 10, message = "Amount must be greater than 10 for cash movement.", groups = PhysicalCashTransactionGroup.class) @Min(value = 1, message = "Amount must be positive and greater than 0.", groups = DigitalTransactionGroup.class) @NotNull(message = "Amount must not be null.") BigDecimal getAmount() { + return amount; + } + + public void setAmount(@Min(value = 10, message = "Amount must be greater than 10 for cash movement.", groups = PhysicalCashTransactionGroup.class) @Min(value = 1, message = "Amount must be positive and greater than 0.", groups = DigitalTransactionGroup.class) @NotNull(message = "Amount must not be null.") BigDecimal amount) { + this.amount = amount; + } + + public @NotNull(message = "Currency must not be null.") String getCurrency() { + return currency; + } + + public void setCurrency(@NotNull(message = "Currency must not be null.") String currency) { + this.currency = currency; + } + + public @NotNull(message = "Type must not be null.") TransactionType getType() { + return type; + } + + public void setType(@NotNull(message = "Type must not be null.") TransactionType type) { + this.type = type; + } + + public @NotNull(message = "Status must not be null.") TransactionStatus getStatus() { + return status; + } + + public void setStatus(@NotNull(message = "Status must not be null.") TransactionStatus status) { + this.status = status; + } + + public @NotNull(message = "Date must not be null.") Instant getDate() { + return date; + } + + public void setDate(@NotNull(message = "Date must not be null.") Instant date) { + this.date = date; + } + + public @NotNull(message = "Label must not be null.") String getLabel() { + return label; + } + + public void setLabel(@NotNull(message = "Label must not be null.") String label) { + this.label = label; + } + + public @NotEmpty(message = "Bill must be define for cash movements.", groups = PhysicalCashTransactionGroup.class) @NotNull(message = "Metadata must not be null.") Map getMetadata() { + return metadata; + } + + public void setMetadata(@NotEmpty(message = "Bill must be define for cash movements.", groups = PhysicalCashTransactionGroup.class) @NotNull(message = "Metadata must not be null.") Map metadata) { + this.metadata = metadata; + } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Transaction that = (Transaction) o; - return Objects.equals(id, that.id) && - Objects.equals(emitterAccountId, that.emitterAccountId) && - Objects.equals(receiverAccountId, that.receiverAccountId) && - Objects.equals(amount, that.amount) && - Objects.equals(currency, that.currency) && - Objects.equals(type, that.type) && - Objects.equals(date, that.date) && - Objects.equals(label, that.label); + return Objects.equals(id, that.id) + && Objects.equals(emitterAccountId, that.emitterAccountId) + && Objects.equals(receiverAccountId, that.receiverAccountId) + && Objects.equals(amount, that.amount) + && Objects.equals(currency, that.currency) + && type == that.type && status == that.status + && Objects.equals(date, that.date) + && Objects.equals(label, that.label) + && Objects.equals(metadata, that.metadata); } - // Redéfinir hashCode() en excluant status et metadata @Override public int hashCode() { - return Objects.hash(id, emitterAccountId, receiverAccountId, amount, currency, type, date, label); + return Objects.hash(id, emitterAccountId, receiverAccountId, amount, currency, type, status, date, label, metadata); } } diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java index 461324fa..7d28044b 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionControllerPort.java @@ -1,14 +1,10 @@ package com.cdx.bas.domain.bank.transaction; -import jakarta.transaction.Transactional; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.POST; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewDigitalTransaction; import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import java.math.BigDecimal; import java.util.Set; public interface TransactionControllerPort { diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java index d455f9e3..10757422 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionServicePort.java @@ -1,8 +1,9 @@ package com.cdx.bas.domain.bank.transaction; -import jakarta.transaction.Transactional; +import com.cdx.bas.domain.bank.transaction.category.NewCashTransaction; +import com.cdx.bas.domain.bank.transaction.category.NewDigitalTransaction; -import java.util.Map; +import java.util.Optional; import java.util.Set; public interface TransactionServicePort { @@ -18,17 +19,15 @@ public interface TransactionServicePort { * Create new transaction * * @param transaction to create - * @param metadata with detail about the transaction */ - void create(Transaction transaction, Map metadata); + void create(Transaction transaction); /** * Update existing transaction * * @param transaction to update - * @param metadata with detail about the transaction */ - void update(Transaction transaction, Map metadata); + void update(Transaction transaction); /** @@ -52,11 +51,11 @@ public interface TransactionServicePort { * @param transactionId * @return Transaction found */ - Transaction findTransaction(Long transactionId); + Optional findTransaction(Long transactionId); /** * Process digital transaction - * + * * @param digitalTransaction to process */ void processDigitalTransaction(Transaction digitalTransaction); diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/TransactionValidator.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionValidator.java similarity index 87% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/TransactionValidator.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionValidator.java index 3b99ae68..7e60ec7e 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/TransactionValidator.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/TransactionValidator.java @@ -1,17 +1,12 @@ -package com.cdx.bas.domain.bank.transaction.validation.validator; +package com.cdx.bas.domain.bank.transaction; -import com.cdx.bas.domain.bank.transaction.Transaction; -import com.cdx.bas.domain.bank.transaction.TransactionException; -import com.cdx.bas.domain.bank.transaction.validation.group.*; +import com.cdx.bas.domain.bank.transaction.category.group.*; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; -import jakarta.validation.ConstraintViolation; import jakarta.validation.Validator; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; @RequestScoped diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewCashTransaction.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/NewCashTransaction.java similarity index 71% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewCashTransaction.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/NewCashTransaction.java index 5ba39993..7dc6e55d 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewCashTransaction.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/NewCashTransaction.java @@ -1,6 +1,4 @@ -package com.cdx.bas.domain.bank.transaction; - -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +package com.cdx.bas.domain.bank.transaction.category; import java.math.BigDecimal; import java.util.Map; diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewDigitalTransaction.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/NewDigitalTransaction.java similarity index 72% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewDigitalTransaction.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/NewDigitalTransaction.java index 8773a06f..ba584460 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/NewDigitalTransaction.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/NewDigitalTransaction.java @@ -1,6 +1,6 @@ -package com.cdx.bas.domain.bank.transaction; +package com.cdx.bas.domain.bank.transaction.category; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; import java.math.BigDecimal; import java.util.Map; diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashAmountServicePort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashAmountServicePort.java new file mode 100644 index 00000000..8aff308b --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashAmountServicePort.java @@ -0,0 +1,5 @@ +package com.cdx.bas.domain.bank.transaction.category.cash; + +public interface CashAmountServicePort { + void applyToAccount (CashTransactionProcessingDetails cashTransactionProcessingDetails); +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashTransactionProcessingDetails.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashTransactionProcessingDetails.java new file mode 100644 index 00000000..7823e860 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/CashTransactionProcessingDetails.java @@ -0,0 +1,43 @@ +package com.cdx.bas.domain.bank.transaction.category.cash; + +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.transaction.Transaction; + +import java.util.Map; + + +public class CashTransactionProcessingDetails { + Transaction transaction; + BankAccount emitterBankAccount; + Map metadata; + + public CashTransactionProcessingDetails(Transaction transaction, BankAccount emitterBankAccount, Map metadata) { + this.transaction = transaction; + this.emitterBankAccount = emitterBankAccount; + this.metadata = metadata; + } + + public Transaction getTransaction() { + return transaction; + } + + public void setTransaction(Transaction transaction) { + this.transaction = transaction; + } + + public BankAccount getEmitterBankAccount() { + return emitterBankAccount; + } + + public void setEmitterBankAccount(BankAccount emitterBankAccount) { + this.emitterBankAccount = emitterBankAccount; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImpl.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImpl.java new file mode 100644 index 00000000..edf91c61 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImpl.java @@ -0,0 +1,33 @@ +package com.cdx.bas.domain.bank.transaction.category.cash.type.deposit; + +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.cash.CashAmountServicePort; +import com.cdx.bas.domain.bank.transaction.category.cash.CashTransactionProcessingDetails; +import com.cdx.bas.domain.currency.rate.ExchangeRateUtils; +import com.cdx.bas.domain.message.MessageFormatter; +import com.cdx.bas.domain.money.Money; +import jakarta.enterprise.context.ApplicationScoped; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +import static com.cdx.bas.domain.message.CommonMessages.*; +import static com.cdx.bas.domain.money.AmountUtils.isNotPositive; + + +@ApplicationScoped +public class DepositAmountServiceImpl implements CashAmountServicePort { + + @Override + public void applyToAccount(CashTransactionProcessingDetails cashTransactionProcessingDetails) { + BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(cashTransactionProcessingDetails.getTransaction().getCurrency(), + cashTransactionProcessingDetails.getTransaction().getAmount()); + if (isNotPositive(euroAmount)) { + throw new TransactionException(MessageFormatter.format(DEPOSIT_TRANSACTION_CONTEXT, DEPOSIT_ACTION, FAILED_STATUS, + Optional.of(SHOULD_HAVE_POSITIVE_VALUE_CAUSE), + List.of(TRANSACTION_ID_DETAIL + cashTransactionProcessingDetails.getTransaction().getId(), EURO_AMOUNT_DETAIL + euroAmount))); + } + cashTransactionProcessingDetails.getEmitterBankAccount().getBalance().plus(Money.of(euroAmount)); + } +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImpl.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImpl.java new file mode 100644 index 00000000..7ba3acb3 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImpl.java @@ -0,0 +1,31 @@ +package com.cdx.bas.domain.bank.transaction.category.cash.type.withdraw; + +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.cash.CashAmountServicePort; +import com.cdx.bas.domain.bank.transaction.category.cash.CashTransactionProcessingDetails; +import com.cdx.bas.domain.currency.rate.ExchangeRateUtils; +import com.cdx.bas.domain.money.Money; +import jakarta.enterprise.context.ApplicationScoped; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +import static com.cdx.bas.domain.message.CommonMessages.*; +import static com.cdx.bas.domain.message.MessageFormatter.format; +import static com.cdx.bas.domain.money.AmountUtils.isNotPositive; + +@ApplicationScoped +public class WithdrawAmountServiceImpl implements CashAmountServicePort { + @Override + public void applyToAccount(CashTransactionProcessingDetails cashTransactionProcessingDetails) { + BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(cashTransactionProcessingDetails.getTransaction().getCurrency(), + cashTransactionProcessingDetails.getTransaction().getAmount()); + if (isNotPositive(euroAmount)) { + throw new TransactionException(format(WITHDRAW_TRANSACTION_CONTEXT, WITHDRAW_ACTION, FAILED_STATUS, + Optional.of(SHOULD_HAVE_POSITIVE_VALUE_CAUSE), + List.of(TRANSACTION_ID_DETAIL + cashTransactionProcessingDetails.getTransaction().getId(), EURO_AMOUNT_DETAIL + euroAmount))); + } + cashTransactionProcessingDetails.getEmitterBankAccount().getBalance().minus(Money.of(euroAmount)); + } +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalAmountServicePort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalAmountServicePort.java new file mode 100644 index 00000000..99045304 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalAmountServicePort.java @@ -0,0 +1,5 @@ +package com.cdx.bas.domain.bank.transaction.category.digital; + +public interface DigitalAmountServicePort { + void transferBetweenAccounts(DigitalTransactionProcessingDetails digitalTransactionProcessingDetails); +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalTransactionProcessingDetails.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalTransactionProcessingDetails.java new file mode 100644 index 00000000..6fedf7db --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/DigitalTransactionProcessingDetails.java @@ -0,0 +1,53 @@ +package com.cdx.bas.domain.bank.transaction.category.digital; + +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.transaction.Transaction; + +import java.util.Map; + + +public class DigitalTransactionProcessingDetails { + Transaction transaction; + BankAccount emitterBankAccount; + BankAccount receiverBankAccount; + Map metadata; + + public DigitalTransactionProcessingDetails(Transaction transaction, BankAccount emitterBankAccount, BankAccount receiverBankAccount, Map metadata) { + this.transaction = transaction; + this.emitterBankAccount = emitterBankAccount; + this.receiverBankAccount = receiverBankAccount; + this.metadata = metadata; + } + + public Transaction getTransaction() { + return transaction; + } + + public void setTransaction(Transaction transaction) { + this.transaction = transaction; + } + + public BankAccount getEmitterBankAccount() { + return emitterBankAccount; + } + + public void setEmitterBankAccount(BankAccount emitterBankAccount) { + this.emitterBankAccount = emitterBankAccount; + } + + public BankAccount getReceiverBankAccount() { + return receiverBankAccount; + } + + public void setReceiverBankAccount(BankAccount receiverBankAccount) { + this.receiverBankAccount = receiverBankAccount; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionType.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/TransactionType.java similarity index 51% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionType.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/TransactionType.java index 61236bd8..20fe1f7c 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionType.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/TransactionType.java @@ -1,4 +1,4 @@ -package com.cdx.bas.domain.bank.transaction.type; +package com.cdx.bas.domain.bank.transaction.category.digital.type; public enum TransactionType { CREDIT, DEBIT, DEPOSIT, WITHDRAW diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/TypeValidator.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/TypeValidator.java similarity index 89% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/TypeValidator.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/TypeValidator.java index 9932f141..06a99b94 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/TypeValidator.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/TypeValidator.java @@ -1,6 +1,5 @@ -package com.cdx.bas.domain.bank.transaction.validation.validator; +package com.cdx.bas.domain.bank.transaction.category.digital.type; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/ValidType.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/ValidType.java similarity index 82% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/ValidType.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/ValidType.java index 6b27fe14..ceb0e49e 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/ValidType.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/ValidType.java @@ -1,6 +1,5 @@ -package com.cdx.bas.domain.bank.transaction.validation.validator; +package com.cdx.bas.domain.bank.transaction.category.digital.type; -import com.cdx.bas.domain.bank.transaction.type.TransactionType; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImpl.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImpl.java new file mode 100644 index 00000000..65f8c882 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImpl.java @@ -0,0 +1,33 @@ +package com.cdx.bas.domain.bank.transaction.category.digital.type.credit; + +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalAmountServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalTransactionProcessingDetails; +import com.cdx.bas.domain.currency.rate.ExchangeRateUtils; +import com.cdx.bas.domain.money.Money; +import jakarta.enterprise.context.ApplicationScoped; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +import static com.cdx.bas.domain.message.CommonMessages.*; +import static com.cdx.bas.domain.message.CommonMessages.EURO_AMOUNT_DETAIL; +import static com.cdx.bas.domain.message.MessageFormatter.format; +import static com.cdx.bas.domain.money.AmountUtils.isNotPositive; + +@ApplicationScoped +public class CreditAmountServiceImpl implements DigitalAmountServicePort { + @Override + public void transferBetweenAccounts(DigitalTransactionProcessingDetails digitalTransactionProcessingDetails) { + BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(digitalTransactionProcessingDetails.getTransaction().getCurrency(), + digitalTransactionProcessingDetails.getTransaction().getAmount()); + if (isNotPositive(euroAmount)) { + throw new TransactionException(format(CREDIT_TRANSACTION_CONTEXT, CREDIT_ACTION, FAILED_STATUS, + Optional.of(SHOULD_HAVE_POSITIVE_VALUE_CAUSE), + List.of(TRANSACTION_ID_DETAIL + digitalTransactionProcessingDetails.getTransaction().getId(), EURO_AMOUNT_DETAIL + euroAmount))); + } + digitalTransactionProcessingDetails.getEmitterBankAccount().getBalance().minus(Money.of(euroAmount)); + digitalTransactionProcessingDetails.getReceiverBankAccount().getBalance().plus(Money.of(euroAmount)); + } +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImpl.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImpl.java new file mode 100644 index 00000000..b908e850 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImpl.java @@ -0,0 +1,33 @@ +package com.cdx.bas.domain.bank.transaction.category.digital.type.debit; + +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalAmountServicePort; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalTransactionProcessingDetails; +import com.cdx.bas.domain.currency.rate.ExchangeRateUtils; +import com.cdx.bas.domain.money.Money; +import jakarta.enterprise.context.ApplicationScoped; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Optional; + +import static com.cdx.bas.domain.message.CommonMessages.*; +import static com.cdx.bas.domain.message.MessageFormatter.format; +import static com.cdx.bas.domain.money.AmountUtils.isNotPositive; + +@ApplicationScoped +public class DebitAmountServiceImpl implements DigitalAmountServicePort { + + @Override + public void transferBetweenAccounts(DigitalTransactionProcessingDetails digitalTransactionProcessingDetails) { + BigDecimal euroAmount = ExchangeRateUtils.getEuroAmountFrom(digitalTransactionProcessingDetails.getTransaction().getCurrency(), + digitalTransactionProcessingDetails.getTransaction().getAmount()); + if (isNotPositive(euroAmount)) { + throw new TransactionException(format(DEBIT_TRANSACTION_CONTEXT, DEBIT_ACTION, FAILED_STATUS, + Optional.of(SHOULD_HAVE_POSITIVE_VALUE_CAUSE), + List.of(TRANSACTION_ID_DETAIL + digitalTransactionProcessingDetails.getTransaction().getId(), EURO_AMOUNT_DETAIL + euroAmount))); + } + digitalTransactionProcessingDetails.getReceiverBankAccount().getBalance().minus(Money.of(euroAmount)); + digitalTransactionProcessingDetails.getEmitterBankAccount().getBalance().plus(Money.of(euroAmount)); + } +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/AdvancedGroup.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/AdvancedGroup.java new file mode 100644 index 00000000..f265f119 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/AdvancedGroup.java @@ -0,0 +1,4 @@ +package com.cdx.bas.domain.bank.transaction.category.group; + +public interface AdvancedGroup { +} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/DigitalTransactionGroup.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/DigitalTransactionGroup.java similarity index 65% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/DigitalTransactionGroup.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/DigitalTransactionGroup.java index 411a0770..4cfd716e 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/DigitalTransactionGroup.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/DigitalTransactionGroup.java @@ -1,4 +1,4 @@ -package com.cdx.bas.domain.bank.transaction.validation.group; +package com.cdx.bas.domain.bank.transaction.category.group; /** * Transaction group that move money from an account to another diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/ExistingTransactionGroup.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/ExistingTransactionGroup.java similarity index 62% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/ExistingTransactionGroup.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/ExistingTransactionGroup.java index 98a2cd6d..6167f8fb 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/ExistingTransactionGroup.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/ExistingTransactionGroup.java @@ -1,4 +1,4 @@ -package com.cdx.bas.domain.bank.transaction.validation.group; +package com.cdx.bas.domain.bank.transaction.category.group; /** * Transaction group for existing transactions diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/NewTransactionGroup.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/NewTransactionGroup.java similarity index 59% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/NewTransactionGroup.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/NewTransactionGroup.java index 066faf32..ccca4ff2 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/NewTransactionGroup.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/NewTransactionGroup.java @@ -1,4 +1,4 @@ -package com.cdx.bas.domain.bank.transaction.validation.group; +package com.cdx.bas.domain.bank.transaction.category.group; /** * Transaction group for new transactions diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/PhysicalCashTransactionGroup.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/PhysicalCashTransactionGroup.java similarity index 67% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/PhysicalCashTransactionGroup.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/PhysicalCashTransactionGroup.java index 22c72cea..c2996f1e 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/PhysicalCashTransactionGroup.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/category/group/PhysicalCashTransactionGroup.java @@ -1,4 +1,4 @@ -package com.cdx.bas.domain.bank.transaction.validation.group; +package com.cdx.bas.domain.bank.transaction.category.group; /** * Transaction group that use cash money to transfer to an account diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/StatusValidator.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/StatusValidator.java similarity index 87% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/StatusValidator.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/StatusValidator.java index b2d82550..f4bf806d 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/StatusValidator.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/StatusValidator.java @@ -1,6 +1,5 @@ -package com.cdx.bas.domain.bank.transaction.validation.validator; +package com.cdx.bas.domain.bank.transaction.status; -import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/TransactionStatusServicePort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/TransactionStatusServicePort.java index 8ce9aefc..5612b4cd 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/TransactionStatusServicePort.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/TransactionStatusServicePort.java @@ -5,6 +5,7 @@ import java.util.Map; public interface TransactionStatusServicePort { + /** * Set transaction to outstanding with additional metadata and avoid multiple process * diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/ValidStatus.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/ValidStatus.java similarity index 69% rename from domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/ValidStatus.java rename to domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/ValidStatus.java index 0885fb0d..41e0798d 100644 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/validator/ValidStatus.java +++ b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/status/ValidStatus.java @@ -1,7 +1,5 @@ -package com.cdx.bas.domain.bank.transaction.validation.validator; +package com.cdx.bas.domain.bank.transaction.status; -import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; -import com.cdx.bas.domain.bank.transaction.validation.validator.StatusValidator; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionProcessorServicePort.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionProcessorServicePort.java deleted file mode 100644 index 6341bc9b..00000000 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/type/TransactionProcessorServicePort.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.cdx.bas.domain.bank.transaction.type; - -import com.cdx.bas.domain.bank.transaction.Transaction; - -public interface TransactionProcessorServicePort { - - /** - * Credit bank account with transaction according to its amount - * - * @param transaction to process - */ - Transaction credit(Transaction transaction); - - /** - * Debit bank account with transaction according to its amount - * - * @param transaction to process - */ - Transaction debit(Transaction transaction); - - /** - * Deposit amount to a corresponding bank account - * - * @param transaction to process - */ - Transaction deposit(Transaction transaction); - - /** - * Withdraw amount to a corresponding bank account - * - * @param transaction to process - */ - Transaction withdraw(Transaction transaction); -} diff --git a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/AdvancedGroup.java b/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/AdvancedGroup.java deleted file mode 100644 index b6f62360..00000000 --- a/domain/src/main/java/com/cdx/bas/domain/bank/transaction/validation/group/AdvancedGroup.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cdx.bas.domain.bank.transaction.validation.group; - -public interface AdvancedGroup { -} diff --git a/domain/src/main/java/com/cdx/bas/domain/currency/error/CurrencyException.java b/domain/src/main/java/com/cdx/bas/domain/currency/error/CurrencyException.java index bce13746..972fd4fc 100644 --- a/domain/src/main/java/com/cdx/bas/domain/currency/error/CurrencyException.java +++ b/domain/src/main/java/com/cdx/bas/domain/currency/error/CurrencyException.java @@ -1,11 +1,17 @@ package com.cdx.bas.domain.currency.error; + +import com.cdx.bas.domain.testing.Generated; + import java.io.Serial; public class CurrencyException extends RuntimeException { @Serial private static final long serialVersionUID = 4661867226831802143L; + + @Generated public CurrencyException(String errorMessage) { super(errorMessage); } } + diff --git a/domain/src/main/java/com/cdx/bas/domain/currency/rate/ExchangeRateUtils.java b/domain/src/main/java/com/cdx/bas/domain/currency/rate/ExchangeRateUtils.java index ea509f96..e25ba4fe 100644 --- a/domain/src/main/java/com/cdx/bas/domain/currency/rate/ExchangeRateUtils.java +++ b/domain/src/main/java/com/cdx/bas/domain/currency/rate/ExchangeRateUtils.java @@ -1,17 +1,19 @@ package com.cdx.bas.domain.currency.rate; import com.cdx.bas.domain.currency.error.CurrencyException; -import lombok.experimental.UtilityClass; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; -@UtilityClass public class ExchangeRateUtils { public static final String PIVOT_CURRENCY = "EUR"; private static final Double NO_EXCHANGE_RATE_VALUE = 1.0; + private ExchangeRateUtils() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + /** * Euro Exchange Rates for each currency * diff --git a/domain/src/main/java/com/cdx/bas/domain/exception/DomainException.java b/domain/src/main/java/com/cdx/bas/domain/exception/DomainException.java index e44d248a..10b67a32 100644 --- a/domain/src/main/java/com/cdx/bas/domain/exception/DomainException.java +++ b/domain/src/main/java/com/cdx/bas/domain/exception/DomainException.java @@ -1,11 +1,16 @@ package com.cdx.bas.domain.exception; + +import com.cdx.bas.domain.testing.Generated; + public class DomainException extends RuntimeException { + @Generated public DomainException(String errorMessage) { super(errorMessage); } + @Generated public DomainException(String message, Throwable cause) { super(message, cause); } diff --git a/domain/src/main/java/com/cdx/bas/domain/message/CommonMessages.java b/domain/src/main/java/com/cdx/bas/domain/message/CommonMessages.java index 7944d185..b78c554e 100644 --- a/domain/src/main/java/com/cdx/bas/domain/message/CommonMessages.java +++ b/domain/src/main/java/com/cdx/bas/domain/message/CommonMessages.java @@ -1,9 +1,10 @@ package com.cdx.bas.domain.message; -import lombok.experimental.UtilityClass; - -@UtilityClass public class CommonMessages { + private CommonMessages() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + // Context public static final String SCHEDULER_CONTEXT = "Scheduler:"; public static final String BANK_ACCOUNT_CONTEXT = "Bank account:"; @@ -47,6 +48,7 @@ public class CommonMessages { // Cause public static final String NOT_FOUND_CAUSE = "not found"; + public static final String MISSING_ID_CAUSE = "missing id"; public static final String TRANSACTION_ERROR_CAUSE = "transaction error"; public static final String BANK_ACCOUNT_ERROR_CAUSE = "bank account error"; public static final String SHOULD_HAVE_POSITIVE_VALUE_CAUSE = "should have positive value"; diff --git a/domain/src/main/java/com/cdx/bas/domain/message/MessageFormatter.java b/domain/src/main/java/com/cdx/bas/domain/message/MessageFormatter.java index feaffd41..c7026d40 100644 --- a/domain/src/main/java/com/cdx/bas/domain/message/MessageFormatter.java +++ b/domain/src/main/java/com/cdx/bas/domain/message/MessageFormatter.java @@ -1,11 +1,10 @@ package com.cdx.bas.domain.message; -import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; import java.util.Collections; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.StringUtils; /** * Utility class for formatting standardized messages used in various operations, following the structure: @@ -13,9 +12,12 @@ * Each optional detail appears on a new line. * This class enables consistent and clear formatting for logs and error messages across the application. */ -@UtilityClass public class MessageFormatter { + private MessageFormatter() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + /** * A constant for a single space character used to separate message components. */ @@ -33,7 +35,7 @@ public class MessageFormatter { * @param details an optional list of extra details, each appearing on a new line * @return a formatted message string based on the provided inputs */ - public String format(String context, String action, String status, Optional optionalCause, List details) { + public static String format(String context, String action, String status, Optional optionalCause, List details) { StringBuilder message = new StringBuilder(); message.append(context) .append(StringUtils.SPACE) @@ -61,7 +63,7 @@ public String format(String context, String action, String status, Optional optionalCause) { + public static String format(String context, String action, String status, Optional optionalCause) { return format(context, action, status, optionalCause, Collections.emptyList()); } @@ -74,7 +76,7 @@ public String format(String context, String action, String status, Optional details) { + public static String format(String context, String action, String status, List details) { return format(context, action, status, Optional.empty(), details); } @@ -86,7 +88,7 @@ public String format(String context, String action, String status, List * @param status the outcome of the action (e.g., "success") * @return a formatted message string with just context, action, and status */ - public String format(String context, String action, String status) { + public static String format(String context, String action, String status) { return format(context, action, status, Optional.empty(), Collections.emptyList()); } } diff --git a/domain/src/main/java/com/cdx/bas/domain/metadata/MetadataFieldNames.java b/domain/src/main/java/com/cdx/bas/domain/metadata/MetadataFieldNames.java index 18d1316a..4ad5b14a 100644 --- a/domain/src/main/java/com/cdx/bas/domain/metadata/MetadataFieldNames.java +++ b/domain/src/main/java/com/cdx/bas/domain/metadata/MetadataFieldNames.java @@ -1,9 +1,11 @@ package com.cdx.bas.domain.metadata; -import lombok.experimental.UtilityClass; - -@UtilityClass public class MetadataFieldNames { + + private MetadataFieldNames() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + public static final String EMITTER_AMOUNT_BEFORE_KEY = "emitter_amount_before"; public static final String EMITTER_AMOUNT_AFTER_KEY = "emitter_amount_after"; public static final String RECEIVER_AMOUNT_BEFORE_KEY = "receiver_amount_before"; diff --git a/domain/src/main/java/com/cdx/bas/domain/money/AmountUtils.java b/domain/src/main/java/com/cdx/bas/domain/money/AmountUtils.java index 046bf0e4..f79b9255 100644 --- a/domain/src/main/java/com/cdx/bas/domain/money/AmountUtils.java +++ b/domain/src/main/java/com/cdx/bas/domain/money/AmountUtils.java @@ -1,11 +1,13 @@ package com.cdx.bas.domain.money; -import lombok.experimental.UtilityClass; - import java.math.BigDecimal; -@UtilityClass public class AmountUtils { + + private AmountUtils() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + public static boolean isNotPositive(BigDecimal euroAmount) { return euroAmount.signum() <= 0; } diff --git a/domain/src/main/java/com/cdx/bas/domain/testing/Generated.java b/domain/src/main/java/com/cdx/bas/domain/testing/Generated.java new file mode 100644 index 00000000..0756da69 --- /dev/null +++ b/domain/src/main/java/com/cdx/bas/domain/testing/Generated.java @@ -0,0 +1,14 @@ +package com.cdx.bas.domain.testing; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, CONSTRUCTOR}) +public @interface Generated { +} diff --git a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionTest.java b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionTest.java new file mode 100644 index 00000000..2a404483 --- /dev/null +++ b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionTest.java @@ -0,0 +1,69 @@ +package com.cdx.bas.domain.bank.transaction; + +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.Instant; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class TransactionTest { + + @Test + void testCompareTo() { + // Arrange + Instant now = Instant.now(); + Instant earlier = now.minusSeconds(3600); // 1 hour earlier + Instant later = now.plusSeconds(3600); // 1 hour later + + Transaction transaction1 = new Transaction(); + transaction1.setId(1L); + transaction1.setEmitterAccountId(101L); + transaction1.setReceiverAccountId(102L); + transaction1.setAmount(new BigDecimal("100")); + transaction1.setCurrency("USD"); + transaction1.setType(TransactionType.CREDIT); + transaction1.setStatus(TransactionStatus.UNPROCESSED); + transaction1.setDate(now); + transaction1.setLabel("Transaction 1"); + transaction1.setMetadata(new HashMap<>()); + + Transaction transactionEarlier = new Transaction(); + transactionEarlier.setId(2L); + transactionEarlier.setEmitterAccountId(101L); + transactionEarlier.setReceiverAccountId(102L); + transactionEarlier.setAmount(new BigDecimal("200")); + transactionEarlier.setCurrency("USD"); + transactionEarlier.setType(TransactionType.DEBIT); + transactionEarlier.setStatus(TransactionStatus.UNPROCESSED); + transactionEarlier.setDate(earlier); + transactionEarlier.setLabel("Transaction Earlier"); + transactionEarlier.setMetadata(new HashMap<>()); + + Transaction transactionLater = new Transaction(); + transactionLater.setId(3L); + transactionLater.setEmitterAccountId(101L); + transactionLater.setReceiverAccountId(102L); + transactionLater.setAmount(new BigDecimal("300")); + transactionLater.setCurrency("USD"); + transactionLater.setType(TransactionType.CREDIT); + transactionLater.setStatus(TransactionStatus.UNPROCESSED); + transactionLater.setDate(later); + transactionLater.setLabel("Transaction Later"); + transactionLater.setMetadata(new HashMap<>()); + + + // Act & Assert + // transaction1 is later than transactionEarlier + assertTrue(transaction1.compareTo(transactionEarlier) > 0, "transaction1 should be after transactionEarlier"); + + // transaction1 is earlier than transactionLater + assertTrue(transaction1.compareTo(transactionLater) < 0, "transaction1 should be before transactionLater"); + + // transaction1 is equal to itself + assertTrue(transaction1.compareTo(transaction1) == 0, "transaction1 should be equal to itself"); + } +} \ No newline at end of file diff --git a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java index 7eae78cb..6b0c3b7e 100644 --- a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java +++ b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/TransactionValidatorTest.java @@ -1,6 +1,7 @@ package com.cdx.bas.domain.bank.transaction; -import com.cdx.bas.domain.bank.transaction.validation.validator.TransactionValidator; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.bank.transaction.status.TransactionStatus; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; @@ -12,9 +13,6 @@ import java.util.List; import java.util.Map; -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.ERROR; -import static com.cdx.bas.domain.bank.transaction.status.TransactionStatus.UNPROCESSED; -import static com.cdx.bas.domain.bank.transaction.type.TransactionType.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; @@ -25,30 +23,28 @@ class TransactionValidatorTest { TransactionValidator transactionValidator; @Test - public void shouldDoNothing_whenNewDigitalTransactionIsValid() { + void shouldDoNothing_whenNewDigitalTransactionIsValid() { // Arrange - Transaction creditTransaction = Transaction.builder() - .id(null) - .emitterAccountId(1L) - .receiverAccountId(2L) - .amount(new BigDecimal("1")) - .currency("EUR") - .type(CREDIT) - .status(UNPROCESSED) - .date(Instant.now()) - .label("new transaction") - .metadata(new HashMap<>()) - .build(); + Transaction creditTransaction = new Transaction(); + creditTransaction.setId(null); + creditTransaction.setEmitterAccountId(1L); + creditTransaction.setReceiverAccountId(2L); + creditTransaction.setAmount(new BigDecimal("1")); + creditTransaction.setCurrency("EUR"); + creditTransaction.setType(TransactionType.CREDIT); + creditTransaction.setStatus(TransactionStatus.UNPROCESSED); + creditTransaction.setDate(Instant.now()); + creditTransaction.setLabel("new transaction"); + creditTransaction.setMetadata(new HashMap<>()); // Act transactionValidator.validateNewDigitalTransaction(creditTransaction); } @Test - public void shouldThrowTransactionExceptionWithMissingViolation_whenNewDigitalTransactionIsEmpty() { + void shouldThrowTransactionExceptionWithMissingViolation_whenNewDigitalTransactionIsEmpty() { // Arrange - Transaction creditTransaction = Transaction.builder() - .build(); + Transaction creditTransaction = new Transaction(); try { // Act transactionValidator.validateNewDigitalTransaction(creditTransaction); @@ -72,17 +68,17 @@ public void shouldThrowTransactionExceptionWithMissingViolation_whenNewDigitalTr } @Test - public void shouldThrowTransactionExceptionWithWrongValue_whenNewDigitalTransactionHasWrongValue() { + void shouldThrowTransactionExceptionWithWrongValue_whenNewDigitalTransactionHasWrongValue() { // Arrange - Transaction creditTransaction = Transaction.builder() - .id(1L) - .emitterAccountId(-1L) - .receiverAccountId(-1L) - .amount(new BigDecimal("0")) - .currency("NFC") - .type(DEPOSIT) - .status(ERROR) - .build(); + Transaction creditTransaction = new Transaction(); + creditTransaction.setId(1L); + creditTransaction.setEmitterAccountId(-1L); + creditTransaction.setReceiverAccountId(-1L); + creditTransaction.setAmount(new BigDecimal("0")); + creditTransaction.setCurrency("NFC"); + creditTransaction.setType(TransactionType.DEPOSIT); + creditTransaction.setStatus(TransactionStatus.ERROR); + try { // Act transactionValidator.validateNewDigitalTransaction(creditTransaction); @@ -107,29 +103,28 @@ public void shouldThrowTransactionExceptionWithWrongValue_whenNewDigitalTransact } @Test - public void shouldDoNothing_whenExistingDigitalTransactionIsValid() { + void shouldDoNothing_whenExistingDigitalTransactionIsValid() { // Arrange - Transaction creditTransaction = Transaction.builder() - .id(100L) - .emitterAccountId(1L) - .receiverAccountId(2L) - .amount(new BigDecimal("1")) - .currency("EUR") - .type(CREDIT) - .status(UNPROCESSED) - .date(Instant.now()) - .label("new transaction") - .metadata(new HashMap<>()) - .build(); + Transaction creditTransaction = new Transaction(); + creditTransaction.setId(100L); + creditTransaction.setEmitterAccountId(1L); + creditTransaction.setReceiverAccountId(2L); + creditTransaction.setAmount(new BigDecimal("1")); + creditTransaction.setCurrency("EUR"); + creditTransaction.setType(TransactionType.CREDIT); + creditTransaction.setStatus(TransactionStatus.UNPROCESSED); + creditTransaction.setDate(Instant.now()); + creditTransaction.setLabel("new transaction"); + creditTransaction.setMetadata(new HashMap<>()); + // Act transactionValidator.validateExistingDigitalTransaction(creditTransaction); } @Test - public void shouldThrowTransactionException_whenExistingDigitalTransactionIsEmpty() { + void shouldThrowTransactionException_whenExistingDigitalTransactionIsEmpty() { // Arrange - Transaction creditTransaction = Transaction.builder() - .build(); + Transaction creditTransaction = new Transaction(); try { // Act transactionValidator.validateExistingDigitalTransaction(creditTransaction); @@ -153,16 +148,16 @@ public void shouldThrowTransactionException_whenExistingDigitalTransactionIsEmpt } } @Test - public void shouldThrowTransactionExceptionWithWrongValue_whenExistingDigitalTransactionHasWrongValue() { + void shouldThrowTransactionExceptionWithWrongValue_whenExistingDigitalTransactionHasWrongValue() { // Arrange - Transaction creditTransaction = Transaction.builder() - .id(-1L) - .emitterAccountId(-1L) - .receiverAccountId(-1L) - .amount(new BigDecimal("0")) - .currency("NFC") - .type(DEPOSIT) - .build(); + Transaction creditTransaction = new Transaction(); + creditTransaction.setId(-1L); + creditTransaction.setEmitterAccountId(-1L); + creditTransaction.setReceiverAccountId(-1L); + creditTransaction.setAmount(new BigDecimal("0")); + creditTransaction.setCurrency("NFC"); + creditTransaction.setType(TransactionType.DEPOSIT); + try { // Act transactionValidator.validateExistingDigitalTransaction(creditTransaction); @@ -187,29 +182,28 @@ public void shouldThrowTransactionExceptionWithWrongValue_whenExistingDigitalTra } @Test - public void shouldDoNothing_whenCashTransactionIsValid() { + void shouldDoNothing_whenCashTransactionIsValid() { // Arrange Map metadata = new HashMap<>(); metadata.put("bill", "5,5"); - Transaction creditTransaction = Transaction.builder() - .emitterAccountId(1L) - .amount(new BigDecimal("10")) - .currency("EUR") - .type(WITHDRAW) - .status(UNPROCESSED) - .date(Instant.now()) - .label("new transaction") - .metadata(metadata) - .build(); + Transaction creditTransaction = new Transaction(); + creditTransaction.setEmitterAccountId(1L); + creditTransaction.setAmount(new BigDecimal("10")); + creditTransaction.setCurrency("EUR"); + creditTransaction.setType(TransactionType.WITHDRAW); + creditTransaction.setStatus(TransactionStatus.UNPROCESSED); + creditTransaction.setDate(Instant.now()); + creditTransaction.setLabel("new transaction"); + creditTransaction.setMetadata(metadata); + // Act transactionValidator.validateCashTransaction(creditTransaction); } @Test - public void shouldThrowTransactionException_whenNewCashTransactionIsEmpty() { + void shouldThrowTransactionException_whenNewCashTransactionIsEmpty() { // Arrange - Transaction creditTransaction = Transaction.builder() - .build(); + Transaction creditTransaction = new Transaction(); try { // Act transactionValidator.validateCashTransaction(creditTransaction); @@ -232,17 +226,17 @@ public void shouldThrowTransactionException_whenNewCashTransactionIsEmpty() { } @Test - public void shouldThrowTransactionException_whenNewCashTransactionHasWrongValue() { + void shouldThrowTransactionException_whenNewCashTransactionHasWrongValue() { // Arrange - Transaction creditTransaction = Transaction.builder() - .id(-1L) - .emitterAccountId(-1L) - .receiverAccountId(-1L) - .amount(new BigDecimal("0")) - .currency("NFC") - .type(DEBIT) - .status(ERROR) - .build(); + Transaction creditTransaction = new Transaction(); + creditTransaction.setId(-1L); + creditTransaction.setEmitterAccountId(-1L); + creditTransaction.setReceiverAccountId(-1L); + creditTransaction.setAmount(new BigDecimal("0")); + creditTransaction.setCurrency("NFC"); + creditTransaction.setType(TransactionType.DEBIT); + creditTransaction.setStatus(TransactionStatus.ERROR); + try { // Act transactionValidator.validateCashTransaction(creditTransaction); diff --git a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImplTest.java b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImplTest.java new file mode 100644 index 00000000..0043b8e5 --- /dev/null +++ b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/deposit/DepositAmountServiceImplTest.java @@ -0,0 +1,77 @@ +package com.cdx.bas.domain.bank.transaction.category.cash.type.deposit; + +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.saving.SavingBankAccount; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.cash.CashTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.money.Money; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.HashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +class DepositAmountServiceImplTest { + + private final DepositAmountServiceImpl depositAmountService = new DepositAmountServiceImpl(); + + @Test + void shouldApplyToAccount_whenDepositProcessorWithPositiveAmount() { + // Arrange + Transaction transaction = new Transaction(); + transaction.setId(1L); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.DEBIT); + transaction.setEmitterAccountId(1L); + transaction.setReceiverAccountId(2L); + transaction.setAmount(new BigDecimal("100.0")); + transaction.setId(1L); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("0.0"))); + + CashTransactionProcessingDetails cashTransactionProcessingDetails = new CashTransactionProcessingDetails(transaction, emitterBankAccount, new HashMap<>()); + + // Act + depositAmountService.applyToAccount(cashTransactionProcessingDetails); + + // Assert + assertThat(emitterBankAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("100.0"))); + } + + @Test + void shouldThrowTransactionException_whenDepositProcessorWithNegativeAmount() { + // Arrange + Transaction transaction = new Transaction(); + long transactionId = 1L; + transaction.setId(transactionId); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.DEBIT); + transaction.setEmitterAccountId(transactionId); + transaction.setReceiverAccountId(2L); + transaction.setAmount(new BigDecimal("-100.0")); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("0.0"))); + + CashTransactionProcessingDetails cashTransactionProcessingDetails = new CashTransactionProcessingDetails(transaction, emitterBankAccount, new HashMap<>()); + + // Act + try { + depositAmountService.applyToAccount(cashTransactionProcessingDetails); + fail("should have a positive value or fail"); + } catch (TransactionException exception) { + // Assert + assertThat(exception.getMessage()).contains("Deposit transaction: deposit failed - should have positive value\nTransaction id:"+ transactionId + + "\nEuro amount:" + transaction.getAmount()); + } + } +} \ No newline at end of file diff --git a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImplTest.java b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImplTest.java new file mode 100644 index 00000000..4b25465d --- /dev/null +++ b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/cash/type/withdraw/WithdrawAmountServiceImplTest.java @@ -0,0 +1,73 @@ +package com.cdx.bas.domain.bank.transaction.category.cash.type.withdraw; + +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.saving.SavingBankAccount; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.cash.CashTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.cash.type.deposit.DepositAmountServiceImpl; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.money.Money; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.HashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +class WithdrawAmountServiceImplTest { + + private final WithdrawAmountServiceImpl withdrawAmountService = new WithdrawAmountServiceImpl(); + + @Test + void shouldApplyToAccount_whenWithdrawProcessorWithPositiveAmount() { + // Arrange + Transaction transaction = new Transaction(); + transaction.setId(1L); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.WITHDRAW); + transaction.setEmitterAccountId(1L); + transaction.setAmount(new BigDecimal("100.00")); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("100.00"))); + CashTransactionProcessingDetails cashTransactionProcessingDetails = new CashTransactionProcessingDetails(transaction, emitterBankAccount, new HashMap<>()); + + // Act + withdrawAmountService.applyToAccount(cashTransactionProcessingDetails); + + // Assert + assertThat(emitterBankAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("0.00"))); + } + + @Test + void shouldThrowTransactionException_whenWithdrawProcessorWithNegativeAmount() { + // Arrange + Transaction transaction = new Transaction(); + long transactionId = 1L; + transaction.setId(transactionId); + transaction.setType(TransactionType.WITHDRAW); + transaction.setCurrency("EUR"); + transaction.setEmitterAccountId(transactionId); + transaction.setAmount(new BigDecimal("-100.0")); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("100.0"))); + CashTransactionProcessingDetails cashTransactionProcessingDetails = new CashTransactionProcessingDetails(transaction, emitterBankAccount, new HashMap<>()); + + // Act + try { + withdrawAmountService.applyToAccount(cashTransactionProcessingDetails); + fail("should have a positive value or fail"); + } catch (TransactionException exception) { + // Assert + assertThat(exception.getMessage()).contains("Withdraw transaction: withdraw failed - should have positive value\nTransaction id:"+ transactionId + + "\nEuro amount:" + transaction.getAmount()); + } + } +} \ No newline at end of file diff --git a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImplTest.java b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImplTest.java new file mode 100644 index 00000000..5267d0d6 --- /dev/null +++ b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/credit/CreditAmountServiceImplTest.java @@ -0,0 +1,87 @@ + +package com.cdx.bas.domain.bank.transaction.category.digital.type.credit; + +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.saving.SavingBankAccount; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.money.Money; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.HashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +class CreditAmountServiceImplTest { + + private final CreditAmountServiceImpl creditAmountService = new CreditAmountServiceImpl(); + + + @Test + void shouldTransferBetweenAccounts_whenCreditProcessorWithPositiveAmount() { + // Arrange + Transaction transaction = new Transaction(); + transaction.setId(1L); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.CREDIT); + transaction.setEmitterAccountId(1L); + transaction.setReceiverAccountId(2L); + transaction.setAmount(new BigDecimal("100")); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("100.0"))); + + BankAccount receiverBankAccount = new SavingBankAccount(); + receiverBankAccount.setId(2L); + receiverBankAccount.setBalance(Money.of(new BigDecimal("0.0"))); + DigitalTransactionProcessingDetails digitalTransactionProcessingDetails = new DigitalTransactionProcessingDetails(transaction, emitterBankAccount, receiverBankAccount, new HashMap<>()); + + // Act + creditAmountService.transferBetweenAccounts(digitalTransactionProcessingDetails); + + // Assert + assertThat(emitterBankAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("0.0"))); + assertThat(receiverBankAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("100.0"))); + } + + @Test + void shouldThrowTransactionException_whenCreditProcessorWithNegativeAmount() { + // Arrange + Transaction transaction = new Transaction(); + long transactionId = 1L; + transaction.setId(transactionId); + transaction.setType(TransactionType.CREDIT); + transaction.setCurrency("EUR"); + transaction.setEmitterAccountId(transactionId); + transaction.setReceiverAccountId(2L); + transaction.setAmount(new BigDecimal("-100.0")); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("100.0"))); + + BankAccount receiverBankAccount = new SavingBankAccount(); + receiverBankAccount.setId(2L); + receiverBankAccount.setBalance(Money.of(new BigDecimal("0.0"))); + DigitalTransactionProcessingDetails digitalTransactionProcessingDetails = new DigitalTransactionProcessingDetails(transaction, emitterBankAccount, receiverBankAccount, new HashMap<>()); + + // Act + try { + creditAmountService.transferBetweenAccounts(digitalTransactionProcessingDetails); + fail("should have a positive value or fail"); + } catch (TransactionException exception) { + // Assert + assertThat(exception.getMessage()).contains("Credit transaction: credit failed - should have positive value\nTransaction id:"+ transactionId + + "\nEuro amount:" + transaction.getAmount()); + } + } +} \ No newline at end of file diff --git a/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImplTest.java b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImplTest.java new file mode 100644 index 00000000..fd049124 --- /dev/null +++ b/domain/src/test/java/com/cdx/bas/domain/bank/transaction/category/digital/type/debit/DebitAmountServiceImplTest.java @@ -0,0 +1,86 @@ +package com.cdx.bas.domain.bank.transaction.category.digital.type.debit; + +import com.cdx.bas.domain.bank.account.BankAccount; +import com.cdx.bas.domain.bank.account.saving.SavingBankAccount; +import com.cdx.bas.domain.bank.transaction.Transaction; +import com.cdx.bas.domain.bank.transaction.TransactionException; +import com.cdx.bas.domain.bank.transaction.category.digital.DigitalTransactionProcessingDetails; +import com.cdx.bas.domain.bank.transaction.category.digital.type.TransactionType; +import com.cdx.bas.domain.money.Money; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.HashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +class DebitAmountServiceImplTest { + + private final DebitAmountServiceImpl debitAmountService = new DebitAmountServiceImpl(); + + @Test + void shouldTransferBetweenAccounts_whenDebitProcessorWithPositiveAmount() { + // Arrange + Transaction transaction = new Transaction(); + transaction.setId(1L); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.DEBIT); + transaction.setEmitterAccountId(1L); + transaction.setReceiverAccountId(2L); + transaction.setAmount(new BigDecimal("100.0")); + transaction.setId(1L); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("0.0"))); + + BankAccount receiverBankAccount = new SavingBankAccount(); + receiverBankAccount.setId(2L); + receiverBankAccount.setBalance(Money.of(new BigDecimal("100.0"))); + DigitalTransactionProcessingDetails digitalTransactionProcessingDetails = new DigitalTransactionProcessingDetails(transaction, emitterBankAccount, receiverBankAccount, new HashMap<>()); + + // Act + debitAmountService.transferBetweenAccounts(digitalTransactionProcessingDetails); + + // Assert + assertThat(emitterBankAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("100.0"))); + assertThat(receiverBankAccount.getBalance()) + .usingRecursiveComparison() + .isEqualTo(Money.of(new BigDecimal("0.0"))); + } + + @Test + void shouldThrowTransactionException_whenDebitProcessorWithNegativeAmount() { + // Arrange + Transaction transaction = new Transaction(); + long transactionId = 1L; + transaction.setId(transactionId); + transaction.setCurrency("EUR"); + transaction.setType(TransactionType.DEBIT); + transaction.setEmitterAccountId(transactionId); + transaction.setReceiverAccountId(2L); + transaction.setAmount(new BigDecimal("-100.0")); + + BankAccount emitterBankAccount = new SavingBankAccount(); + emitterBankAccount.setId(1L); + emitterBankAccount.setBalance(Money.of(new BigDecimal("0.0"))); + + BankAccount receiverBankAccount = new SavingBankAccount(); + receiverBankAccount.setId(2L); + receiverBankAccount.setBalance(Money.of(new BigDecimal("100.0"))); + DigitalTransactionProcessingDetails digitalTransactionProcessingDetails = new DigitalTransactionProcessingDetails(transaction, emitterBankAccount, receiverBankAccount, new HashMap<>()); + + // Act + try { + debitAmountService.transferBetweenAccounts(digitalTransactionProcessingDetails); + fail("should have a positive value or fail"); + } catch (TransactionException exception) { + // Assert + assertThat(exception.getMessage()).contains("Debit transaction: debit failed - should have positive value\nTransaction id:"+ transactionId + + "\nEuro amount:" + transaction.getAmount()); + } + } +} \ No newline at end of file diff --git a/domain/src/test/java/com/cdx/bas/domain/message/MessageFormatterTest.java b/domain/src/test/java/com/cdx/bas/domain/message/MessageFormatterTest.java index aa9b3513..f315e83c 100644 --- a/domain/src/test/java/com/cdx/bas/domain/message/MessageFormatterTest.java +++ b/domain/src/test/java/com/cdx/bas/domain/message/MessageFormatterTest.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Optional; -import static com.cdx.bas.domain.message.CommonMessages.*; import static org.assertj.core.api.Assertions.assertThat; class MessageFormatterTest { diff --git a/env/test-resources/insert-test-h2.sql b/env/test-resources/insert-test-h2.sql index fa622691..8668e679 100644 --- a/env/test-resources/insert-test-h2.sql +++ b/env/test-resources/insert-test-h2.sql @@ -1,7 +1,7 @@ -- DB SEQ ALTER -ALTER SEQUENCE basapp.customers_customer_id_seq RESTART WITH 10; -ALTER SEQUENCE basapp.bank_accounts_account_id_seq RESTART WITH 10; -ALTER SEQUENCE basapp.transactions_transaction_id_seq RESTART WITH 10; +ALTER SEQUENCE basapp.customers_customer_id_seq RESTART WITH 20; +ALTER SEQUENCE basapp.bank_accounts_account_id_seq RESTART WITH 20; +ALTER SEQUENCE basapp.transactions_transaction_id_seq RESTART WITH 20; INSERT INTO basapp.customers(customer_id, first_name, last_name, gender, marital_status, birthday, country, address, city, email, phone_number, metadata) @@ -42,4 +42,6 @@ VALUES (1, 1, 2, 'CREDIT', 1600.00, 'EUR', 'COMPLETED', TIMESTAMP WITH TIME ZONE (6, 1, 7, 'DEBIT', 2000.00, 'EUR', 'UNPROCESSED', TIMESTAMP WITH TIME ZONE '2024-11-06 18:30:00+01:00', 'transaction 6', null), (7, 3, 1, 'CREDIT', 1000.00, 'EUR', 'UNPROCESSED', TIMESTAMP WITH TIME ZONE '2024-12-06 18:00:00+01:00', 'transaction 7', null), (8, 4, 2, 'DEBIT', 300.80, 'EUR', 'UNPROCESSED', TIMESTAMP WITH TIME ZONE '2024-12-06 19:00:00+01:00', 'transaction 8', null), - (9, 8, 7, 'DEBIT', 5000.00, 'EUR', 'UNPROCESSED', TIMESTAMP WITH TIME ZONE '2024-12-06 19:00:10+01:00', 'transaction 9', null); \ No newline at end of file + (9, 8, 7, 'DEBIT', 5000.00, 'EUR', 'UNPROCESSED', TIMESTAMP WITH TIME ZONE '2024-12-06 19:00:10+01:00', 'transaction 9', null), + (10, 1, null, 'DEPOSIT', 100.00, 'EUR', 'COMPLETED', TIMESTAMP WITH TIME ZONE '2024-12-06 19:00:10+01:00', 'transaction 10', null), + (11, 1, null, 'WITHDRAW', 200.00, 'EUR', 'COMPLETED', TIMESTAMP WITH TIME ZONE '2024-12-06 19:00:10+01:00', 'transaction 11', null); \ No newline at end of file diff --git a/pom.xml b/pom.xml index aa66725b..9d0d099d 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ 3.1.5 3.11.0 3.25.3 - 1.18.30 + 1.18.34 @@ -101,7 +101,7 @@ org.projectlombok lombok - 1.18.30 + ${lombok.version} org.apache.commons