diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b24d71e --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# These are some examples of commonly ignored file patterns. +# You should customize this list as applicable to your project. +# Learn more about .gitignore: +# https://www.atlassian.com/git/tutorials/saving-changes/gitignore + +# Node artifact files +node_modules/ +dist/ + +# Compiled Java class files +*.class + +# Compiled Python bytecode +*.py[cod] + +# Log files +*.log + +# Package files +*.jar + +# Maven +target/ +dist/ + +# JetBrains IDE +.idea/ + +# Unit test reports +TEST*.xml + +# Generated by MacOS +.DS_Store + +# Generated by Windows +Thumbs.db + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv + diff --git a/java-code-challenge.iml b/java-code-challenge.iml new file mode 100644 index 0000000..0c3b6d5 --- /dev/null +++ b/java-code-challenge.iml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a16280d --- /dev/null +++ b/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.1 + + + org.antifraude + challenge + 1.0-SNAPSHOT + antiFraud + + 17 + 17 + 17 + UTF-8 + + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-webflux + + + org.postgresql + postgresql + runtime + + + org.postgresql + r2dbc-postgresql + runtime + + + org.projectlombok + lombok + true + + + io.r2dbc + r2dbc-pool + + + org.springframework.kafka + spring-kafka + + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + + \ No newline at end of file diff --git a/src/main/java/org/antifraude/AntiFraudApplication.java b/src/main/java/org/antifraude/AntiFraudApplication.java new file mode 100644 index 0000000..e3449b2 --- /dev/null +++ b/src/main/java/org/antifraude/AntiFraudApplication.java @@ -0,0 +1,13 @@ +package org.antifraude; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AntiFraudApplication { + + public static void main(String[] args) { + SpringApplication.run(AntiFraudApplication.class, args); + } + +} diff --git a/src/main/java/org/antifraude/adapters/config/KafkaConsumerConfig.java b/src/main/java/org/antifraude/adapters/config/KafkaConsumerConfig.java new file mode 100644 index 0000000..5c05711 --- /dev/null +++ b/src/main/java/org/antifraude/adapters/config/KafkaConsumerConfig.java @@ -0,0 +1,37 @@ +package org.antifraude.adapters.config; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaConsumerConfig { + + @Bean + public ConsumerFactory consumerFactory() { + Map configProps = new HashMap<>(); + configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + configProps.put(ConsumerConfig.GROUP_ID_CONFIG, "antifraude"); + configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); +// configProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,true); +// configProps.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "100"); +// configProps.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "15000"); + return new DefaultKafkaConsumerFactory<>(configProps); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } + +} \ No newline at end of file diff --git a/src/main/java/org/antifraude/adapters/config/KafkaProducerConfig.java b/src/main/java/org/antifraude/adapters/config/KafkaProducerConfig.java new file mode 100644 index 0000000..5b0c10e --- /dev/null +++ b/src/main/java/org/antifraude/adapters/config/KafkaProducerConfig.java @@ -0,0 +1,33 @@ +package org.antifraude.adapters.config; + +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaProducerConfig { + @Bean + public ProducerFactory producerFactory() { + Map configProps = new HashMap<>(); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); + configProps.put(ProducerConfig.RETRIES_CONFIG, 0); + configProps.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); + configProps.put(ProducerConfig.LINGER_MS_CONFIG,1); + configProps.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + return new DefaultKafkaProducerFactory<>(configProps); + } + + @Bean + public KafkaTemplate kafkaTemplate() { + return new KafkaTemplate<>(producerFactory()); + } +} \ No newline at end of file diff --git a/src/main/java/org/antifraude/adapters/in/rest/FinancialTransactionController.java b/src/main/java/org/antifraude/adapters/in/rest/FinancialTransactionController.java new file mode 100644 index 0000000..b235eb4 --- /dev/null +++ b/src/main/java/org/antifraude/adapters/in/rest/FinancialTransactionController.java @@ -0,0 +1,47 @@ +package org.antifraude.adapters.in.rest; + +import org.antifraude.ports.in.FinancialTransactionUseCase; +import org.antifraude.ports.in.KafkaUseCase; +import org.antifraude.domain.dto.FinancialTransactionDTO; +import org.antifraude.domain.model.FinancialTransaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Mono; + +import java.util.Objects; + + +@RestController +@RequestMapping("/api/financial-transaction") +public class FinancialTransactionController { + + private static final Logger logger = LoggerFactory.getLogger(FinancialTransactionController.class.getName()); + private final FinancialTransactionUseCase financialTransactionUseCase; + private final KafkaUseCase kafkaUseCase; + + public FinancialTransactionController(FinancialTransactionUseCase financialTransactionUseCase, KafkaUseCase kafkaUseCase) { + this.financialTransactionUseCase = financialTransactionUseCase; + this.kafkaUseCase = kafkaUseCase; + } + + @PostMapping + public Mono> create(@RequestBody FinancialTransaction financialTransaction) { + return financialTransactionUseCase.createFinancialTransaction(financialTransaction) + .map(createdFinancial -> new ResponseEntity<>(createdFinancial, HttpStatus.CREATED)) + .doOnSuccess(createdFinancial -> + kafkaUseCase.sendMessage("antifraude-topic" + , String.valueOf(Objects.requireNonNull(createdFinancial.getBody()).getId()))); + } + + @GetMapping(value = "/{id}") + public Mono> findID(@PathVariable("id") Long financialTransactionId) { + logger.info("FinancialTransactionController findID {}", financialTransactionId); + return financialTransactionUseCase.findFinancialTransactionByID(financialTransactionId) + .map(createdFinancial -> new ResponseEntity<>(createdFinancial, HttpStatus.OK)) + .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + +} diff --git a/src/main/java/org/antifraude/adapters/in/rest/MessageConsumer.java b/src/main/java/org/antifraude/adapters/in/rest/MessageConsumer.java new file mode 100644 index 0000000..945fdaf --- /dev/null +++ b/src/main/java/org/antifraude/adapters/in/rest/MessageConsumer.java @@ -0,0 +1,21 @@ +package org.antifraude.adapters.in.rest; + +import org.antifraude.ports.in.FinancialTransactionUseCase; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +@Slf4j +@Component +@AllArgsConstructor +public class MessageConsumer { + + private final FinancialTransactionUseCase financialTransactionUseCase; + + @KafkaListener(topics = "antifraude-topic", groupId = "antifraude") + public Mono listen(String message) { + return financialTransactionUseCase.updateFinancialTransactionByID(Long.valueOf(message)); + } +} diff --git a/src/main/java/org/antifraude/adapters/out/persistence/FinancialTransactionAdapter.java b/src/main/java/org/antifraude/adapters/out/persistence/FinancialTransactionAdapter.java new file mode 100644 index 0000000..290bad9 --- /dev/null +++ b/src/main/java/org/antifraude/adapters/out/persistence/FinancialTransactionAdapter.java @@ -0,0 +1,58 @@ +package org.antifraude.adapters.out.persistence; + +import org.antifraude.ports.out.LoadFinancialTransactionPort; +import org.antifraude.domain.dto.FinancialTransactionDTO; +import org.antifraude.domain.model.FinancialTransaction; +import org.antifraude.domain.model.StatusTransfer; +import org.antifraude.domain.model.TransferType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import java.util.UUID; + +@Slf4j +@Component +public class FinancialTransactionAdapter implements LoadFinancialTransactionPort { + + private final FinancialTransactionRepository financialTransactionRepository; + + public FinancialTransactionAdapter(FinancialTransactionRepository financialTransactionRepository) { + this.financialTransactionRepository = financialTransactionRepository; + } + + @Override + public Mono save(FinancialTransaction financialTransaction) { + financialTransaction.setTransactionExternalId(UUID.randomUUID()); + financialTransaction.setTransactionStatusId(StatusTransfer.PENDING.getId()); + return financialTransactionRepository.save(financialTransaction); + } + + @Override + public Mono findByID(Long id) { + return financialTransactionRepository.findById(id) + .map(ft -> FinancialTransactionDTO.builder() + .transactionExternalId(ft.getTransactionExternalId()) + .value(ft.getValue()) + .createdAt(ft.getCreatedAt()) + .transactionType(FinancialTransactionDTO.TransactionType.of( + TransferType.getValueByID(ft.getTransferTypeId()))) + .transactionStatus(FinancialTransactionDTO.TransactionStatus + .of(StatusTransfer.getValueByID(ft.getTransactionStatusId()))) + .build()); + + + } + + @Override + public Mono update(Long id) { + return financialTransactionRepository.findById(id).flatMap(financialTransaction -> { + if (financialTransaction.getValue() > 1000) { + financialTransaction.setTransactionStatusId(StatusTransfer.REFUSED.getId()); + } else { + financialTransaction.setTransactionStatusId(StatusTransfer.APPROVED.getId()); + } + return financialTransactionRepository.save(financialTransaction); + }).then(); + } +} diff --git a/src/main/java/org/antifraude/adapters/out/persistence/FinancialTransactionRepository.java b/src/main/java/org/antifraude/adapters/out/persistence/FinancialTransactionRepository.java new file mode 100644 index 0000000..e1db65f --- /dev/null +++ b/src/main/java/org/antifraude/adapters/out/persistence/FinancialTransactionRepository.java @@ -0,0 +1,10 @@ +package org.antifraude.adapters.out.persistence; + +import org.antifraude.domain.model.FinancialTransaction; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FinancialTransactionRepository extends ReactiveCrudRepository { + +} diff --git a/src/main/java/org/antifraude/adapters/out/persistence/KafkaAdapter.java b/src/main/java/org/antifraude/adapters/out/persistence/KafkaAdapter.java new file mode 100644 index 0000000..b4ab5a0 --- /dev/null +++ b/src/main/java/org/antifraude/adapters/out/persistence/KafkaAdapter.java @@ -0,0 +1,21 @@ +package org.antifraude.adapters.out.persistence; + +import org.antifraude.ports.out.LoadKafkaPort; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@AllArgsConstructor +public class KafkaAdapter implements LoadKafkaPort { + + private KafkaTemplate kafkaTemplate; + + @Override + public void send(String topic, String message) { + log.info("Sending message to topic {}:{}", topic, message); + kafkaTemplate.send(topic, message); + } +} diff --git a/src/main/java/org/antifraude/application/FinancialTransactionService.java b/src/main/java/org/antifraude/application/FinancialTransactionService.java new file mode 100644 index 0000000..93d05d8 --- /dev/null +++ b/src/main/java/org/antifraude/application/FinancialTransactionService.java @@ -0,0 +1,37 @@ +package org.antifraude.application; + +import org.antifraude.ports.in.FinancialTransactionUseCase; +import org.antifraude.ports.out.LoadFinancialTransactionPort; +import org.antifraude.domain.dto.FinancialTransactionDTO; +import org.antifraude.domain.model.FinancialTransaction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Slf4j +@Service +public class FinancialTransactionService implements FinancialTransactionUseCase { + + private final LoadFinancialTransactionPort loadFinancialTransactionPort; + + public FinancialTransactionService(LoadFinancialTransactionPort loadFinancialTransactionPort) { + this.loadFinancialTransactionPort = loadFinancialTransactionPort; + } + + @Override + public Mono createFinancialTransaction(FinancialTransaction financialTransaction) { + return loadFinancialTransactionPort.save(financialTransaction); + } + + @Override + public Mono findFinancialTransactionByID(Long id) { + return loadFinancialTransactionPort.findByID(id); + } + + @Override + public Mono updateFinancialTransactionByID(Long id) { + return loadFinancialTransactionPort.update(id); + } + + +} diff --git a/src/main/java/org/antifraude/application/KafkaService.java b/src/main/java/org/antifraude/application/KafkaService.java new file mode 100644 index 0000000..7a26aad --- /dev/null +++ b/src/main/java/org/antifraude/application/KafkaService.java @@ -0,0 +1,22 @@ +package org.antifraude.application; + +import org.antifraude.ports.in.KafkaUseCase; +import org.antifraude.ports.out.LoadKafkaPort; +import org.springframework.stereotype.Service; + + +@Service +public class KafkaService implements KafkaUseCase { + + + private final LoadKafkaPort loadKafkaPort; + + public KafkaService(LoadKafkaPort loadKafkaPort) { + this.loadKafkaPort = loadKafkaPort; + } + + @Override + public void sendMessage(String topic, String message) { + loadKafkaPort.send(topic, message); + } +} diff --git a/src/main/java/org/antifraude/domain/dto/FinancialTransactionDTO.java b/src/main/java/org/antifraude/domain/dto/FinancialTransactionDTO.java new file mode 100644 index 0000000..5837de8 --- /dev/null +++ b/src/main/java/org/antifraude/domain/dto/FinancialTransactionDTO.java @@ -0,0 +1,48 @@ +package org.antifraude.domain.dto; + +import lombok.*; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FinancialTransactionDTO { + + private UUID transactionExternalId; + private TransactionType transactionType; + private TransactionStatus transactionStatus; + private double value; + private LocalDateTime createdAt; + + @Setter + @Getter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class TransactionType { + private String name; + + public static TransactionType of(String name) { + return new TransactionType(name); + } + } + + @Setter + @Getter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class TransactionStatus { + private String name; + + public static TransactionStatus of(String name) { + return new TransactionStatus(name); + } + + } + +} + + + + diff --git a/src/main/java/org/antifraude/domain/model/FinancialTransaction.java b/src/main/java/org/antifraude/domain/model/FinancialTransaction.java new file mode 100644 index 0000000..34523f3 --- /dev/null +++ b/src/main/java/org/antifraude/domain/model/FinancialTransaction.java @@ -0,0 +1,25 @@ +package org.antifraude.domain.model; + +import lombok.*; +import org.springframework.data.annotation.Id; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FinancialTransaction { + @Id + private Long id; + private String accountExternalIdDebit; + private String accountExternalIdCredit; + private int transactionStatusId; + private UUID transactionExternalId; + private LocalDateTime createdAt; + private int transferTypeId; + private int value; +} + diff --git a/src/main/java/org/antifraude/domain/model/StatusTransfer.java b/src/main/java/org/antifraude/domain/model/StatusTransfer.java new file mode 100644 index 0000000..6024e9c --- /dev/null +++ b/src/main/java/org/antifraude/domain/model/StatusTransfer.java @@ -0,0 +1,26 @@ +package org.antifraude.domain.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum StatusTransfer { + + PENDING(1, "pendiente"), + APPROVED(2, "aprobado"), + REFUSED(3, "rechazado"); + + private final int id; + private final String name; + + public static String getValueByID(int id) { + for (StatusTransfer status : StatusTransfer.values()) { + if (status.getId() == id) { + return status.getName(); + } + } + return ""; + } + +} diff --git a/src/main/java/org/antifraude/domain/model/TransferType.java b/src/main/java/org/antifraude/domain/model/TransferType.java new file mode 100644 index 0000000..869ddc1 --- /dev/null +++ b/src/main/java/org/antifraude/domain/model/TransferType.java @@ -0,0 +1,25 @@ +package org.antifraude.domain.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum TransferType { + OWN_ACCOUNT(1, "cuenta propia"), + OTHER_ACCOUNT_IBK(2, "otras cuentas IBK"), + THIRD_PARTY_ACCOUNT(3, "cuenta de terceros"); + + private final int id; + private final String description; + + public static String getValueByID(int id) { + for (TransferType type : TransferType.values()) { + if (type.getId() == id) { + return type.getDescription(); + } + } + return ""; + } + +} diff --git a/src/main/java/org/antifraude/ports/in/FinancialTransactionUseCase.java b/src/main/java/org/antifraude/ports/in/FinancialTransactionUseCase.java new file mode 100644 index 0000000..cf5e8fd --- /dev/null +++ b/src/main/java/org/antifraude/ports/in/FinancialTransactionUseCase.java @@ -0,0 +1,13 @@ +package org.antifraude.ports.in; + +import org.antifraude.domain.dto.FinancialTransactionDTO; +import org.antifraude.domain.model.FinancialTransaction; +import reactor.core.publisher.Mono; + +public interface FinancialTransactionUseCase { + Mono createFinancialTransaction(FinancialTransaction financialTransaction); + + Mono findFinancialTransactionByID(Long id); + + Mono updateFinancialTransactionByID(Long id); +} diff --git a/src/main/java/org/antifraude/ports/in/KafkaUseCase.java b/src/main/java/org/antifraude/ports/in/KafkaUseCase.java new file mode 100644 index 0000000..125267a --- /dev/null +++ b/src/main/java/org/antifraude/ports/in/KafkaUseCase.java @@ -0,0 +1,5 @@ +package org.antifraude.ports.in; + +public interface KafkaUseCase { + void sendMessage(String topic, String message); +} diff --git a/src/main/java/org/antifraude/ports/out/LoadFinancialTransactionPort.java b/src/main/java/org/antifraude/ports/out/LoadFinancialTransactionPort.java new file mode 100644 index 0000000..de8435b --- /dev/null +++ b/src/main/java/org/antifraude/ports/out/LoadFinancialTransactionPort.java @@ -0,0 +1,13 @@ +package org.antifraude.ports.out; + +import org.antifraude.domain.dto.FinancialTransactionDTO; +import org.antifraude.domain.model.FinancialTransaction; +import reactor.core.publisher.Mono; + +public interface LoadFinancialTransactionPort { + Mono save(FinancialTransaction financialTransaction); + + Mono findByID(Long id); + + Mono update(Long id); +} diff --git a/src/main/java/org/antifraude/ports/out/LoadKafkaPort.java b/src/main/java/org/antifraude/ports/out/LoadKafkaPort.java new file mode 100644 index 0000000..c5a2ed3 --- /dev/null +++ b/src/main/java/org/antifraude/ports/out/LoadKafkaPort.java @@ -0,0 +1,5 @@ +package org.antifraude.ports.out; + +public interface LoadKafkaPort { + void send(String topic, String message); +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..8350253 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,19 @@ +spring: + application: + name: antiFraud + r2dbc: + url: r2dbc:postgresql://localhost:5432/antifraudedb + username: postgres + password: postgres + pool: + enabled: true + kafka: + bootstrap-servers: localhost:9092 + consumer: + group-id: antifraude + auto-offset-reset: earliest + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/antifraudedb + username: postgres + password: postgres diff --git a/src/main/resources/db/initquery.sql b/src/main/resources/db/initquery.sql new file mode 100644 index 0000000..027f0ad --- /dev/null +++ b/src/main/resources/db/initquery.sql @@ -0,0 +1,65 @@ +-- Crear la base de datos + +create database antifraudedb; + +-- Luego seleccionar la base de datos y ejecutar las siguientes queries: + +-- drop table financial_transaction; + +CREATE TABLE financial_transaction +( + id serial primary key, + account_external_id_debit character varying(100) NOT NULL, + account_external_id_credit character varying(100) NOT NULL, + transaction_external_id character varying(100) NOT NULL, + transaction_status_id smallint default 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + transfer_type_id smallint, + value smallint +); + +insert +into financial_transaction (account_external_id_debit, + account_external_id_credit, + transaction_status_id, + transaction_external_id, + transfer_type_id, + value) +values ('3109aeb4-ee23-43a9-994b-3f9af5a0d00e', + '4995c6a9-447b-444d-a040-f356c920a2fd', + 1, + '464c5eb9-ee12-4b6c-b354-a839aa54d5f9', + 1, + 130); + +insert +into financial_transaction (account_external_id_debit, + account_external_id_credit, + transaction_status_id, + transaction_external_id, + transfer_type_id, + value) +values ('3119aeb4-ee23-43a9-994b-3f9af5a0d00e', + '4985c6a9-447b-444d-a040-f356c920a2fd', + 2, + '465c5eb9-ee12-4b6c-b354-a839aa54d5f9', + 2, + 150); + +insert +into financial_transaction (account_external_id_debit, + account_external_id_credit, + transaction_status_id, + transaction_external_id, + transfer_type_id, + value) +values ('3129aeb4-ee23-43a9-994b-3f9af5a0d00e', + '4285c6a9-447b-444d-a040-f356c920a2fd', + 3, + '435c5eb9-ee12-4b6c-b354-a839aa54d5f9', + 3, + 1001); + + +select * +from financial_transaction;