diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b63da45
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,42 @@
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..1c2fda5
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..2a00f5d
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..9448234
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..9661ac7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b9b4d36
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 devArm
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 668a6a2..bd9dd96 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+
# Reto Técnico: Procesamiento de Transacciones Bancarias (CLI)
## Objetivo:
@@ -64,3 +65,35 @@ Desarrolla una aplicación de línea de comandos (CLI) que procese un archivo CS
6. **Documentación y Calidad del Código:**
- Código bien documentado y fácil de leer.
- Comentarios explicando pasos clave y lógica del programa.
+
+# retotecnico-cobol
+
+## Introducción:
+
+El procesamiento de archivos en plataformas windows y linux puede ser un desafio, inclusive el paradigma de programación y la cantidad de datos a procesar.
+En esta oportunidad procesamos archivos csv con ayuda de un complemento univocity y el uso de streams. Aunque sin un enfoque reactivo de momento.
+
+## Instrucciones de Ejecución:
+- **Dependencias**: Agregar com.univocity:univocity-parsers:2.9.1 en el archivo build.gradle
+- **Ejeción**:
+Genera un jar en la carpeta build
+```bash
+gradle shadowJar
+java -jar .\build\libs\retotecnico-cobol-1.0-SNAPSHOT-all.jar .\src\NotFound
+java -jar .\build\libs\retotecnico-cobol-1.0-SNAPSHOT-all.jar
+java -jar .\build\libs\retotecnico-cobol-1.0-SNAPSHOT-all.jar .\external.csv
+```
+
+## Enfoque y Solución:
+
+- **Lógica**: Leer el archivo y procesarlo por medio del uso de streams.
+- **Diseño**: Maximo de filas en el archivo 2^31, por el uso de List. Programación bloqueante, en caso decidir reactiva
+precisaria que todo el ecosistema sea reactivo. Sin emabrgo en JAVA 24, se pueden usar los virtual threats.
+En caso no se le proporcione una ruta o el destino no existe, usara un documento que se encuentre en los recursos del jar.
+
+## Estructura del Proyecto:
+
+- **Arquitectura**: Hexagonal, reference Get Your Hands Dirty on Clean Architecture
+- **Package**: Adapters, Ports and Domain
+
+# cd20e34 (Initial commit)
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..49f2171
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,41 @@
+plugins {
+ id 'java'
+ id 'com.github.johnrengelman.shadow' version '7.0.0'
+}
+
+group = 'org.example'
+version = '1.0-SNAPSHOT'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ // https://mvnrepository.com/artifact/com.univocity/univocity-parsers
+ implementation 'com.univocity:univocity-parsers:2.8.4'
+ testImplementation platform('org.junit:junit-bom:5.10.0')
+ testImplementation 'org.junit.jupiter:junit-jupiter'
+}
+
+jar {
+ manifest {
+ attributes(
+ 'Main-Class': 'org.cli.Main'
+ )
+ }
+ from('src/main/resources') {
+ into('resources')
+ }
+}
+
+shadowJar {
+ archiveBaseName.set('retotecnico-cobol')
+ archiveVersion.set('1.0-SNAPSHOT')
+ mergeServiceFiles()
+}
+
+build.dependsOn shadowJar
+
+test {
+ useJUnitPlatform()
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..5b54dc2
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'retotecnico-cobol'
+
diff --git a/src/main/java/org/cli/Main.java b/src/main/java/org/cli/Main.java
new file mode 100644
index 0000000..5d98de0
--- /dev/null
+++ b/src/main/java/org/cli/Main.java
@@ -0,0 +1,54 @@
+package org.cli;
+
+import org.cli.adapter.out.FileAdapter;
+import org.cli.application.domain.model.Transaccion;
+import org.cli.application.domain.service.LoadTransactionService;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class Main {
+ public static void main(String[] args) {
+
+ LoadTransactionService service = new LoadTransactionService(new FileAdapter());
+ Optional filePath = getFilePath(args);
+
+ List transactions = service.loadCsv(filePath.orElse(null));
+
+ Transaccion biggest = transactions.stream().max(Transaccion::compareTo).orElse(null);
+ Map kindCount = transactions.stream().collect(
+ Collectors.groupingBy(t -> t.tipo, Collectors.counting()));
+ BigDecimal balance = getBalance(transactions);
+
+ print(balance, kindCount, biggest);
+
+ }
+
+ private static void print(BigDecimal balance, Map kindCount, Transaccion biggest) {
+ System.out.println("Reporte de Transacciones");
+ System.out.println("---------------------------------------------");
+ System.out.println("Balance Final: " + balance);
+ System.out.println("Transacción de Mayor Monto: " + biggest);
+ System.out.println("Conteo de Transacciones: " + kindCount.toString().replaceAll("[^\\p{L}\\p{N}\\s=,]",""));
+
+ }
+
+ private static BigDecimal getBalance(List transactions) {
+ return transactions.stream()
+ .map(t ->
+ switch (t.tipo){
+ case "Débito" -> t.monto.negate();
+ case "Crédito" -> t.monto;
+ default -> throw new IllegalStateException("Unexpected value: " + t.tipo);
+ })
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ }
+
+ private static Optional getFilePath(String[] args) {
+ return args.length > 0 ? Optional.of(args[0]) : Optional.empty();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/cli/adapter/out/FileAdapter.java b/src/main/java/org/cli/adapter/out/FileAdapter.java
new file mode 100644
index 0000000..b461501
--- /dev/null
+++ b/src/main/java/org/cli/adapter/out/FileAdapter.java
@@ -0,0 +1,33 @@
+package org.cli.adapter.out;
+
+import com.univocity.parsers.csv.CsvParserSettings;
+import com.univocity.parsers.csv.CsvRoutines;
+import org.cli.application.domain.model.Transaccion;
+import org.cli.application.port.out.LoadTransaccionPort;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+public class FileAdapter implements LoadTransaccionPort {
+ @Override
+ public List loadFromLocal(String filename) {
+
+ CsvParserSettings settings = new CsvParserSettings();
+ settings.setHeaderExtractionEnabled(true);
+ CsvRoutines routines = new CsvRoutines(settings);
+
+ return routines.parseAll(Transaccion.class, loadFromFileSystem(filename));
+ }
+ private InputStream loadFromFileSystem(String filename) {
+ try {
+ return new FileInputStream(filename);
+ } catch (IOException | NullPointerException e) {
+ System.err.println("File not found in the filesystem: " + filename);
+ System.out.println("Using a internal file input.csv of own resources ");
+ System.out.println();
+ return getClass().getClassLoader().getResourceAsStream("input.csv");
+ }
+ }
+}
diff --git a/src/main/java/org/cli/application/domain/model/ComparableTransaccion.java b/src/main/java/org/cli/application/domain/model/ComparableTransaccion.java
new file mode 100644
index 0000000..91fe41e
--- /dev/null
+++ b/src/main/java/org/cli/application/domain/model/ComparableTransaccion.java
@@ -0,0 +1,6 @@
+package org.cli.application.domain.model;
+
+public interface ComparableTransaccion extends Comparable {
+ @Override
+ int compareTo(Transaccion t);
+}
diff --git a/src/main/java/org/cli/application/domain/model/Transaccion.java b/src/main/java/org/cli/application/domain/model/Transaccion.java
new file mode 100644
index 0000000..21c2e7a
--- /dev/null
+++ b/src/main/java/org/cli/application/domain/model/Transaccion.java
@@ -0,0 +1,27 @@
+package org.cli.application.domain.model;
+
+import com.univocity.parsers.annotations.Parsed;
+import com.univocity.parsers.annotations.Trim;
+
+import java.math.BigDecimal;
+
+public class Transaccion implements ComparableTransaccion{
+ @Parsed(field = "id")
+ private BigDecimal id;
+ @Trim
+ @Parsed(field = "tipo")
+ public String tipo;
+ @Parsed(field = "monto")
+ public BigDecimal monto;
+
+ @Override
+ public String toString() {
+ return "ID " + id +" - " + monto;
+ }
+
+ @Override
+ public int compareTo(Transaccion t) {
+ return monto.compareTo(t.monto);
+ }
+}
+
diff --git a/src/main/java/org/cli/application/domain/service/LoadTransactionService.java b/src/main/java/org/cli/application/domain/service/LoadTransactionService.java
new file mode 100644
index 0000000..4bf66ab
--- /dev/null
+++ b/src/main/java/org/cli/application/domain/service/LoadTransactionService.java
@@ -0,0 +1,18 @@
+package org.cli.application.domain.service;
+
+import org.cli.application.domain.model.Transaccion;
+import org.cli.application.port.out.LoadTransaccionPort;
+
+import java.io.FileNotFoundException;
+import java.util.List;
+
+public class LoadTransactionService {
+ private final LoadTransaccionPort loadTransaccionPort;
+
+ public LoadTransactionService(LoadTransaccionPort loadTransaccionPort) {
+ this.loadTransaccionPort = loadTransaccionPort;
+ }
+ public List loadCsv(String filename) {
+ return loadTransaccionPort.loadFromLocal(filename);
+ }
+}
diff --git a/src/main/java/org/cli/application/port/out/LoadTransaccionPort.java b/src/main/java/org/cli/application/port/out/LoadTransaccionPort.java
new file mode 100644
index 0000000..36cd15b
--- /dev/null
+++ b/src/main/java/org/cli/application/port/out/LoadTransaccionPort.java
@@ -0,0 +1,10 @@
+package org.cli.application.port.out;
+
+import org.cli.application.domain.model.Transaccion;
+
+import java.io.FileNotFoundException;
+import java.util.List;
+
+public interface LoadTransaccionPort {
+ List loadFromLocal(String filename);
+}
diff --git a/src/main/resources/input.csv b/src/main/resources/input.csv
new file mode 100644
index 0000000..ac0c3bd
--- /dev/null
+++ b/src/main/resources/input.csv
@@ -0,0 +1,6 @@
+id,tipo,monto
+1,Crédito,100.00
+2,Débito,50.00
+3,Crédito,200.00
+4,Débito,75.00
+5,Crédito,150.00
\ No newline at end of file