Initial Commit

This commit is contained in:
Daniel Cortes
2020-05-22 01:37:50 -04:00
commit 52d92b236d
48 changed files with 3270 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
target
.idea

99
pom.xml Executable file
View File

@@ -0,0 +1,99 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>xyz.danielcortes</groupId>
<artifactId>fx</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>13</maven.compiler.source>
<maven.compiler.target>13</maven.compiler.target>
</properties>
<dependencies>
<!-- JAVAFX -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>13</version>
</dependency>
<!-- Math Parser -->
<dependency>
<groupId>org.mariuszgromada.math</groupId>
<artifactId>MathParser.org-mXparser</artifactId>
<version>4.3.3</version>
</dependency>
<!-- DB -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.4.4</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<!-- LOG -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>dependency/</classpathPrefix>
<mainClass>xyz.danielcortes.Launcher</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<descriptors>
<descriptor>src/assembly/deployer.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>install</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

25
src/assembly/deployer.xml Executable file
View File

@@ -0,0 +1,25 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>dist</id>
<formats>
<format>tar.gz</format>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<files>
<file>
<source>${project.build.directory}/${project.build.finalName}.jar</source>
</file>
</files>
<fileSets>
<fileSet>
<directory>${project.build.directory}/dependency</directory>
<outputDirectory>/dependency</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/sql</directory>
<outputDirectory>/sql</outputDirectory>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,87 @@
package xyz.danielcortes;
import javafx.application.Application;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.egresos.EgresoController;
import xyz.danielcortes.egresos.EgresoDAO;
import xyz.danielcortes.egresos.EgresoRepo;
import xyz.danielcortes.egresos.EgresoScene;
import xyz.danielcortes.ingresos.IngresoScene;
import xyz.danielcortes.menubar.AppMenu;
import xyz.danielcortes.turno.TurnoDAO;
import xyz.danielcortes.turno.TurnoRepo;
import xyz.danielcortes.turno.TurnoToolbar;
import xyz.danielcortes.turno.TurnoToolbarController;
import xyz.danielcortes.users.LoginController;
import xyz.danielcortes.users.LoginScene;
import xyz.danielcortes.users.UserDAO;
import xyz.danielcortes.users.UserRepo;
public class App extends Application {
static final Logger logger = LoggerFactory.getLogger(App.class);
@Override
public void start(Stage stage) {
DB db = new DB();
UserDAO userDAO = new UserDAO(db);
EgresoDAO egresoDAO = new EgresoDAO(db);
TurnoDAO turnoDAO = new TurnoDAO(db);
UserRepo userRepo = new UserRepo(userDAO);
EgresoRepo egresoRepo = new EgresoRepo(egresoDAO);
TurnoRepo turnoRepo = new TurnoRepo( turnoDAO);
UndoRedoManager undoRedoManager = new UndoRedoManager();
AppMenu appMenu = new AppMenu();
TurnoToolbar turnoToolbar = new TurnoToolbar();
LoginScene loginScene = new LoginScene();
EgresoScene egresoScene = new EgresoScene(turnoToolbar, appMenu);
LoginController loginController = new LoginController(loginScene, userRepo);
TurnoToolbarController turnoToolbarController = new TurnoToolbarController(turnoToolbar, turnoRepo);
EgresoController egresoController = new EgresoController(egresoScene, egresoRepo, turnoRepo, undoRedoManager);
// turnoToolbarController.addTurnoChangeListener(egresoController::onTurnoChange);
//
// turnoToolbarController.addSaveListener(() -> {
// turnoRepo.commit();
// userRepo.commit();
// egresoRepo.commit();
// undoRedoManager.clear();
// });
//
// loginController.addLoginListener(user -> {
// stage.hide();
// stage.setScene(egresoScene.getScene());
//
// stage.setWidth(1000);
// stage.setHeight(600);
// stage.centerOnScreen();
//
// stage.setTitle("Egreso");
// stage.show();
// });
//
// appMenu.addEgresosListener(() -> {
// stage.setScene(egresoScene.getScene());
// stage.setTitle("Egreso");
// });
//
stage.setScene(new IngresoScene(turnoToolbar, appMenu).getScene());
stage.setWidth(1000);
stage.setHeight(600);
stage.centerOnScreen();
stage.setTitle("Ingresos");
stage.show();
}
public static void main(String[] args) {
launch();
}
}

View File

@@ -0,0 +1,6 @@
package xyz.danielcortes;
public interface Command {
void execute();
void undo();
}

View File

@@ -0,0 +1,365 @@
package xyz.danielcortes;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.egresos.Egreso;
import xyz.danielcortes.egresos.TipoEgreso;
/**
* Este objeto se utiliza como interfaz para realizar queries a una entidad a la base de datos.
*
* @param <T> Entidad la cual se obtendrá de estas queries.
*/
public abstract class DAO<T extends Entity> {
private static Logger log = LoggerFactory.getLogger(DAO.class);
protected DB db;
protected String table;
protected List<String> columns;
/**
* Crea un objeto DAO para realizar queries a la base de datos.
*
* @param db DB que entrega conexiones a la base de datos
* @param table Tabla que a la que el objeto hará queries
* @param columns Columnas que tiene la tabla El único requisito es que el primer elemento
* represente el id de la tabla.
*/
public DAO(DB db, String table, List<String> columns) {
this.db = db;
this.table = table;
this.columns = columns;
}
/**
* Entrega las columnas del objeto en la base de datos
* <p>
* Por ejemplo si es que se definieron las columas id, nombre y tipo este metodo debe entregar una
* lista con el valor de esas columnas del objeto entregado, en orden, en una lista.
* <p>
* El id siempre debe ser el primer valor
*
* @param entity Entidad desde la cual se necesitan obtener las columnas
* @return Una lista con las columnas de la entidad.
*/
protected abstract List<Object> getColumnsValues(T entity);
/**
* Mapea un ResultSet a una entidad
*
* @param rs ResultSet el cual ya tendrá el cursor sobre la entidad requerida
* @return La entidad que contenía el ResultSet
* @throws SQLException Si es que la label pasada al result set no existe.
*/
protected abstract T mapToEntity(ResultSet rs) throws SQLException;
/**
* Obtiene un solo elemento que la query devuelva.
*
* @param query Query que ejecutar
* @return La entidad que la query encontró, si no encontró ninguna, se retornara un null
*/
protected T fetchSingle(String query) {
return fetchSingle(query, Collections.emptyList());
}
/**
* Obtiene un solo elemento que la query devuelva.
*
* @param query Query que ejecutar
* @param parameters Parámetros a pasar a la query.
* @return La entidad que la query encontró, si no encontró ninguna, se retornara un null
*/
protected T fetchSingle(String query, List<Object> parameters) {
try (
Connection con = db.getConnection();
PreparedStatement st = con.prepareStatement(query);
) {
for (int i = 0; i < parameters.size(); i++) {
st.setObject(i + 1, parameters.get(i));
}
try (ResultSet rs = st.executeQuery()) {
if (rs.next()) {
return mapToEntity(rs);
}
}
} catch (SQLException e) {
log.error("Error al realizar query {} con parámetros {}", query, parameters, e);
}
return null;
}
/**
* Obtiene una lista de elementos que la query devuelva.
*
* @param query Query que ejecutar
* @return La lista de entidades que la query encontró, si no encontró ninguna, sera una lista
* vacía
*/
protected List<T> fetchMany(String query) {
return fetchMany(query, Collections.emptyList());
}
/**
* Obtiene una lista de elementos que la query devuelva.
*
* @param query Query que ejecutar
* @param parameters Parámetros a pasar a la query.
* @return La lista de entidades que la query encontró, si no encontró ninguna, sera una lista
* vacía
*/
protected List<T> fetchMany(String query, List<Object> parameters) {
List<T> list = new ArrayList<>();
try (
Connection con = db.getConnection();
PreparedStatement st = con.prepareStatement(query);
) {
for (int i = 0; i < parameters.size(); i++) {
st.setObject(i + 1, parameters.get(i));
}
try (ResultSet rs = st.executeQuery()) {
while (rs.next()) {
list.add(mapToEntity(rs));
}
}
} catch (SQLException e) {
log.error("Error al obtener todos los egresos", e);
}
return list;
}
/**
* Busca una entidad dada su id
*
* @param id Id de la entidad buscada
* @return La entidad encontrada, si no se encontró, retornara un null.
*/
public T findByID(String id) {
//noinspection SqlResolve
String query = "select * from " + table + " where " + columns.get(0) + " = ? limit 1";
return fetchSingle(query, List.of(id));
}
/**
* Busca todas las entidades de la tabla
*
* @return La lista de entidades buscadas, si no había ninguna, sera una lista vacía.
*/
public List<T> findAll() {
return fetchMany("select * from " + table);
}
/**
* Inserta una lista de entidades
*
* @param entities Entidades a insertar.
*/
public void insert(Collection<T> entities) {
if (entities.size() <= 0) {
return;
}
try (
Connection con = db.getConnection();
PreparedStatement st = con.prepareStatement(buildInsertQuery(entities.size()));
) {
int i = 1;
for (T entity : entities) {
for (Object column : getColumnsValues(entity)) {
st.setObject(i++, column);
}
}
st.execute();
} catch (SQLException e) {
log.error("Error al insertar la entidad a la tabla {}", table, e);
}
}
/**
* Actualiza una lista de entidades
*
* @param entities Entidades a actualizar
*/
public void update(Collection<T> entities) {
if (entities.size() <= 0) {
return;
}
try (
Connection con = db.getConnection();
PreparedStatement st = con.prepareStatement(buildUpdateQuery());
) {
for (T entity : entities) {
st.clearParameters();
List<Object> columnsValues = getColumnsValues(entity);
for (int i = 1; i < columnsValues.size(); i++) {
st.setObject(i, columnsValues.get(i));
}
st.setObject(columnsValues.size(), columnsValues.get(0));
st.addBatch();
}
st.executeBatch();
} catch (SQLException e) {
log.error("Error al actualizar las entidad de la tabla {}", table, e);
}
}
/**
* Elimina una lista de entidades
*
* @param entities Entidades a eliminar
*/
public void delete(Collection<T> entities) {
if (entities.size() <= 0) {
return;
}
try (
Connection con = db.getConnection();
PreparedStatement st = con.prepareStatement(buildDeleteQuery(entities.size()));
) {
int i = 1;
for (T entity : entities) {
st.setObject(i++, getColumnsValues(entity).get(0));
}
st.execute();
} catch (SQLException e) {
log.error("Error al eliminar egresos", e);
}
}
/**
* Crea una query para insertar una lista de elementos.
* <p>
* Esta query tendrá la forma de:
* <p>
* insert into table (col1, col2, col3, col4) values (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?)
*
* @param size Cantidad de entidades que se insertaran usando esta query
* @return El sql necesario para insertar una serie de elementos.
*/
protected String buildInsertQuery(int size) {
StringBuilder sql = new StringBuilder();
// insert into
sql.append("insert into ");
// insert into table
sql.append(table);
// insert into table (
sql.append(" (");
// insert into table (col1, col2, col3, col4
sql.append(String.join(", ", columns));
// insert into table (col1, col2, col3, col4)
sql.append(") ");
// insert into table (col1, col2, col3, col4) values
sql.append("values ");
for (int i = 0; i < size; i++) {
// (
sql.append("(");
// (?, ?, ?, ?
for (int j = 0; j < columns.size(); j++) {
sql.append("?");
if (j < columns.size() - 1) {
sql.append(", ");
}
}
// (?, ?, ?, ?)
sql.append(")");
if (i < size - 1) {
// (?, ?, ?, ?),
sql.append(", ");
}
}
log.info("Builder insert SQL {}", sql);
return sql.toString();
}
/**
* Crea una query para actualizar una entidad.
* <p>
* Esta query tendrá la forma de:
* <p>
* update table set col2 = ?, col3 = ?, col4 = ? where col1 = ?
*
* @return El sql necesario para actualizar una entidad.
*/
protected String buildUpdateQuery() {
StringBuilder sql = new StringBuilder();
// update
sql.append("update ");
// update table
sql.append(table);
// update table set
sql.append(" set ");
// update table set col2 = ?, col3 = ?, col4 = ?
for (int i = 1; i < columns.size(); i++) {
sql.append(columns.get(i));
sql.append(" = ?");
if (i < columns.size() - 1) {
sql.append(", ");
} else {
sql.append(" ");
}
}
// update table set col2 = ?, col3 = ?, col4 = ? where
sql.append("where ");
// update table set col2 = ?, col3 = ?, col4 = ? where col1
sql.append(columns.get(0));
// update table set col2 = ?, col3 = ?, col4 = ? where col1 = ?
sql.append(" = ?");
log.info("Builder update SQL {}", sql);
return sql.toString();
}
/**
* Crea una query para eliminar una serie de entidades por su id.
* <p>
* Esta query tendrá la forma de:
* <p>
* delete from table where id in (?, ?, ?, ?, ?)
*
* @param size Cantidad de entidades que se eliminaran usando esta query
* @return El sql necesario para eliminar una serie de entidades por su id
*/
protected String buildDeleteQuery(int size) {
StringBuilder sql = new StringBuilder();
// delete from
sql.append("delete from ");
// delete from table
sql.append(table);
// delete from table where
sql.append(" where ");
// delete from table where id
sql.append(columns.get(0));
// delete from table where id in (
sql.append(" in (");
// delete from table where id in (?, ?, ?, ?, ?
for (int i = 0; i < size; i++) {
sql.append("?");
if (i < size - 1) {
sql.append(", ");
}
}
// delete from table where id in (?, ?, ?, ?, ?)
sql.append(")");
log.info("Builder delete SQL {}", sql);
return sql.toString();
}
}

