Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,19 +160,34 @@ Para ejecutarlo desde IntelliJ IDEA, añade estas opciones de VM en la configura

## Capturas de pantalla

> 📸 *Próximamente*

<!-- Pantalla de login -->
<!-- ![Login](images/screenshot_login.png) -->

<!-- Pantalla de ventas -->
<!-- ![Ventas](images/screenshot_ventas.png) -->

<!-- Gestión de productos -->
<!-- ![Productos](images/screenshot_productos.png) -->

<!-- Ajustes -->
<!-- ![Ajustes](images/screenshot_ajustes.png) -->
<table>
<tr>
<td align="center">
<img src="landing/public/images/screenshots/screenshot-ventas.webp" alt="Pantalla de ventas de FreeTPV" width="420">
<br>
<sub>Ventas</sub>
</td>
<td align="center">
<img src="landing/public/images/screenshots/screenshot-cobro.webp" alt="Pantalla de cobro de FreeTPV" width="420">
<br>
<sub>Cobro</sub>
</td>
</tr>
<tr>
<td align="center">
<img src="landing/public/images/screenshots/screenshot-productos.webp" alt="Gestión de productos en FreeTPV" width="420">
<br>
<sub>Productos</sub>
</td>
<td align="center">
<img src="landing/public/images/screenshots/screenshot-ajustes.webp" alt="Pantalla de ajustes de FreeTPV" width="420">
<br>
<sub>Ajustes</sub>
</td>
</tr>
</table>

