From 519d8ab19af5e3ff87b0d3588611994d59a6fda4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Filip=20Pit=C3=A1k?= <xpitak@fi.muni.cz>
Date: Wed, 24 Apr 2024 02:17:14 +0200
Subject: [PATCH] Implementation for automatic Scheduled payment

---
 .../controller/AccountController.java         |  1 -
 m2m-banking-api/transaction-api/openapi.yaml  |  1 +
 .../repository/ProcessRepositoryImpl.java     | 33 ++++++--
 .../repository/ProcessRepositoryJpa.java      | 12 +++
 .../service/ScheduledPaymentService.java      | 81 +++++++++++++++++++
 .../service/TransactionProcessesService.java  |  8 +-
 .../pa165/banking/domain/process/Process.java | 14 +++-
 .../domain/process/ProcessFactory.java        |  2 +-
 .../process/repository/ProcessRepository.java |  5 ++
 .../domain/transaction/Transaction.java       |  3 +-
 .../src/main/resources/application.yaml       |  5 +-
 .../banking/domain/process/ProcessMock.java   |  9 +++
 .../domain/process/ProcessOperationsTest.java |  2 +-
 13 files changed, 156 insertions(+), 20 deletions(-)
 create mode 100644 transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryJpa.java
 create mode 100644 transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/ScheduledPaymentService.java

diff --git a/account-management/src/main/java/cz/muni/pa165/banking/application/controller/AccountController.java b/account-management/src/main/java/cz/muni/pa165/banking/application/controller/AccountController.java
index d2a767c..dc2ed11 100644
--- a/account-management/src/main/java/cz/muni/pa165/banking/application/controller/AccountController.java
+++ b/account-management/src/main/java/cz/muni/pa165/banking/application/controller/AccountController.java
@@ -3,7 +3,6 @@ package cz.muni.pa165.banking.application.controller;
 import cz.muni.pa165.banking.account.management.AccountApi;
 import cz.muni.pa165.banking.account.management.dto.*;
 import cz.muni.pa165.banking.application.facade.AccountFacade;
-import cz.muni.pa165.banking.exception.EntityNotFoundException;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RestController;
diff --git a/m2m-banking-api/transaction-api/openapi.yaml b/m2m-banking-api/transaction-api/openapi.yaml
index f44f9df..1689d72 100644
--- a/m2m-banking-api/transaction-api/openapi.yaml
+++ b/m2m-banking-api/transaction-api/openapi.yaml
@@ -99,6 +99,7 @@ components:
         - DEPOSIT
         - TRANSFER
         - SCHEDULED
+        - REFUND
       description: Enumaration defining a type for a transaction. Each type may have different certain implementations and validations.
       
     AccountDto:
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryImpl.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryImpl.java
index 8d13fe1..40ca8f0 100644
--- a/transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryImpl.java
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryImpl.java
@@ -2,31 +2,48 @@ package cz.muni.pa165.banking.application.repository;
 
 import cz.muni.pa165.banking.domain.process.Process;
 import cz.muni.pa165.banking.domain.process.repository.ProcessRepository;