View File

@@ -0,0 +1,22 @@
package xyz.danielcortes;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DB {
private String userName = "root";
private String password = "ff9800s_a_d";
private String url = "jdbc:mysql://localhost:3306/simplefx";
public DB(){}
public Connection getConnection() {
try {
return DriverManager.getConnection(url, userName, password);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,45 @@
package xyz.danielcortes;
import java.util.UUID;
import xyz.danielcortes.egresos.Egreso;
public abstract class Entity {
protected final String id;
public Entity() {
this.id = UUID.randomUUID().toString();
}
public Entity(String id) {
this.id = id;
}
public String getId(){
return id;
}
public int hashCode() {
if (id != null) {
return id.hashCode();
} else {
return super.hashCode();
}
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Entity)) {
return false;
}
Entity other = (Entity) o;
if (id == null) {
return false;
}
return id.equals(other.getId());
}
}

View File

@@ -0,0 +1,7 @@
package xyz.danielcortes;
public class Launcher {
public static void main(String[] args) {
App.main(args);
}
}

View File

@@ -0,0 +1,38 @@
package xyz.danielcortes;
import javafx.util.StringConverter;
import org.mariuszgromada.math.mxparser.Expression;
public class MathStringConverter extends StringConverter<Integer> {
@Override
public String toString(Integer value) {
if (value == null) {
return "0";
}
return String.valueOf(value);
}
@Override
public Integer fromString(String value) {
if (value == null) {
return null;
}
value = value.trim();
if (value.length() <= 0) {
return null;
}
int result = 0;
Expression expression = new Expression(value);
if (expression.checkSyntax()) {
result = (int) Math.floor(expression.calculate());
}
return result;
}
}

View File

@@ -0,0 +1,23 @@
package xyz.danielcortes;
public class Pair<L, R> {
private L left;
private R right;
private Pair(){}
public Pair of(L left, R right){
var pair = new Pair();
pair.left = left;
pair.right = right;
return pair;
}
public L getLeft() {
return left;
}
public R getRight() {
return right;
}
}

View File

@@ -0,0 +1,150 @@
package xyz.danielcortes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class Repo<T extends Entity> {
private static Logger log = LoggerFactory.getLogger(Repo.class);
protected DAO<T> dao;
protected Map<String, T> toAdd;
protected Map<String, T> toRemove;
protected Map<String, T> toUpdate;
public Repo(DAO<T> dao) {
this.dao = dao;
toAdd = new HashMap<>();
toRemove = new HashMap<>();
toUpdate = new HashMap<>();
}
public T getById(String id) {
if(toAdd.containsKey(id)){
return toAdd.get(id);
}
if(toUpdate.containsKey(id)) {
return toUpdate.get(id);
}
var entity = dao.findByID(id);
if(entity != null && !toRemove.containsKey(id)){
return entity;
}
return null;
}
public List<T> getAll() {
List<T> list = new ArrayList<>();
list.addAll(toAdd.values());
list.addAll(toUpdate.values());
for(T entity: dao.findAll()) {
if(!list.contains(entity) && !toRemove.containsKey(entity.getId())){
list.add(entity);
}
}
return list;
}
/**
* Agrega una entidad
* <p>
* - Si es que la entidad se iba a eliminar se quitara de esa lista.
* <p>
* - Si no, se agregara normalmente.
*
* @param entity Entity a agregar
*/
public void add(T entity) {
if (toRemove.get(entity.getId()) != null) {
toRemove.remove(entity.getId());
} else {
toAdd.put(entity.getId(), entity);
}
log.info("-".repeat(100));
log.info("Entity added: " + entity);
logCurrentStatus();
log.info("-".repeat(100));
}
/**
* Elimina una entidad
* <p>
* - Si es que la entity se iba a agregar se quitara de esa lista.
* <p>
* - Si no, se eliminara normalmente.
*
* @param entity Entity a eliminar
*/
public void remove(T entity) {
if (toAdd.get(entity.getId()) != null) {
toAdd.remove(entity.getId());
} else {
toRemove.put(entity.getId(), entity);
}
log.info("-".repeat(100));
log.info("Egreso removed: " + entity);
logCurrentStatus();
log.info("-".repeat(100));
}
/**
* Actualiza una entity
* <p>
* - Si es que la entity se no se iba a agregar, se actualizara normalmente.
* <p>
* - Si la entity se iba a agregar, sera actualizado el de la lista de agregar.
*
* @param entity entity a actualizar
*/
public void update(T entity) {
if (toAdd.get(entity.getId()) == null) {
toUpdate.put(entity.getId(), entity);
} else {
toAdd.put(entity.getId(), entity);
}
log.info("-".repeat(100));
log.info("Egreso updated: " + entity);
logCurrentStatus();
log.info("-".repeat(100));
}
public void commit() {
log.info("-".repeat(100));
log.info("Doing commit");
logCurrentStatus();
log.info("-".repeat(100));
dao.insert(toAdd.values());
dao.update(toUpdate.values());
dao.delete(toRemove.values());
toAdd.clear();
toUpdate.clear();
toRemove.clear();
}
private void logCurrentStatus() {
log.info("Current repo status");
log.info("To Add:");
toAdd.values().forEach(e -> log.info("{}", e));
log.info("-".repeat(50));
log.info("To Remove:");
toRemove.values().forEach(e -> log.info("{}", e));
log.info("-".repeat(50));
log.info("To Update:");
toUpdate.values().forEach(e -> log.info("{}", e));
}
}

View File

@@ -0,0 +1,68 @@
package xyz.danielcortes;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UndoRedoManager {
private static Logger log = LoggerFactory.getLogger(UndoRedoManager.class);
private Stack<Command> undoStack;
private Stack<Command> redoStack;
public UndoRedoManager() {
undoStack = new Stack<>();
redoStack = new Stack<>();
}
public void execute(Command command) {
command.execute();
redoStack.clear();
undoStack.push(command);
logUndoRedoStacks();
}
public void undo() {
if (!undoStack.empty()) {
undoStack.peek().undo();
redoStack.push(undoStack.pop());
logUndoRedoStacks();
}
}
public void redo() {
if (!redoStack.empty()) {
redoStack.peek().execute();
undoStack.push(redoStack.pop());
logUndoRedoStacks();
}
}
public void clear() {
undoStack.clear();
redoStack.clear();
log.info("Cleared");
}
private void logUndoRedoStacks() {
log.info("-".repeat(100));
log.info("Current Undo Redo Stack Status: ");
log.info("Undo Stack: ");
for (Command command : undoStack) {
log.info("{}", command);
}
log.info("-".repeat(50));
log.info("Redo Stack: ");
for (Command command : redoStack) {
log.info("{}", command);
}
log.info("-".repeat(100));
}
}

View File

@@ -0,0 +1,66 @@
package xyz.danielcortes.egresos;
import xyz.danielcortes.Entity;
public final class Egreso extends Entity {
private final String nro;
private final String descripcion;
private final Integer valor;
private final TipoEgreso tipo;
private final String turnoID;
public Egreso(String nro, String descripcion, int valor, TipoEgreso tipo, String turnoID) {
super();
this.nro = nro;
this.descripcion = descripcion;
this.valor = valor;
this.tipo = tipo;
this.turnoID = turnoID;
}
public Egreso(String id, String nro, String descripcion, int valor, TipoEgreso tipo, String turnoID) {
super(id);
this.nro = nro;
this.descripcion = descripcion;
this.valor = valor;
this.tipo = tipo;
this.turnoID = turnoID;
}
public final String getId() {
return id;
}
public final String getNro() {
return nro;
}
public final String getDescripcion() {
return descripcion;
}
public final Integer getValor() {
return valor;
}
public final TipoEgreso getTipo() {
return tipo;
}
public final String getTurnoID() {
return turnoID;
}
@Override
public String toString() {
return "Egreso{" +
"id='" + id + '\'' +
", nro='" + nro + '\'' +
", descripcion='" + descripcion + '\'' +
", valor=" + valor +
", tipo=" + tipo +
", turnoID='" + turnoID + '\'' +
'}';
}
}

View File

@@ -0,0 +1,85 @@
package xyz.danielcortes.egresos;
import java.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.Repo;
import xyz.danielcortes.UndoRedoManager;
import xyz.danielcortes.egresos.commands.AddEgresoCommand;
import xyz.danielcortes.egresos.commands.RemoveEgresoCommand;
import xyz.danielcortes.egresos.commands.UpdateEgresoCommand;
import xyz.danielcortes.listeners.ObjectChangeListener;
import xyz.danielcortes.turno.Turno;
import xyz.danielcortes.turno.TurnoRepo;
public class EgresoController {
private static Logger log = LoggerFactory.getLogger(EgresoController.class);
private Turno currentTurno;
private UndoRedoManager undoRedoManager;
private EgresoScene scene;
private EgresoRepo egresoRepo;
private TurnoRepo turnoRepo;
public EgresoController(EgresoScene scene, EgresoRepo egresoRepo, TurnoRepo turnoRepo, UndoRedoManager undoRedoManager) {
this.egresoRepo = egresoRepo;
this.turnoRepo = turnoRepo;
this.scene = scene;
this.undoRedoManager = undoRedoManager;
this.scene.onAdd(this::onAdd);
this.scene.onRemove(this::onRemove);
this.scene.onEdit(this::onEdit);
this.scene.onUndo(this::onUndo);
this.scene.onRedo(this::onRedo);
this.currentTurno = this.turnoRepo.getByFechaCajaTurno(LocalDate.now(), 1, 1);
this.scene.setEgresos(this.egresoRepo.getByTurnoID(this.currentTurno.getId()));
}
public void onTurnoChange(Turno turno) {
this.currentTurno = turno;
undoRedoManager.clear();
scene.setEgresos(egresoRepo.getByTurnoID(turno.getId()));
}
private void onAdd() {
log.info("Action: ADD");
Egreso egreso = new Egreso(
scene.getNro(),
scene.getDescripcion(),
scene.getValor(),
scene.getTipo(),
this.currentTurno.getId()
);
undoRedoManager.execute(new AddEgresoCommand(egresoRepo, scene, egreso));
}
private void onRemove(Egreso egreso) {
log.info("Action: REMOVE");
if (egreso == null) {
return;
}
undoRedoManager.execute(new RemoveEgresoCommand(egresoRepo, scene, egreso));
}
private void onEdit(Egreso oldEgreso, Egreso newEgreso) {
log.info("Action: EDIT");
undoRedoManager.execute(new UpdateEgresoCommand(egresoRepo, scene, newEgreso, oldEgreso));
}
private void onUndo() {
log.info("Action: UNDO");
undoRedoManager.undo();
}
private void onRedo() {
log.info("Action: REDO");
undoRedoManager.redo();
}
}

View File

@@ -0,0 +1,51 @@
package xyz.danielcortes.egresos;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.DAO;
import xyz.danielcortes.DB;
public class EgresoDAO extends DAO<Egreso> {
private static Logger log = LoggerFactory.getLogger(EgresoDAO.class);
public EgresoDAO(DB db) {
super(db, "egreso", List.of("id", "nro", "descripcion", "valor", "tipo", "turno_id"));
}
public List<Egreso> findByTipo(TipoEgreso tipo) {
return fetchMany("select * from egreso where tipo = ?", List.of(tipo));
}
public List<Egreso> findByTurnoID(String turnoID) {
return fetchMany("select * from egreso where turno_id = ?", List.of(turnoID));
}
@Override
protected List<Object> getColumnsValues(Egreso egreso) {
return List.of(
egreso.getId(),
egreso.getNro(),
egreso.getDescripcion(),
egreso.getValor(),
egreso.getTipo().toString(),
egreso.getTurnoID()
);
}
@Override
protected Egreso mapToEntity(ResultSet rs) throws SQLException {
return new Egreso(
rs.getString("id"),
rs.getString("nro"),
rs.getString("descripcion"),
rs.getInt("valor"),
TipoEgreso.fromName(rs.getString("tipo")),
rs.getString("turno_id")
);
}
}

View File

@@ -0,0 +1,71 @@
package xyz.danielcortes.egresos;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.DAO;
import xyz.danielcortes.Repo;
public class EgresoRepo extends Repo<Egreso> {
private static Logger log = LoggerFactory.getLogger(EgresoRepo.class);
private EgresoDAO dao;
public EgresoRepo(EgresoDAO dao) {
super(dao);
this.dao = dao;
}
public List<Egreso> getByTipo(TipoEgreso tipo) {
List<Egreso> egresos = new ArrayList<>();
for (Egreso egreso : toAdd.values()) {
if (egreso.getTipo().equals(tipo)) {
egresos.add(egreso);
}
}
for (Egreso egreso : toUpdate.values()) {
if (egreso.getTipo().equals(tipo)) {
egresos.add(egreso);
}
}
for(Egreso egreso : dao.findByTipo(tipo)){
if(!egresos.contains(egreso) && !toRemove.containsKey(egreso.getId())){
egresos.add(egreso);
}
}
return egresos;
}
public List<Egreso> getByTurnoID(String turnoID) {
List<Egreso> egresos = new ArrayList<>();
for (Egreso egreso : toAdd.values()) {
if (egreso.getTurnoID().equals(turnoID)) {
egresos.add(egreso);
}
}
for (Egreso egreso : toUpdate.values()) {
if (egreso.getTurnoID().equals(turnoID)) {
egresos.add(egreso);
}
}
for(Egreso egreso : dao.findByTurnoID(turnoID)){
if(!egresos.contains(egreso) && !toRemove.containsKey(egreso.getId())){
egresos.add(egreso);
}
}
return egresos;
}
}

View File

@@ -0,0 +1,384 @@
package xyz.danielcortes.egresos;
import java.util.LinkedList;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.util.converter.IntegerStringConverter;
import xyz.danielcortes.MathStringConverter;
import xyz.danielcortes.listeners.AddListener;
import xyz.danielcortes.listeners.EditListener;
import xyz.danielcortes.listeners.RemoveListener;
import xyz.danielcortes.listeners.SimpleListener;
import xyz.danielcortes.menubar.AppMenu;
import xyz.danielcortes.turno.TurnoToolbar;
public class EgresoScene {
private List<AddListener> addListeners;
private List<RemoveListener<Egreso>> deleteListeners;
private List<EditListener<Egreso>> editListeners;
private List<SimpleListener> undoListeners;
private List<SimpleListener> redoListeners;
private Scene scene;
private TurnoToolbar turnoToolbar;
private AppMenu menu;
private TableView<Egreso> table;
private ObservableList<Egreso> egresos;
private ComboBox<TipoEgreso> tipoCombo;
private TextField descripcionField;
private TextField nroField;
private TextField valorField;
private Button addButton;
private Button removeButton;
public EgresoScene(TurnoToolbar turnoToolbar, AppMenu menu) {
this.turnoToolbar = turnoToolbar;
this.menu = menu;
addListeners = new LinkedList<>();
deleteListeners = new LinkedList<>();
editListeners = new LinkedList<>();
undoListeners = new LinkedList<>();
redoListeners = new LinkedList<>();
egresos = FXCollections.observableArrayList();
scene = buildScene();
createListeners();
setTraversalPolicy();
}
private Scene buildScene() {
var controls = createControls();
var tableContainer = createTable();
var box = new VBox(
menu.getMenuBar(),
turnoToolbar.getToolbar(),
new Separator(),
controls,
tableContainer
);
VBox.setVgrow(tableContainer, Priority.ALWAYS);
return new Scene(box);
}
private VBox createTable() {
table = new TableView<>();
table.setEditable(true);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
var tipoColumn = new TableColumn<Egreso, TipoEgreso>("Tipo");
var nroColumn = new TableColumn<Egreso, String>("");
var descColumn = new TableColumn<Egreso, String>("Descripción");
var valorColumn = new TableColumn<Egreso, Integer>("Valor");
tipoColumn.setCellValueFactory(new PropertyValueFactory<>("tipo"));
tipoColumn.setCellFactory(ComboBoxTableCell.forTableColumn(TipoEgreso.values()));
tipoColumn.setOnEditCommit((CellEditEvent<Egreso, TipoEgreso> event) -> {
TablePosition<Egreso, TipoEgreso> pos = event.getTablePosition();
TipoEgreso tipo = event.getNewValue();
Egreso oldEgreso = event.getTableView().getItems().get(pos.getRow());
Egreso newEgreso = new Egreso(
oldEgreso.getId(),
oldEgreso.getNro(),
oldEgreso.getDescripcion(),
oldEgreso.getValor(),
tipo,
oldEgreso.getTurnoID()
);
event.getTableView().getItems().set(pos.getRow(), newEgreso);
this.notifyEditListeners(oldEgreso, newEgreso);
});
nroColumn.setCellValueFactory(new PropertyValueFactory<>("nro"));
nroColumn.setCellFactory(TextFieldTableCell.forTableColumn());
nroColumn.setOnEditCommit((CellEditEvent<Egreso, String> event) -> {
TablePosition<Egreso, String> pos = event.getTablePosition();
String nro = event.getNewValue();
Egreso oldEgreso = event.getTableView().getItems().get(pos.getRow());
Egreso newEgreso = new Egreso(
oldEgreso.getId(),
nro,
oldEgreso.getDescripcion(),
oldEgreso.getValor(),
oldEgreso.getTipo(),
oldEgreso.getTurnoID()
);
event.getTableView().getItems().set(pos.getRow(), newEgreso);
this.notifyEditListeners(oldEgreso, newEgreso);
});
descColumn.setCellValueFactory(new PropertyValueFactory<>("descripcion"));
descColumn.setCellFactory(TextFieldTableCell.forTableColumn());
descColumn.setOnEditCommit((CellEditEvent<Egreso, String> event) -> {
TablePosition<Egreso, String> pos = event.getTablePosition();
String desc = event.getNewValue();
Egreso oldEgreso = event.getTableView().getItems().get(pos.getRow());
Egreso newEgreso = new Egreso(
oldEgreso.getId(),
oldEgreso.getNro(),
desc,
oldEgreso.getValor(),
oldEgreso.getTipo(),
oldEgreso.getTurnoID()
);
event.getTableView().getItems().set(pos.getRow(), newEgreso);
this.notifyEditListeners(oldEgreso, newEgreso);
});
valorColumn.setCellValueFactory(new PropertyValueFactory<>("valor"));
valorColumn.setCellFactory(TextFieldTableCell.forTableColumn(new MathStringConverter()));
valorColumn.setOnEditCommit((CellEditEvent<Egreso, Integer> event) -> {
TablePosition<Egreso, Integer> pos = event.getTablePosition();
int valor = event.getNewValue();
Egreso oldEgreso = event.getTableView().getItems().get(pos.getRow());
Egreso newEgreso = new Egreso(
oldEgreso.getId(),
oldEgreso.getNro(),
oldEgreso.getDescripcion(),
valor,
oldEgreso.getTipo(),
oldEgreso.getTurnoID()
);
event.getTableView().getItems().set(pos.getRow(), newEgreso);
this.notifyEditListeners(oldEgreso, newEgreso);
});
table.getColumns().add(tipoColumn);
table.getColumns().add(nroColumn);
table.getColumns().add(descColumn);
table.getColumns().add(valorColumn);
table.setItems(egresos);
var box = new VBox(table);
VBox.setVgrow(table, Priority.ALWAYS);
return box;
}
private GridPane createControls() {
var grid = new GridPane();
grid.setHgap(10);
grid.setVgap(5);
grid.setPadding(new Insets(10));
var colConstraintA = new ColumnConstraints();
colConstraintA.setPercentWidth(22.5);
grid.getColumnConstraints().add(colConstraintA);
grid.getColumnConstraints().add(colConstraintA);
grid.getColumnConstraints().add(colConstraintA);
grid.getColumnConstraints().add(colConstraintA);
var colConstraintB = new ColumnConstraints();
colConstraintB.setPercentWidth(5);
grid.getColumnConstraints().add(colConstraintB);
grid.getColumnConstraints().add(colConstraintB);
tipoCombo = new ComboBox<>();
tipoCombo.setItems(FXCollections.observableArrayList(TipoEgreso.values()));
tipoCombo.setEditable(false);
tipoCombo.setMaxWidth(Double.MAX_VALUE);
tipoCombo.getSelectionModel().select(0);
nroField = new TextField();
descripcionField = new TextField();
valorField = new TextField();
valorField.setTextFormatter(new TextFormatter<>(new MathStringConverter(), 0));
addButton = new Button("+");
addButton.setMaxWidth(Double.MAX_VALUE);
removeButton = new Button("-");
removeButton.setMaxWidth(Double.MAX_VALUE);
grid.add(new Label("Tipo Ingreso"), 0, 0);
grid.add(new Label(""), 1, 0);
grid.add(new Label("Descripción"), 2, 0);
grid.add(new Label("Valor"), 3, 0);
grid.add(tipoCombo, 0, 1);
grid.add(nroField, 1, 1);
grid.add(descripcionField, 2, 1);
grid.add(valorField, 3, 1);
grid.add(addButton, 4, 1);
grid.add(removeButton, 5, 1);
return grid;
}
private void createListeners() {
// Add listeners
valorField.setOnAction(e -> notifyAddListeners());
addButton.setOnAction(e -> notifyAddListeners());
// Delete listeners
scene.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.DELETE) {
notifyRemoveListeners(getSelected());
}
});
removeButton.setOnAction(e -> notifyRemoveListeners(getSelected()));
// Undo Listeners
scene.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.Z && e.isControlDown() && !e.isShiftDown()) {
notifyUndoListeners();
}
});
// Redo Listeners
scene.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.Z && e.isControlDown() && e.isShiftDown()) {
notifyRedoListeners();
}
});
}
private void setTraversalPolicy() {
tipoCombo.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ENTER) {
nroField.requestFocus();
}
});
nroField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ENTER) {
descripcionField.requestFocus();
}
});
descripcionField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ENTER) {
valorField.requestFocus();
}
});
valorField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ENTER) {
tipoCombo.requestFocus();
}
});
}
private void notifyEditListeners(Egreso oldEgreso, Egreso newEgreso) {
editListeners.forEach(action -> action.run(oldEgreso, newEgreso));
}
private void notifyAddListeners() {
addListeners.forEach(AddListener::run);
}
private void notifyRemoveListeners(Egreso egreso) {
deleteListeners.forEach(action -> action.run(egreso));
}
private void notifyUndoListeners() {
undoListeners.forEach(SimpleListener::run);
}
private void notifyRedoListeners() {
redoListeners.forEach(SimpleListener::run);
}
public Scene getScene() {
return scene;
}
public void setEgresos(List<Egreso> egresos) {
this.egresos.clear();
this.egresos.addAll(egresos);
}
public void addEgresos(List<Egreso> egresos) {
this.egresos.addAll(egresos);
}
public void addEgreso(Egreso egreso) {
this.egresos.add(egreso);
}
public void updateEgreso(Egreso egreso) {
this.egresos.set(egresos.indexOf(egreso), egreso);
}
public void removeEgreso(Egreso egreso) {
this.egresos.remove(egreso);
}
public TipoEgreso getTipo() {
return tipoCombo.getSelectionModel().getSelectedItem();
}
public String getDescripcion() {
return descripcionField.getText();
}
public String getNro() {
return nroField.getText();
}
public int getValor() {
int valor = 0;
try {
valor = Integer.parseInt(valorField.getText());
} catch (NumberFormatException e) {
System.out.println("Bad integer in valorField");
}
return valor;
}
public Egreso getSelected() {
return table.getSelectionModel().getSelectedItem();
}
public void onAdd(AddListener action) {
addListeners.add(action);
}
public void onRemove(RemoveListener<Egreso> action) {
deleteListeners.add(action);
}
public void onEdit(EditListener<Egreso> action) {
editListeners.add(action);
}
public void onUndo(SimpleListener action) {
undoListeners.add(action);
}
public void onRedo(SimpleListener action) {
redoListeners.add(action);
}
}