> También hay capturas en modo oscuro disponibles en la [web del proyecto](https://freetpv.pages.dev).

---

Expand Down
14 changes: 14 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@
<version>4.1.0</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.4</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.4</version>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void initialize() {
600
);

ImprimirService.imprimirTicket(impresora.get(), datosTicket);
ImprimirService.imprimirTicket(impresora.get(), datosTicket, ajustesService.getImpresoraAncho(), ajustesService.getImpresoraCodepage(), ajustesService.getImpresoraCortarPapel());
} catch (IOException e) {
log.error("No se pudo imprimir una prueba", e);
}
Expand Down
46 changes: 35 additions & 11 deletions src/main/java/com/mateo/freetpv/controller/LoadController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import com.mateo.freetpv.FreeTPVApplication;
import com.mateo.freetpv.dao.UsuarioDAO;
import com.mateo.freetpv.service.AjustesService;
import com.mateo.freetpv.service.BackupService;
import com.mateo.freetpv.util.DatabaseConnection;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -19,18 +24,13 @@
public class LoadController {

private static final Logger log = LoggerFactory.getLogger(LoadController.class);
@FXML
private TableColumn<?, ?> cantidadTableColumn;

@FXML
private Button crearButton;

@FXML
private Button importarButton;

@FXML
private TableView<?> importarTableView;

@FXML
private PasswordField newPinField;

Expand All @@ -40,18 +40,19 @@ public class LoadController {
@FXML
private Label errorLabel;

@FXML
private TableColumn<?, ?> resultadoTableColumn;

@FXML
private Button seleccionarButton;

@FXML
private TableColumn<?, ?> verTableColumn;
private Label seleccionadoLabel;

private final String path = System.getProperty("user.home") + "/.freetpv";

private UsuarioDAO usuarioDAO = new UsuarioDAO();
private final UsuarioDAO usuarioDAO = new UsuarioDAO();

private final BackupService backupService = new BackupService();

private File archivoSeleccionado;

public void initialize() {
new File(path).mkdirs();
Expand All @@ -72,6 +73,29 @@ public void initialize() {
Platform.runLater(() -> cargarLogin());
}

@FXML
public void seleccionarBackup() {
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(new FileChooser.ExtensionFilter("Copia de seguridad", "*.tpv"));
File archivo = fc.showOpenDialog(crearButton.getScene().getWindow());
if (archivo != null && archivo.exists()) {
archivoSeleccionado = archivo;
seleccionadoLabel.setText("Has seleccionado " + archivo.getName());
importarButton.setDisable(false);
}
}

@FXML
public void importarBackup() {
if (archivoSeleccionado == null) return;

if (backupService.restaurarBackup(archivoSeleccionado)) {
initialize();
} else {
seleccionadoLabel.setText("No se ha podido importar la copia de seguridad");
}
}

private void cargarLogin() {
try {
// Cargar ventana principal
Expand Down
11 changes: 5 additions & 6 deletions src/main/java/com/mateo/freetpv/controller/VentasController.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,10 @@ public void seleccionarTarjeta() {
Optional<PrintService> impresora = buscarImpresora();
if (impresora.isPresent()) {
try {
ImprimirService.imprimirTicket(impresora.get(), datosTicket);
ImprimirService.abrirCajon(impresora.get());
ImprimirService.imprimirTicket(impresora.get(), datosTicket, ajustesService.getImpresoraAncho(), ajustesService.getImpresoraCodepage(), ajustesService.getImpresoraCortarPapel());
if (ajustesService.getImpresoraAbrirCajon()) {
ImprimirService.abrirCajon(impresora.get());
}
} catch (IOException e) {
log.error("Error al imprimir ticket", e);
}
Expand All @@ -296,10 +298,7 @@ public void seleccionarTarjeta() {
Optional<PrintService> impresora = buscarImpresora();
if (impresora.isPresent()) {
try {
ImprimirService.imprimirTicket(impresora.get(), datosTicket);
if (ajustesService.getImpresoraAbrirCajon()) {
ImprimirService.abrirCajon(impresora.get());
}
ImprimirService.imprimirTicket(impresora.get(), datosTicket, ajustesService.getImpresoraAncho(), ajustesService.getImpresoraCodepage(), ajustesService.getImpresoraCortarPapel());
} catch (IOException e) {
log.error("Error al imprimir preticket", e);
}
Expand Down
40 changes: 35 additions & 5 deletions src/main/java/com/mateo/freetpv/service/ImprimirService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@
import com.github.anastaciocintra.escpos.EscPos;
import com.github.anastaciocintra.escpos.EscPosConst;
import com.github.anastaciocintra.escpos.Style;
import com.github.anastaciocintra.escpos.image.BitonalThreshold;
import com.github.anastaciocintra.escpos.image.CoffeeImageImpl;
import com.github.anastaciocintra.escpos.image.EscPosImage;
import com.github.anastaciocintra.escpos.image.GraphicsImageWrapper;
import com.github.anastaciocintra.output.PrinterOutputStream;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.mateo.freetpv.model.DatosTicket;
import com.mateo.freetpv.model.LineaTicket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.print.PrintService;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
Expand All @@ -21,11 +31,14 @@
import static com.mateo.freetpv.util.ConversionUtil.centimosEuros;

public class ImprimirService {
private static final int WIDTH = 32;
private static int WIDTH = 32;

private static final Logger log = LoggerFactory.getLogger(ImprimirService.class);

public static void imprimirTicket(PrintService printService, DatosTicket ticket) throws IOException {
public static void imprimirTicket(PrintService printService, DatosTicket ticket, int ancho, String codepage, boolean cortar) throws IOException {
WIDTH = ancho == 58 ? 32 : 48;


PrinterOutputStream printerOutputStream = new PrinterOutputStream(printService);
EscPos escpos = new EscPos(printerOutputStream);

Expand All @@ -48,9 +61,12 @@ public static void imprimirTicket(PrintService printService, DatosTicket ticket)
String hora = ahora.format(DateTimeFormatter.ofPattern("HH:mm"));

escpos.initializePrinter();
EscPos.CharacterCodeTable charset = switch (codepage) {
case "CP850" -> EscPos.CharacterCodeTable.CP850_Multilingual;
default -> EscPos.CharacterCodeTable.CP858_Euro;
};
escpos.setCharacterCodeTable(charset);

// Charset / codepage para tildes y ñ
escpos.setCharacterCodeTable(EscPos.CharacterCodeTable.CP858_Euro);

escpos.feed(1);
escpos.writeLF(centerBold, ticket.nombreEmpresa());
Expand Down Expand Up @@ -131,8 +147,22 @@ public static void imprimirTicket(PrintService printService, DatosTicket ticket)
escpos.feed(1);
if (ticket.mostrarWeb()) escpos.writeLF(center, ticket.web());

if (ticket.mostrarQr() && !ticket.qr().isBlank()) {
try {
BitMatrix bitMatrix = new QRCodeWriter().encode(ticket.qr(), BarcodeFormat.QR_CODE, 200, 200);
BufferedImage qrImage = MatrixToImageWriter.toBufferedImage(bitMatrix);

GraphicsImageWrapper imageWrapper = new GraphicsImageWrapper();
imageWrapper.setJustification(EscPosConst.Justification.Center);
EscPosImage escposImage = new EscPosImage(new CoffeeImageImpl(qrImage), new BitonalThreshold());
escpos.write(imageWrapper, escposImage);
} catch (WriterException e) {
log.error("Error al dibujar el código QR", e);
}
}

escpos.feed(2);
escpos.cut(EscPos.CutMode.FULL);
if (cortar) escpos.cut(EscPos.CutMode.FULL);
escpos.close();
}

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
requires bcrypt;
requires java.desktop;
requires escpos.coffee;
requires com.google.zxing;
requires com.google.zxing.javase;


opens com.mateo.freetpv.model to javafx.base;
opens com.mateo.freetpv.controller to javafx.fxml;
Expand Down
26 changes: 10 additions & 16 deletions src/main/resources/com/mateo/freetpv/view/load-view.fxml
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import org.kordamp.ikonli.javafx.*?>
<?import java.lang.String?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<?import org.kordamp.ikonli.javafx.FontIcon?>

<StackPane maxHeight="500.0" maxWidth="850.0" minHeight="500.0" minWidth="800.0" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mateo.freetpv.controller.LoadController">
<children>
<BorderPane prefHeight="200.0" prefWidth="200.0">
Expand Down Expand Up @@ -115,12 +106,15 @@
</Text>
</children>
</HBox>
<Button fx:id="seleccionarButton" graphicTextGap="8.0" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Seleccionar archivo .tpv">
<Button fx:id="seleccionarButton" graphicTextGap="8.0" maxWidth="1.7976931348623157E308"
mnemonicParsing="false" onAction="#seleccionarBackup" text="Seleccionar archivo .tpv">
<graphic>
<FontIcon iconLiteral="far-folder" iconSize="16" />
</graphic>
</Button>
<Button fx:id="importarButton" graphicTextGap="8.0" maxWidth="1.7976931348623157E308" mnemonicParsing="false" styleClass="accent" text="Importar copia">
<Label fx:id="seleccionadoLabel"/>
<Button fx:id="importarButton" disable="true" graphicTextGap="8.0" maxWidth="1.7976931348623157E308"
mnemonicParsing="false" onAction="#importarBackup" styleClass="accent" text="Importar copia">
<VBox.margin>
<Insets />
</VBox.margin>
Expand Down
Loading