+import cz.muni.pa165.banking.domain.process.status.Status;
+import cz.muni.pa165.banking.exception.EntityNotFoundException;
 import org.springframework.stereotype.Repository;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
 
 @Repository
 public class ProcessRepositoryImpl implements ProcessRepository {
     
-    // TODO until app has no DB connection -> Milestone2
-    private final Map<UUID, Process> inmemoryDb = new HashMap<>();
+    private final ProcessRepositoryJpa repository;
+
+    public ProcessRepositoryImpl(ProcessRepositoryJpa repository) {
+        this.repository = repository;
+    }
 
     @Override
     public boolean idExists(UUID uuid) {
-        return inmemoryDb.containsKey(uuid);
+        return repository.existsById(uuid.toString());
     }
 
     @Override
     public Process findById(UUID uuid) {
-        return inmemoryDb.get(uuid);
+        Optional<Process> process = repository.findById(uuid.toString());
+        if (process.isEmpty()) {
+            throw new EntityNotFoundException(String.format("Process with UUID %s not found", uuid));
+        }
+        return process.get();
     }
 
     @Override
     public void save(Process process) {
-        inmemoryDb.put(process.uuid(), process);
+        repository.save(process);
+    }
+
+    @Override
+    public List<Process> findProcessOfStatus(Status status) {
+        return null;
+    }
+
+    @Override
+    public Integer invalidateStaleProcesses() {
+        return null;
     }
 
 }
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryJpa.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryJpa.java
new file mode 100644
index 0000000..c018d0e
--- /dev/null
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/repository/ProcessRepositoryJpa.java
@@ -0,0 +1,12 @@
+package cz.muni.pa165.banking.application.repository;
+
+import cz.muni.pa165.banking.domain.process.Process;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface ProcessRepositoryJpa extends JpaRepository<Process, String> {
+    
+    // TODO find podla statusu
+    
+    // TODO update zaseknute procesy starsie nez e.g. tyzden a zmenit na failed + nastavit message nejaky
+    
+}
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/ScheduledPaymentService.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/ScheduledPaymentService.java
new file mode 100644
index 0000000..eee4667
--- /dev/null
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/ScheduledPaymentService.java
@@ -0,0 +1,81 @@
+package cz.muni.pa165.banking.application.service;
+
+import cz.muni.pa165.banking.account.management.AccountApi;
+import cz.muni.pa165.banking.account.management.dto.ScheduledPaymentDto;
+import cz.muni.pa165.banking.account.management.dto.ScheduledPaymentsDto;
+import cz.muni.pa165.banking.domain.account.Account;
+import cz.muni.pa165.banking.domain.messaging.MessageProducer;
+import cz.muni.pa165.banking.domain.money.Money;
+import cz.muni.pa165.banking.domain.process.Process;
+import cz.muni.pa165.banking.domain.process.ProcessFactory;
+import cz.muni.pa165.banking.domain.process.repository.ProcessRepository;
+import cz.muni.pa165.banking.domain.process.repository.ProcessTransactionRepository;
+import cz.muni.pa165.banking.domain.transaction.Transaction;
+import cz.muni.pa165.banking.domain.transaction.TransactionType;
+import cz.muni.pa165.banking.exception.ServerError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.util.Currency;
+import java.util.Objects;
+
+@Service
+public class ScheduledPaymentService {
+
+    private final Logger LOGGER = LoggerFactory.getLogger(ScheduledPaymentService.class);
+
+    private final AccountApi accountApi;
+
+    private final ProcessTransactionRepository processTransactionRepository;
+
+    private final ProcessRepository processRepository;
+
+    private final MessageProducer messageProducer;
+
+    ScheduledPaymentService(AccountApi accountApi,
+                            ProcessTransactionRepository processTransactionRepository,
+                            ProcessRepository processRepository,
+                            MessageProducer messageProducer) {
+        this.accountApi = accountApi;
+        this.processTransactionRepository = processTransactionRepository;
+        this.processRepository = processRepository;
+        this.messageProducer = messageProducer;
+    }
+
+    @Scheduled(cron = "${scheduled-payments.cron.expression}")
+    public void executeScheduledPayments() {
+        LocalDate now = LocalDate.now();
+        ResponseEntity<ScheduledPaymentsDto> response = accountApi.getScheduledPaymentsOf(now);
+        if (!response.getStatusCode().is2xxSuccessful()) {
+            throw new ServerError("Call to Account Management service unsuccessful.");
+        }
+
+        ProcessFactory factory = new ProcessFactory(processTransactionRepository, processRepository);
+        ScheduledPaymentsDto payments = Objects.requireNonNull(response.getBody());
+        for (ScheduledPaymentDto payment : payments.getScheduledPayments()) {
+            Transaction newTransaction = transactionForScheduledPayment(payment);
+            Process process = factory.create(newTransaction, messageProducer);
+            LOGGER.info("[Create Scheduled Payment Process] %s" + process.getUuid());
+        }
+
+        LOGGER.info("Finished creating processes for scheduled payments.");
+    }
+
+    private Transaction transactionForScheduledPayment(ScheduledPaymentDto payment) {
+        Account source = new Account(payment.getSenderAccount());
+        Account target = new Account(payment.getReceiverAccount());
+        Money money = new Money(payment.getAmount(), Currency.getInstance(payment.getCurrencyCode()));
+        return new Transaction(
+                source,
+                target,
+                TransactionType.SCHEDULED,
+                money,
+                "Automatic execution of scheduled payment"
+        );
+    }
+
+}
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/TransactionProcessesService.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/TransactionProcessesService.java
index fb378e4..04cdaa9 100644
--- a/transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/TransactionProcessesService.java
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/application/service/TransactionProcessesService.java
@@ -1,6 +1,6 @@
 package cz.muni.pa165.banking.application.service;
 
-import cz.muni.pa165.banking.application.messaging.ProcessProducer;
+import cz.muni.pa165.banking.domain.messaging.MessageProducer;
 import cz.muni.pa165.banking.domain.process.Process;
 import cz.muni.pa165.banking.domain.process.ProcessFactory;
 import cz.muni.pa165.banking.domain.process.ProcessTransaction;