View File

@@ -0,0 +1,38 @@
package xyz.danielcortes.egresos;
import java.util.Objects;
public enum TipoEgreso {
facturaMateriaPrima("Factura Materia Prima"),
facturaGastosGenerales("Factura Gastos Generales"),
boletaMateriaPrima("Boleta Materia Prima"),
valeMateriaPrima("Vale Materia Prima"),
boletaGastosGenerales("Boleta Gastos Generales"),
valeGastosGenerales("Vale Gastos Generales"),
guiaMateriaPrima("Guia Materia Prima"),
anticipoArriendo("Anticipo Arriendo"),
anticipoPersonal("Anticipo Personal"),
pagoPartime("Pago Partime"),
retirosGerencia("Retiros Gerencia"),
otro("Otro");
private String name;
TipoEgreso (String name) {
this.name = name;
}
public static TipoEgreso fromName(String name) {
for (TipoEgreso tipo : TipoEgreso.values()) {
if (Objects.equals(tipo.name, name)) {
return tipo;
}
}
return TipoEgreso.otro;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -0,0 +1,34 @@
package xyz.danielcortes.egresos.commands;
import xyz.danielcortes.Command;
import xyz.danielcortes.egresos.Egreso;
import xyz.danielcortes.egresos.EgresoRepo;
import xyz.danielcortes.egresos.EgresoScene;
public class AddEgresoCommand implements Command {
private EgresoRepo repo;
private EgresoScene scene;
private Egreso egreso;
public AddEgresoCommand(EgresoRepo repo, EgresoScene scene, Egreso egreso) {
this.repo = repo;
this.scene = scene;
this.egreso = egreso;
}
@Override
public void execute() {
repo.add(egreso);
scene.addEgreso(egreso);
}
@Override
public void undo() {
repo.remove(egreso);
scene.removeEgreso(egreso);
}
public String toString() {
return "AddEgresoCommand: " + egreso;
}
}

View File

@@ -0,0 +1,35 @@
package xyz.danielcortes.egresos.commands;
import xyz.danielcortes.Command;
import xyz.danielcortes.egresos.Egreso;
import xyz.danielcortes.egresos.EgresoRepo;
import xyz.danielcortes.egresos.EgresoScene;
public class RemoveEgresoCommand implements Command {
private EgresoRepo repo;
private EgresoScene scene;
private Egreso egreso;
public RemoveEgresoCommand(EgresoRepo repo, EgresoScene scene, Egreso egreso) {
this.repo = repo;
this.scene = scene;
this.egreso = egreso;
}
@Override
public void execute() {
repo.remove(egreso);
scene.removeEgreso(egreso);
}
@Override
public void undo() {
repo.add(egreso);
scene.addEgreso(egreso);
}
@Override
public String toString() {
return "RemoveEgresoCommand: " + egreso;
}
}

View File

@@ -0,0 +1,39 @@
package xyz.danielcortes.egresos.commands;
import xyz.danielcortes.Command;
import xyz.danielcortes.egresos.Egreso;
import xyz.danielcortes.egresos.EgresoRepo;
import xyz.danielcortes.egresos.EgresoScene;
public class UpdateEgresoCommand implements Command {
private EgresoRepo repo;
private EgresoScene scene;
private Egreso newEgreso;
private Egreso oldEgreso;
public UpdateEgresoCommand(EgresoRepo repo, EgresoScene scene, Egreso newEgreso, Egreso oldEgreso) {
this.repo = repo;
this.scene = scene;
this.newEgreso = newEgreso;
this.oldEgreso = oldEgreso;
}
@Override
public void execute() {
repo.update(newEgreso);
scene.updateEgreso(newEgreso);
}
@Override
public void undo() {
repo.update(oldEgreso);
scene.updateEgreso(oldEgreso);
}
@Override
public String toString() {
return "UpdateEgresoCommand: " + oldEgreso + " -> " + newEgreso;
}
}

View File

@@ -0,0 +1,70 @@
package xyz.danielcortes.ingresos;
import xyz.danielcortes.Entity;
public final class Ingreso extends Entity {
private final int numero;
private final int ingresoInicial;
private final int ingresoFinal;
private final int total;
private final TipoIngreso tipo;
private final String turnoID;
public Ingreso(int numero, int ingresoInicial, int ingresoFinal, int total,
TipoIngreso tipo, String turnoID) {
this.numero = numero;
this.ingresoInicial = ingresoInicial;
this.ingresoFinal = ingresoFinal;
this.total = total;
this.tipo = tipo;
this.turnoID = turnoID;
}
public Ingreso(String id, int numero, int ingresoInicial, int ingresoFinal, int total,
TipoIngreso tipo, String turnoID) {
super(id);
this.numero = numero;
this.ingresoInicial = ingresoInicial;
this.ingresoFinal = ingresoFinal;
this.total = total;
this.tipo = tipo;
this.turnoID = turnoID;
}
public int getNumero() {
return numero;
}
public int getIngresoInicial() {
return ingresoInicial;
}
public int getIngresoFinal() {
return ingresoFinal;
}
public int getTotal() {
return total;
}
public TipoIngreso getTipo() {
return tipo;
}
public String getTurnoID() {
return turnoID;
}
@Override
public String toString() {
return "Ingreso{" +
"id='" + id + '\'' +
", numero=" + numero +
", ingresoInicial=" + ingresoInicial +
", ingresoFinal=" + ingresoFinal +
", total=" + total +
", tipo=" + tipo +
", turnoID='" + turnoID + '\'' +
'}';
}
}

View File

@@ -0,0 +1,55 @@
package xyz.danielcortes.ingresos;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import xyz.danielcortes.DAO;
import xyz.danielcortes.DB;
public class IngresoDAO extends DAO<Ingreso> {
public IngresoDAO(DB db) {
super(db, "ingreso",
List.of("id", "nro", "ingreso_inicial", "ingreso_final", "total", "tipo", "turno_id"));
}
public Ingreso findByTurnoIDAndTipo(String turnoID, TipoIngreso tipo) {
return fetchSingle(
"select * from ingreso where turno_id = ? and tipo = ?",
List.of(turnoID, tipo.toString())
);
}
public List<Ingreso> findByTurnoID(String turnoID) {
return fetchMany(
"select * from ingreso where turno_id = ?",
List.of(turnoID)
);
}
@Override
protected List<Object> getColumnsValues(Ingreso ingreso) {
return List.of(
ingreso.getId(),
ingreso.getNumero(),
ingreso.getIngresoInicial(),
ingreso.getIngresoFinal(),
ingreso.getTotal(),
ingreso.getTipo(),
ingreso.getTurnoID()
);
}
@Override
protected Ingreso mapToEntity(ResultSet rs) throws SQLException {
return new Ingreso(
rs.getString("id"),
rs.getInt("nro"),
rs.getInt("ingreso_inicial"),
rs.getInt("ingreso_final"),
rs.getInt("total"),
TipoIngreso.fromName(rs.getString("tipo")),
rs.getString("turno_id")
);
}
}

View File

@@ -0,0 +1,64 @@
package xyz.danielcortes.ingresos;
import java.util.ArrayList;
import java.util.List;
import xyz.danielcortes.DAO;
import xyz.danielcortes.Repo;
import xyz.danielcortes.egresos.Egreso;
public class IngresoRepo extends Repo<Ingreso> {
private IngresoDAO dao;
public IngresoRepo(IngresoDAO dao) {
super(dao);
this.dao = dao;
}
public List<Ingreso> getByTurnoIDAndTipo(String turnoID, TipoIngreso tipoIngreso) {
List<Ingreso> ingresos = new ArrayList<>();
for (Ingreso ingreso : toAdd.values()) {
if (ingreso.getTurnoID().equals(turnoID) && ingreso.getTipo().equals(tipoIngreso)) {
ingresos.add(ingreso);
}
}
for (Ingreso ingreso : toUpdate.values()) {
if (ingreso.getTurnoID().equals(turnoID) && ingreso.getTipo().equals(tipoIngreso)) {
ingresos.add(ingreso);
}
}
for(Ingreso ingreso : dao.findByTurnoID(turnoID)){
if(!ingresos.contains(ingreso) && !toRemove.containsKey(ingreso.getId())){
ingresos.add(ingreso);
}
}
return ingresos;
}
public List<Ingreso> getByTurnoID(String turnoID) {
List<Ingreso> ingresos = new ArrayList<>();
for (Ingreso ingreso : toAdd.values()) {
if (ingreso.getTurnoID().equals(turnoID)) {
ingresos.add(ingreso);
}
}
for (Ingreso ingreso : toUpdate.values()) {
if (ingreso.getTurnoID().equals(turnoID)) {
ingresos.add(ingreso);
}
}
for(Ingreso ingreso : dao.findByTurnoID(turnoID)){
if(!ingresos.contains(ingreso) && !toRemove.containsKey(ingreso.getId())){
ingresos.add(ingreso);
}
}
return ingresos;
}
}

View File

@@ -0,0 +1,365 @@
package xyz.danielcortes.ingresos;
import java.util.Objects;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.TextField;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import xyz.danielcortes.menubar.AppMenu;
import xyz.danielcortes.turno.TurnoToolbar;
public class IngresoScene {
private Scene scene;
private TurnoToolbar turnoToolbar;
private AppMenu menu;
public IngresoScene(TurnoToolbar turnoToolbar, AppMenu menu) {
this.turnoToolbar = turnoToolbar;
this.menu = menu;
scene = buildScene();
scene.getStylesheets().add(
Objects.requireNonNull(
getClass().getClassLoader().getResource("app.css")
).toString()
);
}
private Scene buildScene() {
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(5);
grid.setPadding(new Insets(10));
GridPane boletasFiscales = buildBoletasFiscalesComponent();
GridPane boletasManuales = buildBoletasManualesComponent();
GridPane boletasExentas = buildBoletasExentasComponent();
GridPane guias = buildGuiasComponent();
GridPane facturas = buildFacturasComponent();
GridPane totales = buildTotalesComponent();
grid.add(boletasFiscales, 0, 0);
grid.add(boletasManuales, 1, 0);
grid.add(boletasExentas, 2, 0);
grid.add(guias, 0, 1);
grid.add(facturas, 1, 1);
grid.add(totales, 2, 1);
ColumnConstraints colConstraint = new ColumnConstraints();
colConstraint.setPercentWidth(100 / 3);
grid.getColumnConstraints().add(colConstraint);
grid.getColumnConstraints().add(colConstraint);
grid.getColumnConstraints().add(colConstraint);
VBox box = new VBox(menu.getMenuBar(), turnoToolbar.getToolbar(), new Separator(), grid);
VBox.setVgrow(grid, Priority.ALWAYS);
return new Scene(box);
}
private GridPane buildBoletasFiscalesComponent() {
var numeroXField = new TextField();
var numeroZField = new TextField();
var boletaInicialField = new TextField();
var boletaFinalField = new TextField();
var totalField = new TextField();
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(2);
grid.setPadding(new Insets(5));
Label title = new Label("Boletas Fiscales");
title.setFont(Font.font("sans", FontWeight.BOLD, 13));
grid.add(title, 0, 0);
grid.add(new Label("Numero X:"), 0, 1);
grid.add(new Label("Numero Z:"), 1, 1);
grid.add(numeroXField, 0, 2);
grid.add(numeroZField, 1, 2);
grid.add(new Label("Boleta Inicial:"), 0, 3);
grid.add(boletaInicialField, 0, 4, 2, 1);
grid.add(new Label("Boleta Final:"), 0, 5);
grid.add(boletaFinalField, 0, 6, 2, 1);
grid.add(new Label("Total:"), 0, 7);
grid.add(totalField, 0, 8, 2, 1);
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(100 / 2);
grid.getColumnConstraints().add(constraints);
grid.getColumnConstraints().add(constraints);
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setMinHeight(30);
rowConstraints.setValignment(VPos.TOP);
grid.getRowConstraints().add(rowConstraints);
BorderStroke borderStroke = new BorderStroke(
Color.rgb(200, 200, 200),
BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,
BorderWidths.DEFAULT
);
grid.setBorder(new Border(borderStroke));
grid.setMaxHeight(Double.MAX_VALUE);
return grid;
}
private GridPane buildBoletasManualesComponent() {
var boletaInicialField = new TextField();
var boletaFinalField = new TextField();
var totalField = new TextField();
GridPane grid = new GridPane();
grid.setVgap(2);
grid.setPadding(new Insets(5));
Label title = new Label("Boletas Manuales");
title.setFont(Font.font("sans", FontWeight.BOLD, 13));
grid.add(title, 0, 0);
grid.add(new Label("Boleta Inicial:"), 0, 1);
grid.add(boletaInicialField, 0, 2);
grid.add(new Label("Boleta Final:"), 0, 3);
grid.add(boletaFinalField, 0, 4);
grid.add(new Label("Total:"), 0, 5);
grid.add(totalField, 0, 6);
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(100);
grid.getColumnConstraints().add(constraints);
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setMinHeight(30);
rowConstraints.setValignment(VPos.TOP);
grid.getRowConstraints().add(rowConstraints);
BorderStroke borderStroke = new BorderStroke(
Color.rgb(200, 200, 200),
BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,
BorderWidths.DEFAULT
);
grid.setBorder(new Border(borderStroke));
grid.setMaxHeight(Double.MAX_VALUE);
return grid;
}
private GridPane buildBoletasExentasComponent() {
var boletaInicialField = new TextField();
var boletaFinalField = new TextField();
var totalField = new TextField();
GridPane grid = new GridPane();
grid.setVgap(2);
grid.setPadding(new Insets(5));
Label title = new Label("Boletas Exentas");
title.setFont(Font.font("sans", FontWeight.BOLD, 13));
grid.add(title, 0, 0);
grid.add(new Label("Boleta Inicial:"), 0, 1);
grid.add(boletaInicialField, 0, 2);
grid.add(new Label("Boleta Final:"), 0, 3);
grid.add(boletaFinalField, 0, 4);
grid.add(new Label("Total:"), 0, 5);
grid.add(totalField, 0, 6);
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(100);
grid.getColumnConstraints().add(constraints);
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setMinHeight(30);
rowConstraints.setValignment(VPos.TOP);
grid.getRowConstraints().add(rowConstraints);
BorderStroke borderStroke = new BorderStroke(
Color.rgb(200, 200, 200),
BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,
BorderWidths.DEFAULT
);
grid.setBorder(new Border(borderStroke));
grid.setMaxHeight(Double.MAX_VALUE);
return grid;
}
private GridPane buildGuiasComponent() {
var guiaInicialField = new TextField();
var guiaFinalField = new TextField();
var totalField = new TextField();
GridPane grid = new GridPane();
grid.setVgap(2);
grid.setPadding(new Insets(5));
Label title = new Label("Guias");
title.setFont(Font.font("sans", FontWeight.BOLD, 13));
grid.add(title, 0, 0);
grid.add(new Label("Guia Inicial:"), 0, 1);
grid.add(guiaInicialField, 0, 2);
grid.add(new Label("Guia Final:"), 0, 3);
grid.add(guiaFinalField, 0, 4);
grid.add(new Label("Total:"), 0, 5);
grid.add(totalField, 0, 6);
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(100);
grid.getColumnConstraints().add(constraints);
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setMinHeight(30);
rowConstraints.setValignment(VPos.TOP);
grid.getRowConstraints().add(rowConstraints);
BorderStroke borderStroke = new BorderStroke(
Color.rgb(200, 200, 200),
BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,
BorderWidths.DEFAULT
);
grid.setBorder(new Border(borderStroke));
grid.setMaxHeight(Double.MAX_VALUE);
return grid;
}
private GridPane buildFacturasComponent() {
var facturaInicialField = new TextField();
var facturaFinalField = new TextField();
var totalField = new TextField();
GridPane grid = new GridPane();
grid.setVgap(2);
grid.setPadding(new Insets(5));
Label title = new Label("Facturas");
title.setFont(Font.font("sans", FontWeight.BOLD, 13));
grid.add(title, 0, 0);
grid.add(new Label("Factura Inicial:"), 0, 1);
grid.add(facturaInicialField, 0, 2);
grid.add(new Label("Factura Final:"), 0, 3);
grid.add(facturaFinalField, 0, 4);
grid.add(new Label("Total:"), 0, 5);
grid.add(totalField, 0, 6);
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(100);
grid.getColumnConstraints().add(constraints);
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setMinHeight(30);
rowConstraints.setValignment(VPos.TOP);
grid.getRowConstraints().add(rowConstraints);
BorderStroke borderStroke = new BorderStroke(
Color.rgb(200, 200, 200),
BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,
BorderWidths.DEFAULT
);
grid.setBorder(new Border(borderStroke));
grid.setMaxHeight(Double.MAX_VALUE);
return grid;
}
private GridPane buildTotalesComponent() {
var boletasFiscalesField = new TextField();
var boletasManualesField = new TextField();
var boletasExentasField = new TextField();
var guiasField = new TextField();
var facturasField = new TextField();
var totalField = new TextField();
boletasFiscalesField.setEditable(false);
boletasManualesField.setEditable(false);
boletasExentasField.setEditable(false);
guiasField.setEditable(false);
facturasField.setEditable(false);
totalField.setEditable(false);
GridPane grid = new GridPane();
grid.setVgap(2);
grid.setPadding(new Insets(5));
Label title = new Label("Totales");
title.setFont(Font.font("sans", FontWeight.BOLD, 13));
grid.add(title, 0, 0);
grid.add(new Label("Boletas Fiscales:"), 0, 1);
grid.add(boletasFiscalesField, 0, 2);
grid.add(new Label("Boletas Manuales:"), 0, 3);
grid.add(boletasManualesField, 0, 4);
grid.add(new Label("Boletas Exentas:"), 0, 5);
grid.add(boletasExentasField, 0, 6);
grid.add(new Label("Facturas:"), 0, 7);
grid.add(facturasField, 0, 8);
grid.add(new Label("Guias:"), 0, 9);
grid.add(guiasField, 0, 10);
grid.add(new Label("Total:"), 0, 11);
grid.add(totalField, 0, 12);
ColumnConstraints colConstraints = new ColumnConstraints();
colConstraints.setPercentWidth(100);
grid.getColumnConstraints().add(colConstraints);
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setMinHeight(30);
rowConstraints.setValignment(VPos.TOP);
grid.getRowConstraints().add(rowConstraints);
BorderStroke borderStroke = new BorderStroke(
Color.rgb(200, 200, 200),
BorderStrokeStyle.SOLID,
CornerRadii.EMPTY,
BorderWidths.DEFAULT
);
grid.setBorder(new Border(borderStroke));
grid.setMaxHeight(Double.MAX_VALUE);
return grid;
}
public Scene getScene() {
return scene;
}
}

View File

@@ -0,0 +1,31 @@
package xyz.danielcortes.ingresos;
import java.util.Objects;
public enum TipoIngreso {
boletasFiscales("Boletas Fiscales"),
boletasManuales("Boletas Manuales"),
facturas("Facturas"),
guias("Guías"),
boletaExenta("Boleta Exenta");
private String name;
TipoIngreso(String name) {
this.name = name;
}
public static TipoIngreso fromName(String name) {
for (TipoIngreso tipo : TipoIngreso.values()) {
if (Objects.equals(tipo.name, name)) {
return tipo;
}
}
return TipoIngreso.boletaExenta;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -0,0 +1,5 @@
package xyz.danielcortes.listeners;
public interface AddListener {
void run();
}

View File

@@ -0,0 +1,5 @@
package xyz.danielcortes.listeners;
public interface EditListener<T> {
void run(T oldT, T newT);
}

View File

@@ -0,0 +1,7 @@
package xyz.danielcortes.listeners;
import xyz.danielcortes.users.User;
public interface LoginListener {
void login(User user);
}

View File

@@ -0,0 +1,5 @@
package xyz.danielcortes.listeners;
public interface ObjectChangeListener<T> {
void run(T t);
}

View File

@@ -0,0 +1,5 @@
package xyz.danielcortes.listeners;
public interface RemoveListener<T> {
void run(T t);
}

View File

@@ -0,0 +1,5 @@
package xyz.danielcortes.listeners;
public interface SimpleListener {
void run();
}

View File

@@ -0,0 +1,87 @@
package xyz.danielcortes.menubar;
import java.util.List;
import javafx.event.EventType;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseEvent;
import xyz.danielcortes.listeners.SimpleListener;
public class AppMenu {
private MenuBar menuBar;
private MenuItem ingresosItem;
private MenuItem egresosItem;
private MenuItem arqueoItem;
private MenuItem informeEgresosItem;
private MenuItem informeArqueoItem;
private MenuItem informeLibroItem;
private MenuItem informeEstadoResultadoItem;
private MenuItem configUsuarioItem;
public AppMenu() {
this.menuBar = buildMenu();
}
private MenuBar buildMenu() {
var cajaMenu = new Menu("Caja");
ingresosItem = new MenuItem("Ingresos");
egresosItem = new MenuItem("Egresos");
arqueoItem = new MenuItem("Arqueo");
cajaMenu.getItems().add(ingresosItem);
cajaMenu.getItems().add(egresosItem);
cajaMenu.getItems().add(arqueoItem);
var informesMenu = new Menu("Informes");
informeEgresosItem = new MenuItem("Resumen Egresos");
informeArqueoItem = new MenuItem("Resumen Arqueo");
informeLibroItem = new MenuItem("Libro de Ventas");
informeEstadoResultadoItem = new MenuItem("Estado Resultado");
informesMenu.getItems().add(informeEgresosItem);
informesMenu.getItems().add(informeArqueoItem);
informesMenu.getItems().add(informeLibroItem);
informesMenu.getItems().add(informeEstadoResultadoItem);
var configMenu = new Menu("Configuracion");
configUsuarioItem = new MenuItem("Usuarios");
configMenu.getItems().add(configUsuarioItem);
var bar = new MenuBar();
bar.getMenus().add(cajaMenu);
bar.getMenus().add(informesMenu);
bar.getMenus().add(configMenu);
return bar;
}
public MenuBar getMenuBar() { return menuBar; }
public void addIngresosListener(SimpleListener listener) {
ingresosItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
public void addEgresosListener(SimpleListener listener) {
egresosItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
public void addArqueoListener(SimpleListener listener) {
arqueoItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
public void addInformeEgresosListener(SimpleListener listener) {
informeEgresosItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
public void addInformeArqueoItemListener(SimpleListener listener) {
informeArqueoItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
public void addInformeLibroListener(SimpleListener listener) {
informeLibroItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
public void addInformeEstadoResultadoListener(SimpleListener listener) {
informeEstadoResultadoItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
public void addConfigUsuarioListener(SimpleListener listener) {
configUsuarioItem.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> listener.run());
}
}

View File

@@ -0,0 +1,63 @@
package xyz.danielcortes.turno;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import xyz.danielcortes.Entity;
import xyz.danielcortes.egresos.Egreso;
public final class Turno extends Entity {
private final LocalDate fecha;
private final Integer numeroCaja;
private final Integer numeroTurno;
private final Integer fondo;
public Turno(String id, LocalDate fecha, Integer numeroCaja, Integer numeroTurno,
Integer fondo) {
super(id);
this.fecha = fecha;
this.numeroCaja = numeroCaja;
this.numeroTurno = numeroTurno;
this.fondo = fondo;
}
public Turno(LocalDate fecha, Integer numeroCaja, Integer numeroTurno, Integer fondo) {
super();
this.fecha = fecha;
this.numeroCaja = numeroCaja;
this.numeroTurno = numeroTurno;
this.fondo = fondo;
}
public String getId() {
return id;
}
public LocalDate getFecha() {
return fecha;
}
public Integer getNumeroCaja() {
return numeroCaja;
}
public Integer getNumeroTurno() {
return numeroTurno;
}
public Integer getFondo() {
return fondo;
}
@Override
public String toString() {
return "Turno{" +
"id='" + id + '\'' +
", fecha=" + fecha +
", numeroCaja=" + numeroCaja +
", numeroTurno=" + numeroTurno +
", fondo=" + fondo +
'}';
}
}

View File

@@ -0,0 +1,48 @@
package xyz.danielcortes.turno;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.DAO;
import xyz.danielcortes.DB;
public class TurnoDAO extends DAO<Turno> {
private static final Logger log = LoggerFactory.getLogger(TurnoDAO.class);
public TurnoDAO(DB db) {
super(db, "turno", List.of("id", "fecha", "numero_caja", "numero_turno", "fondo"));
}
public Turno findByFechaCajaTurno(LocalDate fecha, int numeroCaja, int numeroTurno) {
return fetchSingle(
"select * from turno where fecha = ? and numero_caja = ? and numero_turno = ?",
List.of(fecha, numeroCaja, numeroTurno)
);
}
@Override
protected List<Object> getColumnsValues(Turno turno) {
return List.of(
turno.getId(),
turno.getFecha(),
turno.getNumeroCaja(),
turno.getNumeroTurno(),
turno.getFondo()
);
}
@Override
protected Turno mapToEntity(ResultSet rs) throws SQLException {
return new Turno(
rs.getString("id"),
rs.getObject("fecha", LocalDate.class),
rs.getInt("numero_caja"),
rs.getInt("numero_turno"),
rs.getInt("fondo")
);
}
}

View File

@@ -0,0 +1,46 @@
package xyz.danielcortes.turno;
import java.time.LocalDate;
import xyz.danielcortes.Repo;
public class TurnoRepo extends Repo<Turno> {
private TurnoDAO dao;
public TurnoRepo(TurnoDAO dao) {
super(dao);
this.dao = dao;
}
public Turno getByFechaCajaTurno(LocalDate fecha, int numeroCaja, int numeroTurno) {
for (Turno turno : toAdd.values()) {
if (turno.getFecha().equals(fecha) &&
turno.getNumeroCaja() == numeroCaja &&
turno.getNumeroTurno() == numeroTurno) {
return turno;
}
}
for (Turno turno : toUpdate.values()) {
if (turno.getFecha().equals(fecha) &&
turno.getNumeroCaja() == numeroCaja &&
turno.getNumeroTurno() == numeroTurno) {
return turno;
}
}
var turno = dao.findByFechaCajaTurno(fecha, numeroCaja, numeroTurno);
if(turno == null) {
turno = new Turno(fecha,numeroCaja, numeroTurno, 0);
toAdd.put(turno.getId(), turno);
return turno;
}
if (!toRemove.containsKey(turno.getId())) {
return turno;
}
return null;
}
}

View File

@@ -0,0 +1,99 @@
package xyz.danielcortes.turno;
import java.time.LocalDate;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import xyz.danielcortes.listeners.SimpleListener;
public class TurnoToolbar {
private GridPane toolbar;
private DatePicker fecha;
private Spinner<Integer> caja;
private Spinner<Integer> turno;
private Button guardarButton;
public TurnoToolbar() {
toolbar = buildToolbar();
}
private GridPane buildToolbar() {
guardarButton = new Button("Guardar");
guardarButton.setPrefWidth(100);
fecha = new DatePicker();
caja = new Spinner<>();
turno = new Spinner<>();
fecha.setValue(LocalDate.now());
caja.setValueFactory(new IntegerSpinnerValueFactory(1, 9));
turno.setValueFactory(new IntegerSpinnerValueFactory(1, 9));
var grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setHgap(10);
grid.setVgap(5);
var spacer = new HBox();
grid.add(new Label("Dia:"), 0, 0);
grid.add(new Label("Caja:"), 1, 0);
grid.add(new Label("Turno:"), 2, 0);
grid.add(fecha, 0, 1);
grid.add(caja, 1, 1);
grid.add(turno, 2, 1);
grid.add(spacer, 3, 1);
grid.add(guardarButton, 4, 1);
GridPane.setHgrow(spacer, Priority.ALWAYS);
GridPane.setValignment(guardarButton, VPos.TOP);
return grid;
}
public GridPane getToolbar() {
return toolbar;
}
public int getCaja() {
return caja.getValue();
}
public int getTurno() {
return turno.getValue();
}
public LocalDate getFecha() {
return fecha.getValue();
}
public void onGuardar(SimpleListener listener) {
guardarButton.setOnAction(e -> listener.run());
}
public void onFechaChange(SimpleListener listener) {
fecha.valueProperty().addListener((observable, oldValue, newValue) -> {
listener.run();
});
}
public void onCajaChange(SimpleListener listener) {
caja.valueProperty().addListener((observable, oldValue, newValue) -> {
listener.run();
});
}
public void onTurnoChange(SimpleListener listener) {
turno.valueProperty().addListener((observable, oldValue, newValue) -> {
listener.run();
});
}
}

View File

@@ -0,0 +1,54 @@
package xyz.danielcortes.turno;
import java.util.LinkedList;
import java.util.List;
import xyz.danielcortes.listeners.ObjectChangeListener;
import xyz.danielcortes.listeners.SimpleListener;
public class TurnoToolbarController {
private TurnoToolbar toolbar;
private TurnoRepo repo;
private List<SimpleListener> saveListeners;
private List<ObjectChangeListener<Turno>> turnoChangeListeners;
public TurnoToolbarController(TurnoToolbar toolbar, TurnoRepo turnoRepo) {
this.toolbar = toolbar;
this.repo = turnoRepo;
this.saveListeners = new LinkedList<>();
this.turnoChangeListeners = new LinkedList<>();
setupListeners();
}
private void setupListeners() {
this.toolbar.onGuardar(this::notifySaveListeners);
this.toolbar.onFechaChange(this::loadNewTurno);
this.toolbar.onCajaChange(this::loadNewTurno);
this.toolbar.onTurnoChange(this::loadNewTurno);
}
private void loadNewTurno() {
var turno = repo.getByFechaCajaTurno(toolbar.getFecha(), toolbar.getCaja(), toolbar.getTurno());
notifyTurnoChangeListeners(turno);
}
public void addTurnoChangeListener(ObjectChangeListener<Turno> listener) {
turnoChangeListeners.add(listener);
}
public void addSaveListener(SimpleListener listener) {
saveListeners.add(listener);
}
public void notifySaveListeners() {
saveListeners.forEach(SimpleListener::run);
}
public void notifyTurnoChangeListeners(Turno turno) {
turnoChangeListeners.forEach(listener -> listener.run(turno));
}
}

View File

@@ -0,0 +1,69 @@
package xyz.danielcortes.users;
import java.util.LinkedList;
import java.util.List;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.stage.StageStyle;
import xyz.danielcortes.listeners.LoginListener;
public class LoginController {
private UserRepo userRepo;
private LoginScene loginScene;
private List<LoginListener> loginListeners;
public LoginController(LoginScene loginScene, UserRepo userRepo) {
this.loginListeners = new LinkedList<>();
this.loginScene = loginScene;
this.userRepo = userRepo;
this.loginScene.onLogin(this::onLogin);
}
public void addLoginListener(LoginListener loginListener) {
loginListeners.add(loginListener);
}
public void onLogin() {
Alert alert = new Alert(AlertType.ERROR);
alert.initStyle(StageStyle.DECORATED);
alert.setHeaderText(null);
alert.setTitle("Error");
alert.setContentText("El usuario o contraseña no son correctos");
if (loginScene.getUsername().length() <= 0) {
loginScene.clearError();
loginScene.inputUsernameError();
loginScene.focusUsername();
return;
}
if (loginScene.getPassword().length <= 0) {
loginScene.clearError();
loginScene.inputPasswordError();
loginScene.focusPassword();
return;
}
User user = userRepo.getByNombre(loginScene.getUsername());
if (user == null) {
alert.showAndWait();
loginScene.focusUsername();
return;
}
if (!user.comparePassword(loginScene.getPassword())) {
alert.showAndWait();
loginScene.focusPassword();
return;
}
notifyLoginListeners(user);
}
private void notifyLoginListeners(User user) {
loginListeners.forEach(l -> l.login(user));
}
}

View File

@@ -0,0 +1,110 @@
package xyz.danielcortes.users;
import java.util.Objects;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import xyz.danielcortes.listeners.SimpleListener;
public class LoginScene {
private Scene scene;
private TextField usernameField;
private PasswordField passwordField;
private Button loginButton;
public LoginScene() {
scene = buildScene();
scene.getStylesheets().add(
Objects.requireNonNull(
getClass().getClassLoader().getResource("app.css")
).toString()
);
}
public Scene buildScene() {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
var title = new Text("Bienvenido");
title.setFont(Font.font("Raleway", FontWeight.NORMAL, 20));
grid.add(title, 0, 0, 2, 1);
var username = new Label("Usuario:");
var usernameBox = new VBox();
usernameBox.getChildren().add(username);
usernameBox.setAlignment(Pos.BASELINE_RIGHT);
grid.add(usernameBox, 0, 1);
usernameField = new TextField();
grid.add(usernameField, 1, 1);
var password = new Label("Password:");
var passwordBox = new VBox();
passwordBox.getChildren().add(password);
passwordBox.setAlignment(Pos.BASELINE_RIGHT);
grid.add(password, 0, 2);
passwordField = new PasswordField();
grid.add(passwordField, 1, 2);
loginButton = new Button("Log in");
var buttonHBox = new HBox(10);
buttonHBox.setAlignment(Pos.BASELINE_RIGHT);
buttonHBox.getChildren().add(loginButton);
grid.add(buttonHBox, 1, 4);
var action = new Text();
grid.add(action, 1, 6);
return new Scene(grid, 400, 300);
}
public void onLogin(SimpleListener action) {
loginButton.setOnAction(e -> action.run());
}
public Scene getScene() {
return scene;
}
public String getUsername() {
return usernameField.getText();
}
public char[] getPassword() {
return passwordField.getText().toCharArray();
}
public void focusUsername() {
usernameField.requestFocus();
}
public void focusPassword() {
passwordField.requestFocus();
}
public void inputUsernameError(){
usernameField.getStyleClass().add("error");
}
public void inputPasswordError() {
passwordField.getStyleClass().add("error");
}
public void clearError() {
usernameField.getStyleClass().remove("error");
passwordField.getStyleClass().remove("error");
}
}

View File

@@ -0,0 +1,73 @@
package xyz.danielcortes.users;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
/**
* Clase encargada de crear y comparar contraseñas de forma segura
*/
public class Passwords {
private static final Random RANDOM = new SecureRandom();
private static final int ITERATIONS = 10000;
private static final int KEY_LENGHT = 256;
private Passwords() {
}
/**
* Genera aleatoriamente un <code>byte[]</code> de tamaño 16
*
* @return El <code>byte[]</code> generados
*/
public static byte[] getNextSalt() {
byte[] salt = new byte[16];
RANDOM.nextBytes(salt);
return salt;
}
/**
* Compara 2 contraseñas, la primera sin hashear aun y la segunda ya hasheada
*
* @param password <code>char[]</code> que contiene la contraseña sin hashear aun
* @param salt <code>byte[]</code> que contiene la sal con la que juntar la contraseña al momento de hashearla
* @param expectedHash <code>byte[]</code> con una contraseña ya hasheada con la que comprar
* @return un boleano indicando que las contraseñas son iguales o no
*/
public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash) {
byte[] pwdHash = hash(password, salt);
Arrays.fill(password, Character.MIN_VALUE);
if (pwdHash.length != expectedHash.length)
return false;
for (int i = 0; i < pwdHash.length; i++) {
if (pwdHash[i] != expectedHash[i])
return false;
}
return true;
}
/**
* Hashea una contraseña.
*
* @param password <code>char[]</code> con la contraseña a hashear
* @param salt <code>byte[]</code> con la salt que se le aplicara a la contraseña
* @return <code>byte[]</code> con la contraseña hasheada
*/
public static byte[] hash(char[] password, byte[] salt) {
PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGHT);
Arrays.fill(password, Character.MIN_VALUE);
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
return skf.generateSecret(spec).getEncoded();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
} finally {
spec.clearPassword();
}
}
}

View File

@@ -0,0 +1,75 @@
package xyz.danielcortes.users;
import java.util.UUID;
import xyz.danielcortes.Entity;
import xyz.danielcortes.egresos.Egreso;
public final class User extends Entity {
private final String nombre;
private final byte[] password;
private final byte[] salt;
public User(String nombre, char[] password) {
super();
this.nombre = nombre;
this.salt = Passwords.getNextSalt();
this.password = Passwords.hash(password, salt);
}
public User(String id, String nombre, byte[] password, byte[] salt) {
super(id);
this.nombre = nombre;
this.password = password;
this.salt = salt;
}
public final String getId() {
return id;
}
public final String getNombre() {
return nombre;
}
public final byte[] getPassword() {
return password;
}
public final byte[] getSalt() {
return salt;
}
public final boolean comparePassword(char[] password) {
return Passwords.isExpectedPassword(password, this.salt, this.password);
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", nombre='" + nombre + '\'' +
'}';
}
public int hashCode() {
if ( id != null )
return id.hashCode();
else
return super.hashCode();
}
public boolean equals(Object o) {
if ( this == o )
return true;
if ( !(o instanceof Egreso) )
return false;
Egreso other = (Egreso) o;
if ( id == null )
return false;
return id.equals(other.getId());
}
}

View File

@@ -0,0 +1,43 @@
package xyz.danielcortes.users;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.DAO;
import xyz.danielcortes.DB;
import xyz.danielcortes.egresos.EgresoDAO;
public class UserDAO extends DAO<User> {
private static Logger log = LoggerFactory.getLogger(EgresoDAO.class);
public UserDAO(DB db) {
super(db, "user", List.of("id", "nombre", "password", "salt"));
}
public User findByNombre(String nombre) {
return fetchSingle("select * from usuario where nombre = ?", List.of(nombre));
}
@Override
protected List<Object> getColumnsValues(User user) {
return List.of(
user.getId(),
user.getNombre(),
user.getPassword(),
user.getSalt()
);
}
@Override
protected User mapToEntity(ResultSet rs) throws SQLException {
return new User(
rs.getString("id"),
rs.getString("nombre"),
rs.getBytes("password"),
rs.getBytes("salt")
);
}
}

View File

@@ -0,0 +1,44 @@
package xyz.danielcortes.users;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.danielcortes.Repo;
public class UserRepo extends Repo<User> {
private static Logger log = LoggerFactory.getLogger(UserRepo.class);
private UserDAO dao;
public UserRepo(UserDAO dao) {
super(dao);
this.dao = dao;
}
public User getByNombre(String nombre) {
for (var user : toAdd.values()) {
if (user.getNombre().equals(nombre)) {
return user;
}
}
for (var user : toUpdate.values()) {
if (user.getNombre().equals(nombre)) {
return user;
}
}
var user = dao.findByNombre(nombre);
if (user != null && !toRemove.containsKey(user.getId())) {
return user;
}
return null;
}
}

24
src/main/resources/app.css Executable file
View File

@@ -0,0 +1,24 @@
.text-input.error {
-fx-focus-color: #d35244;
-fx-faint-focus-color: #d3524422;
-fx-highlight-fill: -fx-accent;
-fx-highlight-text-fill: white;
-fx-background-color: -fx-focus-color,
-fx-control-inner-background,
-fx-faint-focus-color,
linear-gradient(
from 0px 0px to 0px 5px,
-fx-control-inner-background,
-fx-control-inner-background
);
-fx-background-insets: -0.2, 1, -1.4, 3;
-fx-background-radius: 3, 2, 4, 0;
-fx-prompt-text-fill: transparent;
}
.border {
-fx-border-color: -fx-text-box-border -fx-text-box-border -fx-text-box-border -fx-text-box-border,
-fx-shadow-highlight-color -fx-shadow-highlight-color -fx-shadow-highlight-color -fx-shadow-highlight-color;
-fx-border-insets: 0, 1 0 0 0;
}

20
src/main/resources/logback.xml Executable file
View File

@@ -0,0 +1,20 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%4level %logger{0}] %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>file.log</file>
<append>false</append>
<encoder>
<pattern>%4level %logger{0} - %msg%n</pattern>
</encoder>
</appender>
<logger name="xyz.danielcortes" level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="file"/>
</logger>
</configuration>

58
src/sql/database.sql Executable file
View File

@@ -0,0 +1,58 @@
drop database if exists simplefx;
create database simplefx;
use simplefx;
create table turno
(
id char(36) not null,
fecha date not null,
numero_caja int not null,
numero_turno int not null,
fondo int not null default 0,
inserted_at timestamp not null default now(),
modified_at timestamp not null default now() on update now(),
constraint unique_turno_fecha_caja_turno unique (fecha, numero_caja, numero_turno),
primary key (id)
);
create table egreso
(
id char(36) not null,
nro varchar(255) not null default '',
descripcion varchar(255) not null default '',
valor int not null default 0,
tipo varchar(255) not null default 'Otro',
turno_id char(36) not null,
inserted_at timestamp not null default now(),
modified_at timestamp not null default now() on update now(),
foreign key (turno_id) references turno (id),
primary key (id)
);
create table ingreso
(
id char(36) not null,
nro int null default '0',
ingreso_inicial int not null,
ingreso_final int not null,
total int not null,
tipo_ingreso varchar(255) not null,
turno_id char(36) not null,
inserted_at timestamp not null default now(),
modified_at timestamp not null default now() on update now(),
constraint unique_ingreso_turno_tipo unique (turno_id, tipo_ingreso),
foreign key (turno_id) references turno (id),
primary key (id)
);
create table usuario
(
id char(36) not null,
nombre varchar(255) not null,
password binary(32) not null,
salt binary(16) not null,
created_at timestamp default now(),
modified_at timestamp default now() on update now(),
primary key (id)
);