@@ -26,9 +26,11 @@ public class TransactionProcessesService {
 
     private final ProcessRepository processRepository;
     
-    private final ProcessProducer processProducer;
+    private final MessageProducer processProducer;
 
-    public TransactionProcessesService(ProcessTransactionRepository processTransactionRepository, ProcessRepository processRepository, ProcessProducer processProducer) {
+    public TransactionProcessesService(ProcessTransactionRepository processTransactionRepository,
+                                       ProcessRepository processRepository,
+                                       MessageProducer processProducer) {
         this.processTransactionRepository = processTransactionRepository;
         this.processRepository = processRepository;
         this.processProducer = processProducer;
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/Process.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/Process.java
index 46275d0..b0e7aa0 100644
--- a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/Process.java
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/Process.java
@@ -10,15 +10,21 @@ import java.util.UUID;
 @Entity
 public class Process {
     
-    private final UUID uuid;
+    private UUID uuid;
     
     private StatusInformation currentStatus;
     
-    Process() {
-        uuid = UUID.randomUUID();
-        currentStatus = new StatusInformation(Instant.now(), Status.CREATED, "Process created, waiting for processing.");
+    public static Process createNew() {
+        Process process = new Process();
+        process.uuid = UUID.randomUUID();
+        process.currentStatus = new StatusInformation(Instant.now(), Status.CREATED, "Process created, waiting for processing.");
+        
+        return process;
     }
 
+    @Deprecated // hibernate
+    public Process() {}
+
     /**
      * Return a copy of Process UUID, ensuring the UUID is not modified or replaced. 
      */
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/ProcessFactory.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/ProcessFactory.java
index dfc8988..66f2ff7 100644
--- a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/ProcessFactory.java
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/ProcessFactory.java
@@ -23,7 +23,7 @@ public class ProcessFactory {
 
 
     public Process create(Transaction transaction, MessageProducer messageProducer) {
-        Process newProcess = new Process();
+        Process newProcess = Process.createNew();
         processRepository.save(newProcess);
 
         ProcessTransaction assignedTransaction = new ProcessTransaction(
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/repository/ProcessRepository.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/repository/ProcessRepository.java
index 12b56e5..e9ff491 100644
--- a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/repository/ProcessRepository.java
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/process/repository/ProcessRepository.java
@@ -1,7 +1,9 @@
 package cz.muni.pa165.banking.domain.process.repository;
 
 import cz.muni.pa165.banking.domain.process.Process;
+import cz.muni.pa165.banking.domain.process.status.Status;
 
+import java.util.List;
 import java.util.UUID;
 
 public interface ProcessRepository {
@@ -12,4 +14,7 @@ public interface ProcessRepository {
     
     void save(Process process);
     
+    List<Process> findProcessOfStatus(Status status);
+    
+    Integer invalidateStaleProcesses();
 }
diff --git a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/transaction/Transaction.java b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/transaction/Transaction.java
index 35a7d17..6b0ed78 100644
--- a/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/transaction/Transaction.java
+++ b/transaction-processor/src/main/java/cz/muni/pa165/banking/domain/transaction/Transaction.java
@@ -20,7 +20,8 @@ public class Transaction {
         // Hibernate
     }
     
-    public Transaction(Account source, Account target, TransactionType type, Money amount, String detail) {
+    public Transaction(Account source, Account target,
+                       TransactionType type, Money amount, String detail) {
         this.source = source;
         this.target = target;
         this.type = type;
diff --git a/transaction-processor/src/main/resources/application.yaml b/transaction-processor/src/main/resources/application.yaml
index 5ab2509..15fbc4b 100644
--- a/transaction-processor/src/main/resources/application.yaml
+++ b/transaction-processor/src/main/resources/application.yaml
@@ -1,6 +1,9 @@
 db:
   hostname: localhost
-
+scheduled-payments:
+  cron:
+    expression: "00 7 * * *"
+  
 banking:
   apps:
     management:
diff --git a/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessMock.java b/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessMock.java
index 8cbf9de..9076a23 100644
--- a/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessMock.java
+++ b/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessMock.java
@@ -1,9 +1,18 @@
 package cz.muni.pa165.banking.domain.process;
 
+import java.util.UUID;
+
 public class ProcessMock extends Process {
     
+    private UUID uuid;
+    
     public ProcessMock() {
         super();
+        this.uuid = UUID.randomUUID();
     }
     
+    @Override
+    public UUID getUuid() {
+        return this.uuid;
+    }
 }
diff --git a/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessOperationsTest.java b/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessOperationsTest.java
index 7875ab1..61593a1 100644
--- a/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessOperationsTest.java
+++ b/transaction-processor/src/test/java/cz/muni/pa165/banking/domain/process/ProcessOperationsTest.java
@@ -12,7 +12,7 @@ class ProcessOperationsTest {
 
     @Test
     void changeState() {
-        Process process = new Process();
+        Process process = Process.createNew();
 
         StatusInformation newStatus = new StatusInformation(
                 Instant.now(),
-- 
GitLab