Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa3f641bf2 | |||
| d4f89f39bd | |||
| 91a6c4a304 | |||
| ed648a2740 | |||
| 6330d7fd51 | |||
| c241964a8b | |||
| f3e0da2689 | |||
| 4421075bec | |||
| 71c79575c8 | |||
| 1866782111 | |||
| a7e76e00d3 | |||
| 1cadfe5bfc | |||
| c40be1c203 | |||
| 02026633ed | |||
| c4297a4be2 | |||
| fd52d3df6d | |||
| c320ef615d | |||
| 2a20ea7bdb | |||
| a56ad43852 | |||
| 188214dfbe | |||
| cdbe2cccc0 | |||
| 5152846478 | |||
| f5c5d546c5 | |||
| 5c9d54dabf |
@@ -0,0 +1,4 @@
|
||||
target/
|
||||
dependency-reduced-pom.xml
|
||||
tools/*.db
|
||||
tools/*.html
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="21">
|
||||
<profile kind="CodeFormatterProfile" name="jFxKasse" version="21">
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"vscjava.vscode-java-pack",
|
||||
"vscjava.vscode-maven",
|
||||
"redhat.java"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Run jFxKasse",
|
||||
"request": "launch",
|
||||
"mainClass": "com.jFxKasse.application.JavaFX11Main",
|
||||
"projectName": "jFxKasse",
|
||||
"vmArgs": "--module-path ${env:PATH_TO_FX} --add-modules javafx.controls,javafx.fxml --add-opens javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED"
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Run jFxKasse (Maven)",
|
||||
"request": "launch",
|
||||
"mainClass": "com.jFxKasse.application.JavaFX11Main",
|
||||
"projectName": "jFxKasse"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"java.project.sourcePaths": [
|
||||
"src/main/java"
|
||||
],
|
||||
"java.project.outputPath": "target/classes",
|
||||
"java.project.referencedLibraries": [],
|
||||
"java.configuration.updateBuildConfiguration": "automatic",
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
"editor.formatOnSave": true,
|
||||
"files.encoding": "utf8",
|
||||
|
||||
// Java formatter: point to workspace-local Eclipse formatter profile
|
||||
"java.format.settings.url": ".vscode/eclipse-formatter.xml",
|
||||
"java.format.settings.profile": "jFxKasse",
|
||||
"[java]": {
|
||||
"editor.defaultFormatter": "redhat.java"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "mvn compile",
|
||||
"type": "shell",
|
||||
"command": "mvn compile",
|
||||
"group": "build",
|
||||
"problemMatcher": [
|
||||
"$javac"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "mvn package",
|
||||
"type": "shell",
|
||||
"command": "mvn package -DskipTests",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$javac"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "mvn clean",
|
||||
"type": "shell",
|
||||
"command": "mvn clean",
|
||||
"group": "build",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "mvn javafx:run",
|
||||
"type": "shell",
|
||||
"command": "mvn javafx:run",
|
||||
"group": "build",
|
||||
"problemMatcher": [
|
||||
"$javac"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "mvn test",
|
||||
"type": "shell",
|
||||
"command": "mvn test",
|
||||
"group": "test",
|
||||
"problemMatcher": [
|
||||
"$javac"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,2 +1,82 @@
|
||||
# jFxKasse
|
||||
easy payment system for small to middel sized events with a sales slip printer
|
||||
|
||||
Simple POS (Point of Sale) system for small to medium-sized events with receipt printer support.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java JDK 21+
|
||||
- Maven 3.9+
|
||||
- Display >= 1366x768
|
||||
|
||||
## Video Tutorials (German | Deutsch)
|
||||
[YouTube Video: jFxKasse - Kassensystem - Kurzanleitung](https://www.youtube.com/watch?v=DV9DDESw40I)
|
||||
|
||||
[YouTube Video: jFxKasse - Kassensystem - Installieren](https://www.youtube.com/watch?v=IY1bqRjwh0Q)
|
||||
|
||||
## Setup (VSCode on openSUSE Tumbleweed)
|
||||
|
||||
### 1. Install dependencies
|
||||
|
||||
```bash
|
||||
sudo zypper install java-21-openjdk java-21-openjdk-devel maven
|
||||
```
|
||||
|
||||
### 2. Install VSCode extensions
|
||||
|
||||
Open the project in VSCode. You will be prompted to install the recommended extensions:
|
||||
|
||||
- **Extension Pack for Java** (`vscjava.vscode-java-pack`)
|
||||
- **Maven for Java** (`vscjava.vscode-maven`)
|
||||
- **Language Support for Java** (`redhat.java`)
|
||||
|
||||
### 3. Build & Run
|
||||
|
||||
```bash
|
||||
# Compile
|
||||
mvn compile
|
||||
|
||||
# Run via Maven
|
||||
mvn javafx:run
|
||||
|
||||
# Package as fat JAR
|
||||
mvn package
|
||||
java -jar target/jFxKasse-shaded.jar
|
||||
```
|
||||
|
||||
Or use the preconfigured VSCode tasks (`Ctrl+Shift+B`) and launch configurations (`F5`).
|
||||
|
||||
## Screenshots
|
||||
|
||||
| Main View | Jobs | Positions | Settings |
|
||||
|-----------|------|-----------|----------|
|
||||
|  |  |  |  |
|
||||
|
||||
## Hardware
|
||||
|
||||
Tested with: [Epson TM T20II](https://www.epson.de/products/sd/pos-printer/epson-tm-t20ii). Other receipt printers should work as well.
|
||||
|
||||
## Tools
|
||||
### merge_db
|
||||
|
||||
Merges multiple SQLite databases into one file with automatic deduplication.
|
||||
Reference tables (`category`, `positionen`) are deduplicated by content; `jobs` rows are appended in order with re-sequenced IDs to avoid conflicts.
|
||||
|
||||
```bash
|
||||
python tools/merge_db.py -o OUTPUT_DB INPUT_DB [INPUT_DB ...]
|
||||
|
||||
# Example
|
||||
python tools/merge_db.py -o merged.db 02.db 03.db 04.db
|
||||
```
|
||||
|
||||
### generate_report
|
||||
|
||||
Generates a German HTML + PDF report from a [jFxKasse](https://git.mosad.xyz/localhorst/jFxKasse) SQLite database, including category/item tables, order KPIs, revenue breakdowns, and hourly trend charts per category.
|
||||
|
||||
```bash
|
||||
python tools/generate_report.py kassendaten.db # → kassendaten_bericht.html
|
||||
python tools/generate_report.py kassendaten.db -o out.html
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
GPL-3.0
|
||||
@@ -1,42 +1,49 @@
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com</groupId>
|
||||
<artifactId>jFxKasse</artifactId>
|
||||
<version>0.3.0</version>
|
||||
<version>0.4.0</version>
|
||||
<name>jFxKasse</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<javafx.version>23.0.1</javafx.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>6.0.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>11</version>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>11</version>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jfoenix</groupId>
|
||||
<artifactId>jfoenix</artifactId>
|
||||
<version>9.0.8</version>
|
||||
<version>9.0.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.23.1</version>
|
||||
<version>3.51.2.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -46,11 +53,11 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<version>3.15.0</version>
|
||||
<configuration>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
<!--<release>11</release> -->
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<release>21</release>
|
||||
<showWarnings>true</showWarnings>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
</configuration>
|
||||
@@ -59,7 +66,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<version>3.6.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -71,17 +78,16 @@
|
||||
<mainClass>com.jFxKasse.application.Main</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>3.6.1</version>
|
||||
<configuration>
|
||||
<finalName>jFxKasse</finalName>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.jFxKasse.application.JavaFX11Main</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
@@ -95,7 +101,16 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-maven-plugin</artifactId>
|
||||
<version>0.0.8</version>
|
||||
<configuration>
|
||||
<mainClass>com.jFxKasse.application.JavaFX11Main</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 83 KiB |
@@ -1,12 +1,8 @@
|
||||
package com.jFxKasse.application;
|
||||
|
||||
import com.jFxKasse.application.Main;
|
||||
public class JavaFX11Main {
|
||||
|
||||
public class JavaFX11Main
|
||||
{
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Main.main(args);
|
||||
public static void main(String[] args) {
|
||||
Main.main(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +1,153 @@
|
||||
package com.jFxKasse.application;
|
||||
|
||||
import javafx.application.Application;
|
||||
import java.io.File;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
import com.jFxKasse.controller.MainWindowController;
|
||||
import com.jFxKasse.controller.PrinterController;
|
||||
import com.jFxKasse.controller.XMLController;
|
||||
import com.jFxKasse.controller.DBController;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
|
||||
public class Main extends Application
|
||||
{
|
||||
// path to config.xml and the database
|
||||
public static String filepath;
|
||||
|
||||
private static String osName = System.getProperty("os.name");
|
||||
|
||||
private static String userHome = System.getProperty("user.home");
|
||||
|
||||
private MainWindowController mwc;
|
||||
|
||||
private XMLController xmlc = new XMLController(filepath);
|
||||
|
||||
private DBController dbc = new DBController(filepath);
|
||||
|
||||
private PrinterController pc = new PrinterController();
|
||||
|
||||
private Stage primaryStage;
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage)
|
||||
{
|
||||
this.primaryStage = primaryStage;
|
||||
System.out.println("\nstarting jFxKasse\n");
|
||||
mainWindow();
|
||||
}
|
||||
|
||||
private void mainWindow()
|
||||
{
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(
|
||||
getClass().getResource("/fxml/MainWindow.fxml"));
|
||||
|
||||
AnchorPane pane = loader.load();
|
||||
primaryStage.setTitle("jFxKasse"); // Title of window
|
||||
|
||||
mwc = loader.getController(); // set the mwc as the JavaFx
|
||||
// MainWindowController
|
||||
pc.searchPrinters(); // search for available printers
|
||||
mwc.setMain(this, dbc, xmlc, pc); // set the created instances to the
|
||||
// mwc
|
||||
|
||||
firstStart(); // test if this is the first run
|
||||
|
||||
Scene scene = new Scene(pane);
|
||||
scene.getStylesheets().add(
|
||||
Main.class.getResource("/css/application.css").toExternalForm());
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show(); // shows the stage
|
||||
|
||||
Timeline timeline = new Timeline(
|
||||
new KeyFrame(Duration.seconds(1), ev -> {
|
||||
mwc.updateTimeLabel(); // update time label on UI
|
||||
}));
|
||||
timeline.setCycleCount(Animation.INDEFINITE);
|
||||
timeline.play();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
if (osName.contains("Windows")) {
|
||||
System.out.println("FCK Windows");
|
||||
filepath = userHome + "/Documents/jFxKasse/";
|
||||
} else {
|
||||
filepath = userHome + "/jFxKasse/";
|
||||
}
|
||||
launch(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the config.xml is preset.
|
||||
*/
|
||||
private void firstStart() throws Exception
|
||||
{
|
||||
if (xmlc.loadSettings()) {
|
||||
// config.xml found, app starting normal
|
||||
System.out.println("XML found!");
|
||||
mwc.initUI(); // Starting the UI elements
|
||||
mwc.setDBLabel(); // Set databese labels
|
||||
dbc.setDbname(xmlc.getDatabaseName()); // handover database name
|
||||
dbc.connectDatabase(); // estabishing DB conection
|
||||
mwc.fillTablePositionen(); // fill TreeTable 'Positionen'
|
||||
mwc.fillCategory();
|
||||
mwc.fillPrinterSettings();
|
||||
mwc.fillTableJobs();
|
||||
mwc.loadGridButtons();
|
||||
mwc.getSelectedCat(); // Load DB entries in Chois Box
|
||||
mwc.createNewJob();
|
||||
} else {
|
||||
// config.xml NOT found, first start of app
|
||||
System.out.println("no XML found!");
|
||||
xmlc.initXML(); // set default values
|
||||
mwc.blockUI(true); // disable UI elements that need DB
|
||||
mwc.blockUnlock();
|
||||
File dir = new File(filepath);
|
||||
dir.mkdir(); // Create new Subfolder
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.jFxKasse.application;
|
||||
|
||||
import com.jFxKasse.controller.MainWindowController;
|
||||
import com.jFxKasse.controller.PrinterController;
|
||||
import com.jFxKasse.controller.XMLController;
|
||||
import com.jFxKasse.controller.DBController;
|
||||
import com.jFxKasse.controller.KeyController;
|
||||
|
||||
import javafx.application.Application;
|
||||
import java.io.File;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
|
||||
public class Main extends Application {
|
||||
// Path to config.xml and the database
|
||||
public static String filepath;
|
||||
|
||||
private static String osName = System.getProperty("os.name");
|
||||
|
||||
private static String userHome = System.getProperty("user.home");
|
||||
|
||||
private MainWindowController mwc;
|
||||
|
||||
// Initialized in start() after filepath is set by main()
|
||||
private XMLController xmlc;
|
||||
|
||||
private DBController dbc;
|
||||
|
||||
private PrinterController pc = new PrinterController();
|
||||
|
||||
private KeyController kc;
|
||||
|
||||
private Stage primaryStage;
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
this.primaryStage = primaryStage;
|
||||
// filepath is set by main() before launch(); initialize controllers here
|
||||
xmlc = new XMLController(filepath);
|
||||
dbc = new DBController(filepath);
|
||||
System.out.println("\nstarting jFxKasse\n");
|
||||
mainWindow();
|
||||
}
|
||||
|
||||
private void mainWindow() {
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/MainWindow.fxml"));
|
||||
|
||||
AnchorPane pane = loader.load();
|
||||
primaryStage.setTitle("jFxKasse"); // Window title
|
||||
|
||||
mwc = loader.getController(); // Set the MainWindowController
|
||||
pc.searchPrinters(); // Search for available printers
|
||||
mwc.setMain(this, dbc, xmlc, pc); // Pass instances to the controller
|
||||
|
||||
firstStart(); // Check if this is the first run
|
||||
|
||||
Scene scene = new Scene(pane);
|
||||
scene.getStylesheets().add(Main.class.getResource("/css/application.css").toExternalForm());
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show(); // Show the stage
|
||||
|
||||
// Attach the KeyController
|
||||
kc = new KeyController(scene, mwc);
|
||||
|
||||
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), ev -> {
|
||||
mwc.updateTimeLabel(); // Update time label on UI
|
||||
}));
|
||||
timeline.setCycleCount(Animation.INDEFINITE);
|
||||
timeline.play();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (osName.contains("Windows")) {
|
||||
System.out.println("FCK Windows");
|
||||
filepath = userHome + "/Documents/jFxKasse/";
|
||||
}
|
||||
else {
|
||||
filepath = userHome + "/jFxKasse/";
|
||||
}
|
||||
launch(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the config.xml is present and initializes accordingly.
|
||||
*/
|
||||
private void firstStart() throws Exception {
|
||||
if (xmlc.loadSettings()) {
|
||||
// config.xml found, app starting normally
|
||||
System.out.println("XML found!");
|
||||
mwc.initUI(); // Initialize the UI elements
|
||||
mwc.setDBLabel(); // Set database labels
|
||||
dbc.setDbname(xmlc.getDatabaseName()); // Pass database name
|
||||
try {
|
||||
dbc.connectDatabase(); // Establish DB connection
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
showErrorDialog("Datenbankfehler", "Die Datenbank konnte nicht geöffnet werden.",
|
||||
"Datei: " + xmlc.getDatabaseName() + ".db\n\n"
|
||||
+ "Bitte prüfen Sie, ob die Datei vorhanden und nicht beschädigt ist.\n"
|
||||
+ "Fehlermeldung: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
mwc.fillTablePositionen(); // Fill TreeTable 'Positions'
|
||||
mwc.fillCategory();
|
||||
mwc.fillPrinterSettings();
|
||||
mwc.fillTableJobs();
|
||||
mwc.loadGridButtons();
|
||||
mwc.getSelectedCat(); // Load DB entries in ChoiceBox
|
||||
mwc.createNewJob();
|
||||
}
|
||||
else {
|
||||
// config.xml NOT found — first start of app
|
||||
System.out.println("no XML found!");
|
||||
xmlc.initXML(); // Set default values in memory
|
||||
File dir = new File(filepath);
|
||||
dir.mkdir(); // Create config directory
|
||||
try {
|
||||
xmlc.saveSettings(); // Persist defaults immediately so next start is normal
|
||||
System.out.println("Default config.xml written.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
showErrorDialog("Konfigurationsfehler", "Die Konfigurationsdatei konnte nicht geschrieben werden.",
|
||||
"Pfad: " + filepath + "config.xml\n\n" + "Bitte prüfen Sie die Schreibrechte.\n"
|
||||
+ "Fehlermeldung: " + e.getMessage());
|
||||
}
|
||||
mwc.blockUI(true); // Disable UI elements that need a DB
|
||||
mwc.blockUnlock(); // Show first-start hint, disable lock button
|
||||
}
|
||||
}
|
||||
|
||||
/** Shows a modal error alert dialog on the JavaFX thread. */
|
||||
private void showErrorDialog(String title, String header, String content) {
|
||||
javafx.scene.control.Alert alert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.ERROR);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
alert.setResizable(true);
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,25 +16,20 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class PrintJob
|
||||
{
|
||||
public class PrintJob {
|
||||
|
||||
private TimeDate timedate = new TimeDate();
|
||||
|
||||
public void printJob(int jobID, XMLController xmlc, DBController dbc,
|
||||
PrinterController pc)
|
||||
{
|
||||
public void printJob(int jobID, XMLController xmlc, DBController dbc, PrinterController pc) {
|
||||
|
||||
if ((xmlc.getPrintername().equals("Drucker auswählen")
|
||||
|| xmlc.getPrintername() == null)) {
|
||||
// no printer selected
|
||||
System.out.println("Kein Drucker eingestellt!!!");
|
||||
if ((xmlc.getPrintername().equals("Drucker auswählen") || xmlc.getPrintername() == null)) {
|
||||
// No printer selected
|
||||
System.out.println("No printer configured!");
|
||||
|
||||
// creates a dialog
|
||||
Dialog<Pair<String, String>> dialog = new Dialog<>();
|
||||
dialog.setTitle("Kein Drucker");
|
||||
dialog.setHeaderText("Es ist kein Drucker einestellt.\n"
|
||||
+ "In den Einstellungen einen Drucker auswählen.");
|
||||
dialog.setHeaderText("Es ist kein Drucker einestellt.\n" + "In den Einstellungen einen Drucker auswählen.");
|
||||
|
||||
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK);
|
||||
|
||||
@@ -43,30 +38,29 @@ public class PrintJob
|
||||
grid.setVgap(10);
|
||||
grid.setPadding(new Insets(20, 150, 10, 10));
|
||||
|
||||
grid.add(new Label("Tipp:\n"
|
||||
+ "Es kann ein virtueller Drucker installiert werden: \n\n"
|
||||
+ "www.cups-pdf.de"), 0, 0);
|
||||
grid.add(
|
||||
new Label(
|
||||
"Tipp:\n" + "Es kann ein virtueller Drucker installiert werden: \n\n" + "www.cups-pdf.de"),
|
||||
0, 0);
|
||||
|
||||
dialog.getDialogPane().setContent(grid);
|
||||
dialog.setResizable(true);
|
||||
dialog.showAndWait();
|
||||
|
||||
} else {
|
||||
// printer selected
|
||||
}
|
||||
else {
|
||||
// Printer selected
|
||||
pc.selectPrinter(xmlc.getPrintername());
|
||||
/* Single bill or splitted */
|
||||
/* Single bill or split by category */
|
||||
if (xmlc.getCategorySplitted()) {
|
||||
// split the bills
|
||||
// Split the bills
|
||||
|
||||
PrintDataSplitted pdsplitted = new PrintDataSplitted(
|
||||
xmlc.getLinebreak(), xmlc.getOffsetHeader(),
|
||||
xmlc.getOffsetFooter(),
|
||||
timedate.getSystemTime() + " " + timedate.getSystemDate(),
|
||||
PrintDataSplitted pdsplitted = new PrintDataSplitted(xmlc.getLinebreak(), xmlc.getOffsetHeader(),
|
||||
xmlc.getOffsetFooter(), timedate.getSystemTime() + " " + timedate.getSystemDate(),
|
||||
xmlc.getHeader(), xmlc.getFooter());
|
||||
|
||||
pdsplitted.setData(Integer.toString(jobID), dbc.getTime_Job(jobID),
|
||||
dbc.getQuantity_Job(jobID), dbc.getName_Job(jobID),
|
||||
dbc.getValue_Job(jobID), dbc.getCategory_Job(jobID),
|
||||
pdsplitted.setData(Integer.toString(jobID), dbc.getTime_Job(jobID), dbc.getQuantity_Job(jobID),
|
||||
dbc.getName_Job(jobID), dbc.getValue_Job(jobID), dbc.getCategory_Job(jobID),
|
||||
dbc.getJobValue_Job(jobID));
|
||||
|
||||
System.out.println("Printing job ...");
|
||||
@@ -77,16 +71,15 @@ public class PrintJob
|
||||
pc.printString(printString.get(i));
|
||||
}
|
||||
|
||||
} else {
|
||||
// one single bills
|
||||
PrintDataSimple pds = new PrintDataSimple(xmlc.getLinebreak(),
|
||||
xmlc.getOffsetHeader(), xmlc.getOffsetFooter(),
|
||||
timedate.getSystemTime() + " " + timedate.getSystemDate(),
|
||||
}
|
||||
else {
|
||||
// Single bill
|
||||
PrintDataSimple pds = new PrintDataSimple(xmlc.getLinebreak(), xmlc.getOffsetHeader(),
|
||||
xmlc.getOffsetFooter(), timedate.getSystemTime() + " " + timedate.getSystemDate(),
|
||||
xmlc.getHeader(), xmlc.getFooter());
|
||||
|
||||
pds.setData(Integer.toString(jobID), dbc.getTime_Job(jobID),
|
||||
dbc.getQuantity_Job(jobID), dbc.getName_Job(jobID),
|
||||
dbc.getValue_Job(jobID), dbc.getCategory_Job(jobID),
|
||||
pds.setData(Integer.toString(jobID), dbc.getTime_Job(jobID), dbc.getQuantity_Job(jobID),
|
||||
dbc.getName_Job(jobID), dbc.getValue_Job(jobID), dbc.getCategory_Job(jobID),
|
||||
dbc.getJobValue_Job(jobID));
|
||||
System.out.println("Printing job ...");
|
||||
pc.printString(pds.getPrintString());
|
||||
|
||||
@@ -4,18 +4,15 @@ import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class TimeDate
|
||||
{
|
||||
public String getSystemTime()
|
||||
{
|
||||
public class TimeDate {
|
||||
public String getSystemTime() {
|
||||
DateFormat dateFormat = new SimpleDateFormat("HH:mm");
|
||||
Date date = new Date();
|
||||
String time = dateFormat.format(date);
|
||||
return time;
|
||||
}
|
||||
|
||||
public String getSystemDate()
|
||||
{
|
||||
public String getSystemDate() {
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");
|
||||
Date date = new Date();
|
||||
String dateStr = dateFormat.format(date);
|
||||
|
||||
@@ -11,83 +11,77 @@ import com.jFxKasse.datatypes.tableDataJob;
|
||||
import com.jFxKasse.datatypes.tableDataPositionen;
|
||||
import java.io.File;
|
||||
|
||||
public class DBController
|
||||
{
|
||||
public class DBController {
|
||||
private Connection connection;
|
||||
|
||||
private String DB_PATH;
|
||||
|
||||
private String dbname;
|
||||
|
||||
public void main()
|
||||
{
|
||||
public void main() {
|
||||
try {
|
||||
connection = DriverManager
|
||||
.getConnection("jdbc:sqlite:" + DB_PATH + dbname + ".db");
|
||||
} catch (SQLException e) {
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DB_PATH + dbname + ".db");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public DBController(String path)
|
||||
{
|
||||
public DBController(String path) {
|
||||
this.DB_PATH = path;
|
||||
}
|
||||
|
||||
public void setDbname(String dbname)
|
||||
{
|
||||
public void setDbname(String dbname) {
|
||||
this.dbname = dbname;
|
||||
}
|
||||
|
||||
public void connectDatabase()
|
||||
{ // connect to database
|
||||
public void connectDatabase() { // Connect to database
|
||||
System.out.println("Connecting... DB name: " + dbname);
|
||||
try {
|
||||
if (connection != null)
|
||||
return;
|
||||
connection = DriverManager
|
||||
.getConnection("jdbc:sqlite:" + DB_PATH + dbname + ".db");
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DB_PATH + dbname + ".db");
|
||||
if (!connection.isClosed())
|
||||
System.out.println("DB connection established");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
public void run()
|
||||
{
|
||||
public void run() {
|
||||
try {
|
||||
if (!connection.isClosed() && connection != null) {
|
||||
connection.close();
|
||||
if (connection.isClosed())
|
||||
System.out.println();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean existDB(String pPfad)
|
||||
{ // does the DB exists?
|
||||
public boolean existDB(String pPfad) { // Check if the DB exists
|
||||
File varTmpDir = new File(pPfad);
|
||||
if (!varTmpDir.exists()) {
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public String getCategoryNameFromPositionen(int pID)
|
||||
{
|
||||
public String getCategoryNameFromPositionen(int pID) {
|
||||
int catInPos = 0;
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT posid, cat FROM positionen "
|
||||
+ "WHERE posid = " + pID + ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT posid, cat FROM positionen " + "WHERE posid = " + pID + ";");
|
||||
catInPos = rs.getInt("cat");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -97,10 +91,11 @@ public class DBController
|
||||
}
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT catid, catname FROM category "
|
||||
+ "WHERE catid = " + catInPos + ";");
|
||||
ResultSet rs = stmt
|
||||
.executeQuery("SELECT catid, catname FROM category " + "WHERE catid = " + catInPos + ";");
|
||||
return rs.getString("catname");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "keine Kategorie";
|
||||
@@ -108,34 +103,31 @@ public class DBController
|
||||
|
||||
}
|
||||
|
||||
// table Position section //
|
||||
public void createTablePositionen()
|
||||
{ // create table position
|
||||
// Table 'Positionen' section //
|
||||
public void createTablePositionen() { // Create table 'positionen'
|
||||
System.out.println("Creating table Positionen");
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("DROP TABLE IF EXISTS positionen;");
|
||||
stmt.executeUpdate(
|
||||
"CREATE TABLE positionen (posid, name, value, cat, color);");
|
||||
} catch (SQLException e) {
|
||||
stmt.executeUpdate("CREATE TABLE positionen (posid, name, value, cat, color);");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// create 25 demo/default data entries
|
||||
// Create 25 demo/default data entries
|
||||
for (int i = 0; i < 25; i++) {
|
||||
fillPositionen_Positionen(i + 1, "Noch frei", (float) 0.00, 6,
|
||||
"#ad0000");
|
||||
fillPositionen_Positionen(i + 1, "Noch frei", (float) 0.00, 6, "#ad0000");
|
||||
}
|
||||
}
|
||||
|
||||
public void fillPositionen_Positionen(int pID, String pName, float pValue,
|
||||
int pCat, String pColor)
|
||||
{ // create new data in table
|
||||
public void fillPositionen_Positionen(int pID, String pName, float pValue, int pCat, String pColor) { // Create new
|
||||
// entry in
|
||||
// table
|
||||
System.out.println("Creating new positionen entry");
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT INTO positionen VALUES (?, ?, ?, ?, ?);");
|
||||
PreparedStatement ps = connection.prepareStatement("INSERT INTO positionen VALUES (?, ?, ?, ?, ?);");
|
||||
ps.setInt(1, pID); // primary
|
||||
ps.setString(2, pName);
|
||||
ps.setFloat(3, pValue);
|
||||
@@ -146,144 +138,134 @@ public class DBController
|
||||
connection.setAutoCommit(false);
|
||||
ps.executeBatch(); // SQL execution
|
||||
connection.setAutoCommit(true);
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getName_Positionen(int pID)
|
||||
{
|
||||
public String getName_Positionen(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT posid, name FROM positionen WHERE posid = " + pID + ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT posid, name FROM positionen WHERE posid = " + pID + ";");
|
||||
return rs.getString("name");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "Error 404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getValue_Positionen(int pID)
|
||||
{
|
||||
public String getValue_Positionen(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT posid, value FROM positionen WHERE posid = " + pID
|
||||
+ ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT posid, value FROM positionen WHERE posid = " + pID + ";");
|
||||
return rs.getString("value");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "Error 404";
|
||||
}
|
||||
}
|
||||
|
||||
public int getCat_Positionen(int pID)
|
||||
{
|
||||
public int getCat_Positionen(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT catid, cat FROM positionen WHERE catid = " + pID + ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT catid, cat FROM positionen WHERE catid = " + pID + ";");
|
||||
System.out.println("getCarTet: " + rs.getInt("cat"));
|
||||
return rs.getInt("cat");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String getColor_Positionen(int pID)
|
||||
{
|
||||
public String getColor_Positionen(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT posid, color FROM positionen WHERE posid = " + pID
|
||||
+ ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT posid, color FROM positionen WHERE posid = " + pID + ";");
|
||||
return rs.getString("color");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "Error 404";
|
||||
}
|
||||
}
|
||||
|
||||
public void setName_Positionen(int pID, String pName)
|
||||
{
|
||||
public void setName_Positionen(int pID, String pName) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("UPDATE positionen SET name = '" + pName
|
||||
+ "'WHERE posid =" + pID + ";");
|
||||
} catch (SQLException e) {
|
||||
stmt.executeUpdate("UPDATE positionen SET name = '" + pName + "'WHERE posid =" + pID + ";");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue_Positionen(int pID, String pValue)
|
||||
{
|
||||
public void setValue_Positionen(int pID, String pValue) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("UPDATE positionen SET value = '" + pValue
|
||||
+ "'WHERE posid =" + pID + ";");
|
||||
} catch (SQLException e) {
|
||||
stmt.executeUpdate("UPDATE positionen SET value = '" + pValue + "'WHERE posid =" + pID + ";");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCat_Positionen(int pID, int pCat)
|
||||
{
|
||||
public void setCat_Positionen(int pID, int pCat) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("UPDATE positionen SET cat = '" + pCat
|
||||
+ "'WHERE posid =" + pID + ";");
|
||||
} catch (SQLException e) {
|
||||
stmt.executeUpdate("UPDATE positionen SET cat = '" + pCat + "'WHERE posid =" + pID + ";");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setColor_Positionen(int pID, String pColor)
|
||||
{
|
||||
public void setColor_Positionen(int pID, String pColor) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("UPDATE positionen SET color = '" + pColor
|
||||
+ "'WHERE posid =" + pID + ";");
|
||||
} catch (SQLException e) {
|
||||
stmt.executeUpdate("UPDATE positionen SET color = '" + pColor + "'WHERE posid =" + pID + ";");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<tableDataPositionen> ladeTabellePositionen()
|
||||
{
|
||||
public ArrayList<tableDataPositionen> ladeTabellePositionen() {
|
||||
ArrayList<tableDataPositionen> daten = new ArrayList<>();
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM positionen;");
|
||||
while (rs.next()) {
|
||||
try {
|
||||
daten.add(new tableDataPositionen(rs.getInt("posid"),
|
||||
rs.getString("name"), rs.getString("value"),
|
||||
daten.add(new tableDataPositionen(rs.getInt("posid"), rs.getString("name"), rs.getString("value"),
|
||||
rs.getString("cat"), rs.getString("color")));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return daten;
|
||||
}
|
||||
|
||||
public void ausgebenSysoPositionen()
|
||||
{
|
||||
public void ausgebenSysoPositionen() {
|
||||
System.out.println("Print positionen");
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
@@ -296,21 +278,22 @@ public class DBController
|
||||
System.out.println(" ");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// table Category section //
|
||||
public void createTableCategory()
|
||||
{ // create table position
|
||||
System.out.println("Erstelle Tabelle Kategorie");
|
||||
// Table 'Category' section //
|
||||
public void createTableCategory() { // Create table 'category'
|
||||
System.out.println("Creating table Category");
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("DROP TABLE IF EXISTS category;");
|
||||
stmt.executeUpdate("CREATE TABLE category (catid, catname);");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -323,76 +306,71 @@ public class DBController
|
||||
|
||||
}
|
||||
|
||||
public void setName_Category(int pID, String pName)
|
||||
{
|
||||
public void setName_Category(int pID, String pName) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("UPDATE category SET catname = '" + pName
|
||||
+ "'WHERE catid =" + pID + ";");
|
||||
} catch (SQLException e) {
|
||||
stmt.executeUpdate("UPDATE category SET catname = '" + pName + "'WHERE catid =" + pID + ";");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void fillCategory_Category(int pID, String pName)
|
||||
{
|
||||
public void fillCategory_Category(int pID, String pName) {
|
||||
|
||||
System.out.println("Erstelle neuen Kategorie Eintrag");
|
||||
System.out.println("Creating new category entry");
|
||||
try {
|
||||
PreparedStatement ps = connection
|
||||
.prepareStatement("INSERT INTO category VALUES (?, ?);");
|
||||
PreparedStatement ps = connection.prepareStatement("INSERT INTO category VALUES (?, ?);");
|
||||
ps.setInt(1, pID); // primary
|
||||
ps.setString(2, pName);
|
||||
ps.addBatch();
|
||||
connection.setAutoCommit(false);
|
||||
ps.executeBatch(); // SQL execution
|
||||
connection.setAutoCommit(true);
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getName_Category(int pID)
|
||||
{
|
||||
public String getName_Category(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT catid, catname FROM category WHERE catid = " + pID
|
||||
+ ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT catid, catname FROM category WHERE catid = " + pID + ";");
|
||||
return rs.getString("catname");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "Error 404";
|
||||
}
|
||||
}
|
||||
|
||||
// table Jobs section //
|
||||
public void erstelleTabelleJobs()
|
||||
{ // create table jobs
|
||||
// Table 'Jobs' section //
|
||||
public void erstelleTabelleJobs() { // Create table 'jobs'
|
||||
System.out.println("Creating table Jobs");
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("DROP TABLE IF EXISTS jobs;");
|
||||
stmt.executeUpdate(
|
||||
"CREATE TABLE jobs (jobid, time, positionen_quantity, positionen_name, positionen_value, positionen_cat, state, jobvalue);");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public int getLatestJobNumber_Job()
|
||||
{
|
||||
public int getLatestJobNumber_Job() {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid from jobs ORDER BY jobid DESC LIMIT 1;");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid from jobs ORDER BY jobid DESC LIMIT 1;");
|
||||
return rs.getInt("jobid");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
// System.err.println("Couldn't handle DB-Query");
|
||||
// e.printStackTrace();
|
||||
|
||||
@@ -400,152 +378,138 @@ public class DBController
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getTime_Job(int pID)
|
||||
{
|
||||
public String getTime_Job(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid, time FROM jobs WHERE jobid = " + pID + ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid, time FROM jobs WHERE jobid = " + pID + ";");
|
||||
return rs.getString("time");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getQuantity_Job(int pID)
|
||||
{
|
||||
public String getQuantity_Job(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid, positionen_quantity FROM jobs WHERE jobid = "
|
||||
+ pID + ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid, positionen_quantity FROM jobs WHERE jobid = " + pID + ";");
|
||||
return rs.getString("positionen_quantity");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getName_Job(int pID)
|
||||
{
|
||||
public String getName_Job(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid, positionen_name FROM jobs WHERE jobid = " + pID
|
||||
+ ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid, positionen_name FROM jobs WHERE jobid = " + pID + ";");
|
||||
return rs.getString("positionen_name");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getValue_Job(int pID)
|
||||
{
|
||||
public String getValue_Job(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid, positionen_value FROM jobs WHERE jobid = " + pID
|
||||
+ ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid, positionen_value FROM jobs WHERE jobid = " + pID + ";");
|
||||
return rs.getString("positionen_value");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getCategory_Job(int pID)
|
||||
{
|
||||
public String getCategory_Job(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid, positionen_cat FROM jobs WHERE jobid = " + pID
|
||||
+ ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid, positionen_cat FROM jobs WHERE jobid = " + pID + ";");
|
||||
return rs.getString("positionen_cat");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getState_Job(int pID)
|
||||
{
|
||||
public String getState_Job(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid, state FROM jobs WHERE jobid = " + pID + ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid, state FROM jobs WHERE jobid = " + pID + ";");
|
||||
return rs.getString("state");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getJobValue_Job(int pID)
|
||||
{
|
||||
public String getJobValue_Job(int pID) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT jobid, jobvalue FROM jobs WHERE jobid = " + pID + ";");
|
||||
ResultSet rs = stmt.executeQuery("SELECT jobid, jobvalue FROM jobs WHERE jobid = " + pID + ";");
|
||||
return rs.getString("jobvalue");
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "404";
|
||||
}
|
||||
}
|
||||
|
||||
public String getAllJobValue_Job()
|
||||
{
|
||||
public String getAllJobValue_Job() {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT state, jobvalue, SUM(jobvalue) AS ALLVALUE FROM jobs WHERE state = "
|
||||
+ '"' + "verbucht" + '"' + ";");
|
||||
return rs.getString("ALLVALUE");
|
||||
} catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
public String getJobCount()
|
||||
{
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT count(*) AS JOBCOUNT FROM jobs WHERE state = " + '"'
|
||||
ResultSet rs = stmt
|
||||
.executeQuery("SELECT state, jobvalue, SUM(jobvalue) AS ALLVALUE FROM jobs WHERE state = " + '"'
|
||||
+ "verbucht" + '"' + ";");
|
||||
return rs.getString("JOBCOUNT");
|
||||
} catch (SQLException e) {
|
||||
return rs.getString("ALLVALUE");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus_Jobs(int pID, String pStatus)
|
||||
{
|
||||
public String getJobCount() {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("UPDATE jobs SET state = '" + pStatus
|
||||
+ "'WHERE jobid =" + pID + ";");
|
||||
} catch (SQLException e) {
|
||||
ResultSet rs = stmt.executeQuery(
|
||||
"SELECT count(*) AS JOBCOUNT FROM jobs WHERE state = " + '"' + "verbucht" + '"' + ";");
|
||||
return rs.getString("JOBCOUNT");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus_Jobs(int pID, String pStatus) {
|
||||
try {
|
||||
Statement stmt = connection.createStatement();
|
||||
stmt.executeUpdate("UPDATE jobs SET state = '" + pStatus + "'WHERE jobid =" + pID + ";");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<tableDataJob> loadTableJobs_Job()
|
||||
{
|
||||
public ArrayList<tableDataJob> loadTableJobs_Job() {
|
||||
|
||||
ArrayList<tableDataJob> tmp = new ArrayList<tableDataJob>();
|
||||
try {
|
||||
@@ -557,31 +521,29 @@ public class DBController
|
||||
|
||||
String tablePosition = rs.getString("positionen_name");
|
||||
|
||||
tableDataJob data = new tableDataJob(rs.getInt("jobid"),
|
||||
rs.getString("time"), tablePosition, rs.getString("state"),
|
||||
rs.getString("jobvalue"));
|
||||
tableDataJob data = new tableDataJob(rs.getInt("jobid"), rs.getString("time"), tablePosition,
|
||||
rs.getString("state"), rs.getString("jobvalue"));
|
||||
|
||||
tmp.add(data);
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public void fillJobs_Jobs(int pID, String pTime, String pPositionen_quantity,
|
||||
String pPositionen_name, String pPositionen_value,
|
||||
String pPositionen_cat, String pState, String pJobvalue)
|
||||
{
|
||||
System.out.println("Create new Job Entry");
|
||||
public void fillJobs_Jobs(int pID, String pTime, String pPositionen_quantity, String pPositionen_name,
|
||||
String pPositionen_value, String pPositionen_cat, String pState, String pJobvalue) {
|
||||
System.out.println("Creating new job entry");
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT INTO jobs VALUES (?, ?, ?, ?, ?, ?, ?, ?);");
|
||||
PreparedStatement ps = connection.prepareStatement("INSERT INTO jobs VALUES (?, ?, ?, ?, ?, ?, ?, ?);");
|
||||
ps.setInt(1, pID); // primary
|
||||
ps.setString(2, pTime);
|
||||
ps.setString(3, pPositionen_quantity);
|
||||
@@ -594,7 +556,8 @@ public class DBController
|
||||
connection.setAutoCommit(false);
|
||||
ps.executeBatch(); // SQL execution
|
||||
connection.setAutoCommit(true);
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
catch (SQLException e) {
|
||||
System.err.println("Couldn't handle DB-Query");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
package com.jFxKasse.controller;
|
||||
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
|
||||
public class KeyController {
|
||||
private MainWindowController mwc;
|
||||
|
||||
public KeyController(Scene scene, MainWindowController mwc) {
|
||||
this.mwc = mwc;
|
||||
|
||||
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
|
||||
@Override
|
||||
public void handle(KeyEvent keyEvent) {
|
||||
switch (mwc.getActiveTab()) {
|
||||
case 0:
|
||||
handleTabNewJob(keyEvent);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
handleTabJobs(keyEvent);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
handleTabPosEdit(keyEvent);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
handleTabSettings(keyEvent);
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void handleTabNewJob(KeyEvent key) {
|
||||
if ((key.getCode() == KeyCode.ENTER) && (!mwc.btnPrintBill.isDisabled())) {
|
||||
mwc.btnPrintBillAction(null);
|
||||
}
|
||||
|
||||
if ((key.getCode() == KeyCode.ESCAPE) && (!mwc.btnLock.isDisabled())) {
|
||||
mwc.btnLockAction(null);
|
||||
}
|
||||
|
||||
if ((key.getCode() == KeyCode.DELETE) && (!mwc.btnDeleteSelectedPosition.isDisabled())) {
|
||||
mwc.btnDeleteSelectedPositionAction(null);
|
||||
}
|
||||
|
||||
handelGridButtons(key);
|
||||
|
||||
}
|
||||
|
||||
private void handleTabJobs(KeyEvent key) {
|
||||
if ((key.getCode() == KeyCode.ENTER) && (!mwc.btnReprintJob.isDisabled())) {
|
||||
mwc.btnReprintJobAction(null);
|
||||
}
|
||||
|
||||
if ((key.getCode() == KeyCode.DELETE) && (!mwc.btnCancelJob.isDisabled())) {
|
||||
mwc.btnCancelJobAction(null);
|
||||
}
|
||||
|
||||
if ((key.getCode() == KeyCode.S) && (!mwc.btnCalcStats.isDisabled())) {
|
||||
mwc.btnCalcStatsAction(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTabPosEdit(KeyEvent key) {
|
||||
if ((key.getCode() == KeyCode.ENTER) && (!mwc.btnSaveEntry.isDisabled())) {
|
||||
mwc.btnSaveEntryAction(null);
|
||||
}
|
||||
|
||||
if ((key.getCode() == KeyCode.DELETE) && (!mwc.btnClearEntry.isDisabled())) {
|
||||
mwc.btnClearEntryAction(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTabSettings(KeyEvent key) {
|
||||
if ((key.getCode() == KeyCode.ENTER) && (!mwc.btnSavePrinter.isDisabled())) {
|
||||
mwc.btnSavePrinterAction(null);
|
||||
}
|
||||
|
||||
if ((key.getCode() == KeyCode.ENTER) && (!mwc.btnSaveCat.isDisabled())) {
|
||||
mwc.btnSaveCatAction(null);
|
||||
}
|
||||
|
||||
if ((key.getCode() == KeyCode.ENTER) && (!mwc.btnCreateNewDatabase.isDisabled())) {
|
||||
try {
|
||||
mwc.btnCreateNewDatabaseAction(null);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handelGridButtons(KeyEvent key) {
|
||||
|
||||
switch (key.getCode()) {
|
||||
case Q:
|
||||
mwc.gridButton01Action(null);
|
||||
break;
|
||||
|
||||
case W:
|
||||
mwc.gridButton02Action(null);
|
||||
break;
|
||||
|
||||
case E:
|
||||
mwc.gridButton03Action(null);
|
||||
break;
|
||||
|
||||
case R:
|
||||
mwc.gridButton04Action(null);
|
||||
break;
|
||||
|
||||
case T:
|
||||
mwc.gridButton05Action(null);
|
||||
break;
|
||||
|
||||
case Z:
|
||||
mwc.gridButton06Action(null);
|
||||
break;
|
||||
|
||||
case U:
|
||||
mwc.gridButton07Action(null);
|
||||
break;
|
||||
|
||||
case I:
|
||||
mwc.gridButton08Action(null);
|
||||
break;
|
||||
|
||||
case O:
|
||||
mwc.gridButton09Action(null);
|
||||
break;
|
||||
|
||||
case P:
|
||||
mwc.gridButton10Action(null);
|
||||
break;
|
||||
|
||||
case A:
|
||||
mwc.gridButton11Action(null);
|
||||
break;
|
||||
|
||||
case S:
|
||||
mwc.gridButton12Action(null);
|
||||
break;
|
||||
|
||||
case D:
|
||||
mwc.gridButton13Action(null);
|
||||
break;
|
||||
|
||||
case F:
|
||||
mwc.gridButton14Action(null);
|
||||
break;
|
||||
|
||||
case G:
|
||||
mwc.gridButton15Action(null);
|
||||
break;
|
||||
|
||||
case H:
|
||||
mwc.gridButton16Action(null);
|
||||
break;
|
||||
|
||||
case J:
|
||||
mwc.gridButton17Action(null);
|
||||
break;
|
||||
|
||||
case K:
|
||||
mwc.gridButton18Action(null);
|
||||
break;
|
||||
|
||||
case L:
|
||||
mwc.gridButton19Action(null);
|
||||
break;
|
||||
|
||||
case Y:
|
||||
mwc.gridButton20Action(null);
|
||||
break;
|
||||
|
||||
case X:
|
||||
mwc.gridButton21Action(null);
|
||||
break;
|
||||
|
||||
case C:
|
||||
mwc.gridButton22Action(null);
|
||||
break;
|
||||
|
||||
case V:
|
||||
mwc.gridButton23Action(null);
|
||||
break;
|
||||
|
||||
case B:
|
||||
mwc.gridButton24Action(null);
|
||||
break;
|
||||
|
||||
case N:
|
||||
mwc.gridButton25Action(null);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* some parts are from http://www.mets-blog.com/java-pos-thermal-printer-example/
|
||||
* Some parts are from http://www.mets-blog.com/java-pos-thermal-printer-example/
|
||||
*/
|
||||
|
||||
package com.jFxKasse.controller;
|
||||
@@ -21,26 +21,23 @@ import javax.print.SimpleDoc;
|
||||
import javax.print.attribute.HashPrintRequestAttributeSet;
|
||||
import javax.print.attribute.PrintRequestAttributeSet;
|
||||
|
||||
public class PrinterController implements Printable
|
||||
{
|
||||
// All available Printers on this system
|
||||
public class PrinterController implements Printable {
|
||||
// All available printers on this system
|
||||
private PrintService[] printService;
|
||||
|
||||
// selected printer
|
||||
// Selected printer
|
||||
private PrintService selectedPrinter;
|
||||
|
||||
private DocFlavor flavor;
|
||||
|
||||
public PrinterController()
|
||||
{
|
||||
public PrinterController() {
|
||||
flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A String array with all available printers
|
||||
*/
|
||||
public String[] getAvailablePrinters()
|
||||
{
|
||||
public String[] getAvailablePrinters() {
|
||||
int printerSize = PrinterJob.lookupPrintServices().length;
|
||||
String printers[] = new String[printerSize];
|
||||
|
||||
@@ -51,10 +48,9 @@ public class PrinterController implements Printable
|
||||
}
|
||||
|
||||
/**
|
||||
* searchs connected printers on the system
|
||||
* Searches for connected printers on the system
|
||||
*/
|
||||
public void searchPrinters()
|
||||
{
|
||||
public void searchPrinters() {
|
||||
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
|
||||
this.printService = PrintServiceLookup.lookupPrintServices(flavor, pras);
|
||||
String printers[] = getAvailablePrinters();
|
||||
@@ -67,11 +63,11 @@ public class PrinterController implements Printable
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the printer via its name
|
||||
* Selects the printer by its name
|
||||
*
|
||||
* @param printerName
|
||||
*/
|
||||
public void selectPrinter(String printerName)
|
||||
{
|
||||
public void selectPrinter(String printerName) {
|
||||
String printers[] = getAvailablePrinters();
|
||||
|
||||
for (int i = 0; i < printers.length; i++) {
|
||||
@@ -84,11 +80,11 @@ public class PrinterController implements Printable
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a string to the selected printer
|
||||
*
|
||||
* @param input data as String
|
||||
* @param text data as String
|
||||
*/
|
||||
public void printString(String text)
|
||||
{
|
||||
public void printString(String text) {
|
||||
PrintService service = selectedPrinter;
|
||||
DocPrintJob job = service.createPrintJob();
|
||||
|
||||
@@ -97,21 +93,21 @@ public class PrinterController implements Printable
|
||||
bytes = text.getBytes(StandardCharsets.UTF_8);
|
||||
Doc doc = new SimpleDoc(bytes, flavor, null);
|
||||
job.print(doc, null);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public int print(Graphics g, PageFormat pf, int page) throws PrinterException
|
||||
{
|
||||
public int print(Graphics g, PageFormat pf, int page) throws PrinterException {
|
||||
if (page > 0) { /* We have only one page, and 'page' is zero-based */
|
||||
return NO_SUCH_PAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* User (0,0) is typically outside the imageable area, so we must
|
||||
* translate by the X and Y values in the PageFormat to avoid clipping
|
||||
* User (0,0) is typically outside the imageable area, so we must translate by
|
||||
* the X and Y values in the PageFormat to avoid clipping
|
||||
*/
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.translate(pf.getImageableX(), pf.getImageableY());
|
||||
@@ -119,21 +115,20 @@ public class PrinterController implements Printable
|
||||
return PAGE_EXISTS;
|
||||
}
|
||||
|
||||
public void printBytes(byte[] bytes)
|
||||
{
|
||||
public void printBytes(byte[] bytes) {
|
||||
PrintService service = selectedPrinter;
|
||||
DocPrintJob job = service.createPrintJob();
|
||||
|
||||
try {
|
||||
Doc doc = new SimpleDoc(bytes, flavor, null);
|
||||
job.print(doc, null);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void cutPaper()
|
||||
{
|
||||
public void cutPaper() {
|
||||
byte[] cutP = new byte[] { 0x1d, 'V', 1 };
|
||||
printBytes(cutP);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class XMLController
|
||||
{
|
||||
public class XMLController {
|
||||
private String databaseName = null;
|
||||
|
||||
private String printername = null;
|
||||
@@ -25,18 +24,18 @@ public class XMLController
|
||||
|
||||
private boolean categorySplitted;
|
||||
|
||||
private boolean autoPrintBill;
|
||||
|
||||
private String filePath = null;
|
||||
|
||||
private Properties props = null;
|
||||
|
||||
public XMLController(String filePath)
|
||||
{
|
||||
public XMLController(String filePath) {
|
||||
this.filePath = filePath + "config.xml";
|
||||
props = new Properties();
|
||||
}
|
||||
|
||||
public void saveSettings() throws Exception
|
||||
{ // Save settings to config.xml
|
||||
public void saveSettings() throws Exception { // Save settings to config.xml
|
||||
|
||||
System.out.println("Saving XML");
|
||||
|
||||
@@ -50,10 +49,20 @@ public class XMLController
|
||||
|
||||
if (this.categorySplitted) {
|
||||
categorySplitted = "true";
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
categorySplitted = "false";
|
||||
}
|
||||
|
||||
String autoPrintBill = null;
|
||||
|
||||
if (this.autoPrintBill) {
|
||||
autoPrintBill = "true";
|
||||
}
|
||||
else {
|
||||
autoPrintBill = "false";
|
||||
}
|
||||
|
||||
try {
|
||||
props.setProperty("databasename", this.databaseName);
|
||||
props.setProperty("printername", this.printername);
|
||||
@@ -63,16 +72,17 @@ public class XMLController
|
||||
props.setProperty("header", this.header);
|
||||
props.setProperty("footer", this.footer);
|
||||
props.setProperty("categorySplitted", categorySplitted);
|
||||
props.setProperty("autoPrintBill", autoPrintBill);
|
||||
|
||||
outputStream = new FileOutputStream(filePath);
|
||||
props.storeToXML(outputStream, "jFxKasse settings");
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean loadSettings() throws Exception
|
||||
{ // reads the settings from config.xml
|
||||
public boolean loadSettings() throws Exception { // Read settings from config.xml
|
||||
InputStream inputStream;
|
||||
try {
|
||||
inputStream = new FileInputStream(filePath);
|
||||
@@ -82,21 +92,22 @@ public class XMLController
|
||||
|
||||
try {
|
||||
this.linebreak = Integer.valueOf(props.getProperty("linebreak"));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.linebreak = 28;
|
||||
}
|
||||
|
||||
try {
|
||||
this.offsetHeader = Integer
|
||||
.valueOf(props.getProperty("offsetHeader"));
|
||||
} catch (Exception e) {
|
||||
this.offsetHeader = Integer.valueOf(props.getProperty("offsetHeader"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.offsetHeader = 1;
|
||||
}
|
||||
|
||||
try {
|
||||
this.offsetFooter = Integer
|
||||
.valueOf(props.getProperty("offsetFooter"));
|
||||
} catch (Exception e) {
|
||||
this.offsetFooter = Integer.valueOf(props.getProperty("offsetFooter"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.offsetFooter = 2;
|
||||
}
|
||||
|
||||
@@ -106,23 +117,37 @@ public class XMLController
|
||||
try {
|
||||
if (props.getProperty("categorySplitted").equals("true")) {
|
||||
this.categorySplitted = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.categorySplitted = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.categorySplitted = false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (props.getProperty("autoPrintBill").equals("true")) {
|
||||
this.autoPrintBill = true;
|
||||
}
|
||||
else {
|
||||
this.autoPrintBill = false;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
this.autoPrintBill = false;
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void initXML()
|
||||
{
|
||||
public void initXML() {
|
||||
this.printername = "Drucker auswählen";
|
||||
|
||||
this.offsetHeader = 1;
|
||||
@@ -132,88 +157,82 @@ public class XMLController
|
||||
|
||||
this.categorySplitted = false;
|
||||
|
||||
this.autoPrintBill = false;
|
||||
|
||||
this.header = "XYZ GmbH";
|
||||
this.footer = "Vielen Dank für den Einkauf";
|
||||
}
|
||||
|
||||
public String getDatabaseName()
|
||||
{
|
||||
public String getDatabaseName() {
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
public void setDatabaseName(String databaseName)
|
||||
{
|
||||
public void setDatabaseName(String databaseName) {
|
||||
this.databaseName = databaseName;
|
||||
}
|
||||
|
||||
public String getPrintername()
|
||||
{
|
||||
public String getPrintername() {
|
||||
return printername;
|
||||
}
|
||||
|
||||
public void setPrintername(String printername)
|
||||
{
|
||||
public void setPrintername(String printername) {
|
||||
this.printername = printername;
|
||||
}
|
||||
|
||||
public int getLinebreak()
|
||||
{
|
||||
public int getLinebreak() {
|
||||
return linebreak;
|
||||
}
|
||||
|
||||
public void setLinebreak(int linebreak)
|
||||
{
|
||||
public void setLinebreak(int linebreak) {
|
||||
this.linebreak = linebreak;
|
||||
}
|
||||
|
||||
public int getOffsetHeader()
|
||||
{
|
||||
public int getOffsetHeader() {
|
||||
return offsetHeader;
|
||||
}
|
||||
|
||||
public void setOffsetHeader(int offsetHeader)
|
||||
{
|
||||
public void setOffsetHeader(int offsetHeader) {
|
||||
this.offsetHeader = offsetHeader;
|
||||
}
|
||||
|
||||
public int getOffsetFooter()
|
||||
{
|
||||
public int getOffsetFooter() {
|
||||
return offsetFooter;
|
||||
}
|
||||
|
||||
public void setOffsetFooter(int offsetFooter)
|
||||
{
|
||||
public void setOffsetFooter(int offsetFooter) {
|
||||
this.offsetFooter = offsetFooter;
|
||||
}
|
||||
|
||||
public String getHeader()
|
||||
{
|
||||
public String getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setHeader(String header)
|
||||
{
|
||||
public void setHeader(String header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public String getFooter()
|
||||
{
|
||||
public String getFooter() {
|
||||
return footer;
|
||||
}
|
||||
|
||||
public void setFooter(String footer)
|
||||
{
|
||||
public void setFooter(String footer) {
|
||||
this.footer = footer;
|
||||
}
|
||||
|
||||
public boolean getCategorySplitted()
|
||||
{
|
||||
public boolean getCategorySplitted() {
|
||||
return categorySplitted;
|
||||
}
|
||||
|
||||
public void setCategorySplitted(boolean categorySplitted)
|
||||
{
|
||||
public void setCategorySplitted(boolean categorySplitted) {
|
||||
this.categorySplitted = categorySplitted;
|
||||
}
|
||||
|
||||
public boolean getAutoPrintBill() {
|
||||
return autoPrintBill;
|
||||
}
|
||||
|
||||
public void setAutoPrintBill(boolean autoPrintBill) {
|
||||
this.autoPrintBill = autoPrintBill;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
package com.jFxKasse.datatypes;
|
||||
|
||||
public class Category
|
||||
{
|
||||
public class Category {
|
||||
private String categoryName;
|
||||
|
||||
private String positionsString = "\n";
|
||||
|
||||
public Category(String categoryName)
|
||||
{
|
||||
public Category(String categoryName) {
|
||||
this.categoryName = categoryName;
|
||||
}
|
||||
|
||||
public String getCategoryName()
|
||||
{
|
||||
public String getCategoryName() {
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
public void addPosition(int quantity, String name, String value,
|
||||
PrintData pd)
|
||||
{
|
||||
public void addPosition(int quantity, String name, String value, PrintData pd) {
|
||||
for (int i = 0; i < quantity; i++) {
|
||||
positionsString = positionsString
|
||||
+ pd.setRight(pd.breakLines(name), value + " €") + "\n";
|
||||
positionsString = positionsString + pd.setRight(pd.breakLines(name), value + " €") + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
public String getPositionsString()
|
||||
{
|
||||
public String getPositionsString() {
|
||||
return positionsString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.jFxKasse.datatypes;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Job
|
||||
{
|
||||
public class Job {
|
||||
private int jobnumber;
|
||||
|
||||
private float jobvalue;
|
||||
@@ -18,8 +19,7 @@ public class Job
|
||||
|
||||
private ArrayList<String> positionenCat;
|
||||
|
||||
public Job(int pJobnumber)
|
||||
{
|
||||
public Job(int pJobnumber) {
|
||||
this.jobnumber = pJobnumber;
|
||||
positionenQuantity = new ArrayList<Integer>();
|
||||
positionenName = new ArrayList<String>();
|
||||
@@ -28,32 +28,26 @@ public class Job
|
||||
|
||||
}
|
||||
|
||||
public void setJobtime(String jobtime)
|
||||
{
|
||||
public void setJobtime(String jobtime) {
|
||||
this.jobtime = jobtime;
|
||||
}
|
||||
|
||||
public int getJobnumber()
|
||||
{
|
||||
public int getJobnumber() {
|
||||
return this.jobnumber;
|
||||
}
|
||||
|
||||
public String getJobtime()
|
||||
{
|
||||
public String getJobtime() {
|
||||
return this.jobtime;
|
||||
}
|
||||
|
||||
public float getJobValue()
|
||||
{
|
||||
public float getJobValue() {
|
||||
|
||||
calcJobValue();
|
||||
|
||||
return this.jobvalue;
|
||||
}
|
||||
|
||||
public void addPosition(String pPositionenName, float pPositionenValue,
|
||||
String pPositionenCat)
|
||||
{
|
||||
public void addPosition(String pPositionenName, float pPositionenValue, String pPositionenCat) {
|
||||
for (int i = 0; i < positionenName.size(); i++) {
|
||||
if (positionenName.get(i).equals(pPositionenName)) {
|
||||
// Item is already in list, increase quantity
|
||||
@@ -70,49 +64,44 @@ public class Job
|
||||
calcJobValue();
|
||||
}
|
||||
|
||||
public void printJobOnConsole()
|
||||
{
|
||||
public void printJobOnConsole() {
|
||||
System.out.println("---------------------------------------------");
|
||||
System.out.println("JobNummer: " + jobnumber);
|
||||
System.out.println("---------------------------------------------");
|
||||
|
||||
for (int i = 0; i < positionenName.size(); i++) {
|
||||
|
||||
System.out.println(
|
||||
positionenQuantity.get(i) + " " + positionenName.get(i) + " "
|
||||
+ positionenValue.get(i) + " " + positionenCat.get(i));
|
||||
System.out.println(positionenQuantity.get(i) + " " + positionenName.get(i) + " " + positionenValue.get(i)
|
||||
+ " " + positionenCat.get(i));
|
||||
}
|
||||
|
||||
System.out.println("---------------------------------------------");
|
||||
|
||||
}
|
||||
|
||||
public ArrayList<tableDataCurrentOrder> getCurrentJobPositionen()
|
||||
{
|
||||
public ArrayList<tableDataCurrentOrder> getCurrentJobPositionen() {
|
||||
ArrayList<tableDataCurrentOrder> jobitems = new ArrayList<tableDataCurrentOrder>();
|
||||
|
||||
|
||||
for (int i = 0; i < positionenName.size(); i++) {
|
||||
|
||||
tableDataCurrentOrder tmp = new tableDataCurrentOrder(
|
||||
positionenName.get(i), positionenQuantity.get(i));
|
||||
tableDataCurrentOrder tmp = new tableDataCurrentOrder(positionenName.get(i), positionenQuantity.get(i));
|
||||
|
||||
jobitems.add(tmp);
|
||||
}
|
||||
return jobitems;
|
||||
}
|
||||
|
||||
private void calcJobValue()
|
||||
{
|
||||
private void calcJobValue() {
|
||||
jobvalue = 0;
|
||||
|
||||
for (int i = 0; i < positionenValue.size(); i++) {
|
||||
jobvalue = jobvalue
|
||||
+ (positionenQuantity.get(i) * positionenValue.get(i));
|
||||
jobvalue = jobvalue + (positionenQuantity.get(i) * positionenValue.get(i));
|
||||
}
|
||||
// Round to two decimal places
|
||||
jobvalue = BigDecimal.valueOf(jobvalue).setScale(2, RoundingMode.HALF_UP).floatValue();
|
||||
}
|
||||
|
||||
public String createPosQuantityDBString()
|
||||
{
|
||||
public String createPosQuantityDBString() {
|
||||
String tmp = String.valueOf(positionenQuantity.get(0));
|
||||
for (int i = 1; i < positionenName.size(); i++) {
|
||||
|
||||
@@ -121,8 +110,7 @@ public class Job
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public String createPosNameDBString()
|
||||
{
|
||||
public String createPosNameDBString() {
|
||||
String tmp = positionenName.get(0);
|
||||
for (int i = 1; i < positionenName.size(); i++) {
|
||||
|
||||
@@ -131,8 +119,7 @@ public class Job
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public String createPosValueDBString()
|
||||
{
|
||||
public String createPosValueDBString() {
|
||||
String tmp = String.valueOf(positionenValue.get(0));
|
||||
for (int i = 1; i < positionenName.size(); i++) {
|
||||
|
||||
@@ -141,8 +128,7 @@ public class Job
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public String createPosCatDBString()
|
||||
{
|
||||
public String createPosCatDBString() {
|
||||
String tmp = positionenCat.get(0);
|
||||
for (int i = 1; i < positionenName.size(); i++) {
|
||||
|
||||
@@ -151,8 +137,7 @@ public class Job
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public void deletePosName(String pPosName)
|
||||
{
|
||||
public void deletePosName(String pPosName) {
|
||||
|
||||
for (int i = 0; i < positionenName.size(); i++) {
|
||||
|
||||
@@ -160,7 +145,8 @@ public class Job
|
||||
|
||||
if (positionenQuantity.get(i) > 1) {
|
||||
positionenQuantity.set(i, positionenQuantity.get(i) - 1);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
|
||||
positionenQuantity.remove(i);
|
||||
positionenName.remove(i);
|
||||
@@ -175,8 +161,15 @@ public class Job
|
||||
|
||||
}
|
||||
|
||||
public boolean existsPosName(String pPosName)
|
||||
{
|
||||
public void clearAllPositions() {
|
||||
positionenQuantity.clear();
|
||||
positionenName.clear();
|
||||
positionenValue.clear();
|
||||
positionenCat.clear();
|
||||
jobvalue = 0;
|
||||
}
|
||||
|
||||
public boolean existsPosName(String pPosName) {
|
||||
for (int i = 0; i < positionenName.size(); i++) {
|
||||
if (positionenName.get(i).equals(pPosName)) {
|
||||
return true;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.jFxKasse.datatypes;
|
||||
|
||||
public abstract class PrintData
|
||||
{
|
||||
public abstract class PrintData {
|
||||
|
||||
protected int headerSpace;
|
||||
|
||||
@@ -30,7 +29,8 @@ public abstract class PrintData
|
||||
protected String jobValue;
|
||||
|
||||
/**
|
||||
* Constructor with all data that is not in the DB
|
||||
* Constructor with all data that is not stored in the DB
|
||||
*
|
||||
* @param lineBreak
|
||||
* @param headerSpace
|
||||
* @param footerSpace
|
||||
@@ -38,9 +38,8 @@ public abstract class PrintData
|
||||
* @param header
|
||||
* @param footer
|
||||
*/
|
||||
public PrintData(int lineBreak, int headerSpace, int footerSpace,
|
||||
String timeAndDatePrint, String header, String footer)
|
||||
{
|
||||
public PrintData(int lineBreak, int headerSpace, int footerSpace, String timeAndDatePrint, String header,
|
||||
String footer) {
|
||||
this.lineBreak = lineBreak;
|
||||
this.headerSpace = headerSpace;
|
||||
this.footerSpace = footerSpace;
|
||||
@@ -50,7 +49,8 @@ public abstract class PrintData
|
||||
}
|
||||
|
||||
/**
|
||||
* set all Data that is in the DB
|
||||
* Set all data that is stored in the DB
|
||||
*
|
||||
* @param jobID
|
||||
* @param timeAndDateOrder
|
||||
* @param positionenQuantity
|
||||
@@ -59,10 +59,8 @@ public abstract class PrintData
|
||||
* @param positionenCategory
|
||||
* @param jobValue
|
||||
*/
|
||||
public void setData(String jobID, String timeAndDateOrder,
|
||||
String positionsQuantity, String positionsName,
|
||||
String positionsValue, String positionsCategory, String jobValue)
|
||||
{
|
||||
public void setData(String jobID, String timeAndDateOrder, String positionsQuantity, String positionsName,
|
||||
String positionsValue, String positionsCategory, String jobValue) {
|
||||
this.jobID = jobID;
|
||||
this.timeAndDateOrder = timeAndDateOrder;
|
||||
this.positionsQuantity = positionsQuantity;
|
||||
@@ -73,25 +71,26 @@ public abstract class PrintData
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks a string with newlines after the max line length
|
||||
*
|
||||
* @param data String
|
||||
* @return same String splitted with \n after the max. line lenght
|
||||
* @return the same String split with \n after max line length
|
||||
*/
|
||||
protected String breakLines(String data)
|
||||
{
|
||||
protected String breakLines(String data) {
|
||||
boolean next = false;
|
||||
|
||||
int count = lineBreak;
|
||||
|
||||
if (data.length() > lineBreak) {
|
||||
// Needs to be splitted
|
||||
// Needs to be split
|
||||
next = true;
|
||||
} else {
|
||||
// No need to be splitted
|
||||
}
|
||||
else {
|
||||
// No need to split
|
||||
return data;
|
||||
}
|
||||
|
||||
// first part
|
||||
// First part
|
||||
String tmp = data.substring(0, lineBreak);
|
||||
|
||||
while (next) {
|
||||
@@ -99,21 +98,22 @@ public abstract class PrintData
|
||||
try {
|
||||
tmp = tmp + "\n" + data.substring(count, lineBreak + count);
|
||||
count = count + lineBreak;
|
||||
} catch (Exception e) {
|
||||
// data string not long enough
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Data string not long enough
|
||||
next = false;
|
||||
}
|
||||
}
|
||||
// add the last part
|
||||
// Add the last part
|
||||
return tmp + "\n" + data.substring(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* prints a line of '--------'
|
||||
* @return
|
||||
* Prints a line of dashes '--------'
|
||||
*
|
||||
* @return separator line
|
||||
*/
|
||||
protected String getSeparator()
|
||||
{
|
||||
protected String getSeparator() {
|
||||
String tmp = "-";
|
||||
|
||||
for (int i = 1; i < lineBreak; i++) {
|
||||
@@ -123,12 +123,12 @@ public abstract class PrintData
|
||||
}
|
||||
|
||||
/**
|
||||
* sets a String into the center
|
||||
* Centers a string within the line width
|
||||
*
|
||||
* @param data
|
||||
* @return the centered String
|
||||
* @return the centered string
|
||||
*/
|
||||
protected String setCenter(String data)
|
||||
{
|
||||
protected String setCenter(String data) {
|
||||
int dataLenght = data.length();
|
||||
int prefix = ((lineBreak - dataLenght) / 2);
|
||||
String tmp = " ";
|
||||
@@ -137,18 +137,18 @@ public abstract class PrintData
|
||||
tmp = tmp + " ";
|
||||
}
|
||||
tmp = tmp + data;
|
||||
|
||||
|
||||
return breakLines(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* sets a String right-justified after an prefix
|
||||
* Right-justifies a string after a prefix
|
||||
*
|
||||
* @param prefix
|
||||
* @param data
|
||||
* @return the right-justified String
|
||||
* @return the right-justified string
|
||||
*/
|
||||
protected String setRight(String prefix, String data)
|
||||
{
|
||||
protected String setRight(String prefix, String data) {
|
||||
|
||||
int prefixLenght = prefix.length();
|
||||
|
||||
@@ -173,7 +173,7 @@ public abstract class PrintData
|
||||
}
|
||||
|
||||
/**
|
||||
* How the print String or Strings are made
|
||||
* Defines how the print string(s) are generated
|
||||
*/
|
||||
abstract protected void generatePrintString();
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.jFxKasse.datatypes;
|
||||
|
||||
public class PrintDataSimple extends PrintData
|
||||
{
|
||||
public class PrintDataSimple extends PrintData {
|
||||
|
||||
private String printString;
|
||||
|
||||
/**
|
||||
* Constructor with all data that is not in the DB
|
||||
* Constructor with all data that is not stored in the DB
|
||||
*
|
||||
* @param lineBreak
|
||||
* @param headerSpace
|
||||
* @param footerSpace
|
||||
@@ -14,28 +14,25 @@ public class PrintDataSimple extends PrintData
|
||||
* @param header
|
||||
* @param footer
|
||||
*/
|
||||
public PrintDataSimple(int lineBreak, int headerSpace, int footerSpace,
|
||||
String timeAndDatePrint, String header, String footer)
|
||||
{
|
||||
super(lineBreak, headerSpace, footerSpace, timeAndDatePrint, header,
|
||||
footer);
|
||||
public PrintDataSimple(int lineBreak, int headerSpace, int footerSpace, String timeAndDatePrint, String header,
|
||||
String footer) {
|
||||
super(lineBreak, headerSpace, footerSpace, timeAndDatePrint, header, footer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the String
|
||||
* @return the final Print String
|
||||
* Generates the print string
|
||||
*
|
||||
* @return the final print string
|
||||
*/
|
||||
public String getPrintString()
|
||||
{
|
||||
public String getPrintString() {
|
||||
generatePrintString();
|
||||
return this.printString;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generatePrintString()
|
||||
{
|
||||
protected void generatePrintString() {
|
||||
/* Header */
|
||||
String header = "\n";
|
||||
String header = " ";
|
||||
for (int i = 1; i < headerSpace; i++) {
|
||||
header = header + "\n";
|
||||
}
|
||||
@@ -45,16 +42,14 @@ public class PrintDataSimple extends PrintData
|
||||
|
||||
/* Info */
|
||||
|
||||
String info = setRight("Bestellung: ", timeAndDateOrder) + "\n"
|
||||
+ setRight("Druck: ", timeAndDatePrint) + "\n"
|
||||
String info = setRight("Bestellung: ", timeAndDateOrder) + "\n" + setRight("Druck: ", timeAndDatePrint) + "\n"
|
||||
+ setRight("Bestellnummer: ", jobID);
|
||||
|
||||
/* Positionen */
|
||||
|
||||
String positionen = "\n";
|
||||
|
||||
int posCount = positionsQuantity.length()
|
||||
- positionsQuantity.replace(";", "").length() + 1;
|
||||
int posCount = positionsQuantity.length() - positionsQuantity.replace(";", "").length() + 1;
|
||||
|
||||
String[] positionQuantity = positionsQuantity.split(";");
|
||||
|
||||
@@ -65,8 +60,7 @@ public class PrintDataSimple extends PrintData
|
||||
for (int i = 0; i < posCount; i++) { // All different posNames
|
||||
int quantity = Integer.parseInt(positionQuantity[i]);
|
||||
for (int j = 0; j < quantity; j++) { // quantities
|
||||
positionen = positionen + setRight(breakLines(positionName[i]),
|
||||
positionValue[i] + " €") + "\n";
|
||||
positionen = positionen + setRight(breakLines(positionName[i]), positionValue[i] + " €") + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,9 +79,8 @@ public class PrintDataSimple extends PrintData
|
||||
|
||||
/* Build final Print String */
|
||||
|
||||
printString = header + "\n" + getSeparator() + "\n" + info + "\n"
|
||||
+ getSeparator() + "\n" + positionen + "\n" + getSeparator() + "\n"
|
||||
+ price + "\n" + getSeparator() + "\n" + footer;
|
||||
printString = header + "\n" + getSeparator() + "\n" + info + "\n" + getSeparator() + "\n" + positionen + "\n"
|
||||
+ getSeparator() + "\n" + price + "\n" + getSeparator() + "\n" + footer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ package com.jFxKasse.datatypes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PrintDataSplitted extends PrintData
|
||||
{
|
||||
public class PrintDataSplitted extends PrintData {
|
||||
private ArrayList<String> printString = new ArrayList<String>();
|
||||
|
||||
private ArrayList<Category> categories = new ArrayList<Category>();
|
||||
@@ -11,7 +10,8 @@ public class PrintDataSplitted extends PrintData
|
||||
private int categoryCount = 0;
|
||||
|
||||
/**
|
||||
* Constructor with all data that is not in the DB
|
||||
* Constructor with all data that is not stored in the DB
|
||||
*
|
||||
* @param lineBreak
|
||||
* @param headerSpace
|
||||
* @param footerSpace
|
||||
@@ -19,31 +19,28 @@ public class PrintDataSplitted extends PrintData
|
||||
* @param header
|
||||
* @param footer
|
||||
*/
|
||||
public PrintDataSplitted(int lineBreak, int headerSpace, int footerSpace,
|
||||
String timeAndDatePrint, String header, String footer)
|
||||
{
|
||||
super(lineBreak, headerSpace, footerSpace, timeAndDatePrint, header,
|
||||
footer);
|
||||
public PrintDataSplitted(int lineBreak, int headerSpace, int footerSpace, String timeAndDatePrint, String header,
|
||||
String footer) {
|
||||
super(lineBreak, headerSpace, footerSpace, timeAndDatePrint, header, footer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the String
|
||||
* @return the final Array with the Print Strings
|
||||
* Generates the print strings
|
||||
*
|
||||
* @return the final array of print strings
|
||||
*/
|
||||
public ArrayList<String> getPrintStrings()
|
||||
{
|
||||
public ArrayList<String> getPrintStrings() {
|
||||
generatePrintString();
|
||||
return printString;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generatePrintString()
|
||||
{
|
||||
protected void generatePrintString() {
|
||||
|
||||
String firstBill;
|
||||
|
||||
/* Header */
|
||||
String header = "\n";
|
||||
String header = "-";
|
||||
for (int i = 1; i < headerSpace; i++) {
|
||||
header = header + "\n";
|
||||
}
|
||||
@@ -53,11 +50,10 @@ public class PrintDataSplitted extends PrintData
|
||||
|
||||
/* Info */
|
||||
|
||||
String info = setRight("Bestellung: ", timeAndDateOrder) + "\n"
|
||||
+ setRight("Druck: ", timeAndDatePrint) + "\n"
|
||||
String info = setRight("Bestellung: ", timeAndDateOrder) + "\n" + setRight("Druck: ", timeAndDatePrint) + "\n"
|
||||
+ setRight("Bestellnummer: ", jobID);
|
||||
|
||||
/* Splitted Bills */
|
||||
/* Splitted Bills by Category */
|
||||
|
||||
/* Price */
|
||||
|
||||
@@ -74,21 +70,19 @@ public class PrintDataSplitted extends PrintData
|
||||
|
||||
/* Build first Print String */
|
||||
|
||||
firstBill = header + "\n" + getSeparator() + "\n" + info + "\n"
|
||||
+ getSeparator() + "\n" + setCenter("Bon wurde aufgeteilt") + "\n"
|
||||
+ getSeparator() + "\n" + price + "\n" + getSeparator() + "\n"
|
||||
+ footer;
|
||||
firstBill = header + "\n" + getSeparator() + "\n" + info + "\n" + getSeparator() + "\n"
|
||||
+ setCenter("Bon wurde aufgeteilt") + "\n" + getSeparator() + "\n" + price + "\n" + getSeparator()
|
||||
+ "\n" + footer;
|
||||
|
||||
printString.add(firstBill);
|
||||
|
||||
/* first bill ends here */
|
||||
/* First bill ends here */
|
||||
|
||||
/* Categories in extra bills */
|
||||
/* Category-specific extra bills */
|
||||
|
||||
String positions = null;
|
||||
|
||||
int posCount = positionsQuantity.length()
|
||||
- positionsQuantity.replace(";", "").length() + 1;
|
||||
int posCount = positionsQuantity.length() - positionsQuantity.replace(";", "").length() + 1;
|
||||
|
||||
String[] positionQuantity = positionsQuantity.split(";");
|
||||
String[] positionName = positionsName.split(";");
|
||||
@@ -97,17 +91,16 @@ public class PrintDataSplitted extends PrintData
|
||||
|
||||
for (int i = 0; i < posCount; i++) { // All different posNames
|
||||
int quantity = Integer.parseInt(positionQuantity[i]);
|
||||
insertToCategory(quantity, positionName[i], positionValue[i],
|
||||
positionCategory[i]);
|
||||
insertToCategory(quantity, positionName[i], positionValue[i], positionCategory[i]);
|
||||
}
|
||||
|
||||
// loops through all categories
|
||||
// Loops through all categories
|
||||
for (int i = 0; i < categories.size(); i++) {
|
||||
|
||||
String thisBill;
|
||||
|
||||
/* Header */
|
||||
header = "\n";
|
||||
header = " ";
|
||||
for (int o = 1; o < headerSpace; o++) {
|
||||
header = header + "\n";
|
||||
}
|
||||
@@ -117,8 +110,7 @@ public class PrintDataSplitted extends PrintData
|
||||
|
||||
/* Info */
|
||||
|
||||
info = setRight("Bestellung: ", timeAndDateOrder) + "\n"
|
||||
+ setRight("Druck: ", timeAndDatePrint) + "\n"
|
||||
info = setRight("Bestellung: ", timeAndDateOrder) + "\n" + setRight("Druck: ", timeAndDatePrint) + "\n"
|
||||
+ setRight("Bestellnummer: ", jobID);
|
||||
|
||||
/* Positions */
|
||||
@@ -134,11 +126,9 @@ public class PrintDataSplitted extends PrintData
|
||||
}
|
||||
footer = footer + "_";
|
||||
|
||||
thisBill = header + "\n" + getSeparator() + "\n" + info + "\n"
|
||||
+ getSeparator() + "\n"
|
||||
+ setCenter(categories.get(i).getCategoryName()) + "\n"
|
||||
+ getSeparator() + positions + "\n" + getSeparator() + "\n"
|
||||
+ footer;
|
||||
thisBill = header + "\n" + getSeparator() + "\n" + info + "\n" + getSeparator() + "\n"
|
||||
+ setCenter(categories.get(i).getCategoryName()) + "\n" + getSeparator() + positions + "\n"
|
||||
+ getSeparator() + "\n" + footer;
|
||||
|
||||
printString.add(thisBill);
|
||||
|
||||
@@ -146,9 +136,7 @@ public class PrintDataSplitted extends PrintData
|
||||
|
||||
}
|
||||
|
||||
private void insertToCategory(int quantity, String name, String value,
|
||||
String category)
|
||||
{
|
||||
private void insertToCategory(int quantity, String name, String value, String category) {
|
||||
boolean createNewCategorie = true;
|
||||
for (int i = 0; i < categoryCount; i++) {
|
||||
if (category.equals(categories.get(i).getCategoryName())) {
|
||||
@@ -158,7 +146,7 @@ public class PrintDataSplitted extends PrintData
|
||||
}
|
||||
|
||||
if (createNewCategorie) {
|
||||
// position has a new category
|
||||
// Position has a new category
|
||||
categories.add(new Category(category));
|
||||
categories.get(categoryCount).addPosition(quantity, name, value, this);
|
||||
categoryCount++;
|
||||
|
||||
@@ -5,46 +5,38 @@ import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
public class tableDataCurrentOrder
|
||||
{
|
||||
public class tableDataCurrentOrder {
|
||||
|
||||
private final StringProperty position = new SimpleStringProperty();
|
||||
|
||||
private final IntegerProperty quantity = new SimpleIntegerProperty();
|
||||
|
||||
public tableDataCurrentOrder(final String pPosition, final Integer pQuantity)
|
||||
{
|
||||
public tableDataCurrentOrder(final String pPosition, final Integer pQuantity) {
|
||||
this.position.set(pPosition);
|
||||
this.quantity.set(pQuantity);
|
||||
}
|
||||
|
||||
public StringProperty positionProperty()
|
||||
{
|
||||
public StringProperty positionProperty() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public IntegerProperty quantityProperty()
|
||||
{
|
||||
public IntegerProperty quantityProperty() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public String getPosition()
|
||||
{
|
||||
public String getPosition() {
|
||||
return positionProperty().get();
|
||||
}
|
||||
|
||||
public Integer getQuantity()
|
||||
{
|
||||
public Integer getQuantity() {
|
||||
return quantityProperty().get();
|
||||
}
|
||||
|
||||
public final void setPosition(String pPosition)
|
||||
{
|
||||
public final void setPosition(String pPosition) {
|
||||
positionProperty().set(pPosition);
|
||||
}
|
||||
|
||||
public final void setQuantity(int pQuantity)
|
||||
{
|
||||
public final void setQuantity(int pQuantity) {
|
||||
quantityProperty().set(pQuantity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
public class tableDataJob
|
||||
{
|
||||
public class tableDataJob {
|
||||
|
||||
private final IntegerProperty number = new SimpleIntegerProperty();
|
||||
|
||||
@@ -18,9 +17,8 @@ public class tableDataJob
|
||||
|
||||
private final StringProperty value = new SimpleStringProperty();
|
||||
|
||||
public tableDataJob(final Integer pNumber, final String pTime,
|
||||
final String pPositionen, final String pStatus, final String pValue)
|
||||
{
|
||||
public tableDataJob(final Integer pNumber, final String pTime, final String pPositionen, final String pStatus,
|
||||
final String pValue) {
|
||||
this.number.set(pNumber);
|
||||
this.time.set(pTime);
|
||||
this.positionen.set(pPositionen);
|
||||
@@ -29,78 +27,63 @@ public class tableDataJob
|
||||
|
||||
}
|
||||
|
||||
public IntegerProperty numberProperty()
|
||||
{
|
||||
public IntegerProperty numberProperty() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public StringProperty timeProperty()
|
||||
{
|
||||
public StringProperty timeProperty() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public StringProperty positionProperty()
|
||||
{
|
||||
public StringProperty positionProperty() {
|
||||
return positionen;
|
||||
}
|
||||
|
||||
public StringProperty statusProperty()
|
||||
{
|
||||
public StringProperty statusProperty() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public StringProperty valueProperty()
|
||||
{
|
||||
public StringProperty valueProperty() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Integer getNumber()
|
||||
{
|
||||
public Integer getNumber() {
|
||||
return numberProperty().get();
|
||||
}
|
||||
|
||||
public String getTime()
|
||||
{
|
||||
public String getTime() {
|
||||
return timeProperty().get();
|
||||
}
|
||||
|
||||
public String getPosition()
|
||||
{
|
||||
public String getPosition() {
|
||||
return positionProperty().get();
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
public String getStatus() {
|
||||
return statusProperty().get();
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
public String getValue() {
|
||||
return valueProperty().get();
|
||||
}
|
||||
|
||||
public final void setNumber(int pNumber)
|
||||
{
|
||||
public final void setNumber(int pNumber) {
|
||||
numberProperty().set(pNumber);
|
||||
}
|
||||
|
||||
public final void setTime(String pTime)
|
||||
{
|
||||
public final void setTime(String pTime) {
|
||||
timeProperty().set(pTime);
|
||||
}
|
||||
|
||||
public final void setPosition(String pPosition)
|
||||
{
|
||||
public final void setPosition(String pPosition) {
|
||||
positionProperty().set(pPosition);
|
||||
}
|
||||
|
||||
public final void setStatus(String pStatus)
|
||||
{
|
||||
public final void setStatus(String pStatus) {
|
||||
statusProperty().set(pStatus);
|
||||
}
|
||||
|
||||
public final void setValue(String pValue)
|
||||
{
|
||||
public final void setValue(String pValue) {
|
||||
valueProperty().set(pValue);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,21 +5,20 @@ import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
public class tableDataPositionen
|
||||
{ // data-object with id, name, value, color
|
||||
public class tableDataPositionen { // Data object with id, name, value, color
|
||||
|
||||
private final IntegerProperty id = new SimpleIntegerProperty();
|
||||
|
||||
private final StringProperty name = new SimpleStringProperty();
|
||||
|
||||
private final StringProperty value = new SimpleStringProperty();
|
||||
|
||||
|
||||
private final StringProperty cat = new SimpleStringProperty();
|
||||
|
||||
|
||||
private final StringProperty color = new SimpleStringProperty();
|
||||
|
||||
public tableDataPositionen(final int id, final String name, final String value, final String cat, final String color)
|
||||
{
|
||||
public tableDataPositionen(final int id, final String name, final String value, final String cat,
|
||||
final String color) {
|
||||
this.id.set(id);
|
||||
this.name.set(name);
|
||||
this.value.set(value);
|
||||
@@ -27,75 +26,63 @@ public class tableDataPositionen
|
||||
this.color.set(color);
|
||||
}
|
||||
|
||||
public IntegerProperty idProperty()
|
||||
{
|
||||
public IntegerProperty idProperty() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public StringProperty nameProperty()
|
||||
{
|
||||
public StringProperty nameProperty() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public StringProperty valueProperty()
|
||||
{
|
||||
public StringProperty valueProperty() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public StringProperty catProperty() {
|
||||
return cat;
|
||||
}
|
||||
|
||||
public StringProperty colorProperty()
|
||||
{
|
||||
|
||||
public StringProperty colorProperty() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public int getID()
|
||||
{
|
||||
public int getID() {
|
||||
return idProperty().get();
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
public String getName() {
|
||||
return nameProperty().get();
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
public String getValue() {
|
||||
return valueProperty().get();
|
||||
}
|
||||
|
||||
|
||||
public String getCat() {
|
||||
return catProperty().get();
|
||||
}
|
||||
|
||||
public String getColor()
|
||||
{
|
||||
|
||||
public String getColor() {
|
||||
return colorProperty().get();
|
||||
}
|
||||
|
||||
public final void setID(int id)
|
||||
{
|
||||
public final void setID(int id) {
|
||||
idProperty().set(id);
|
||||
}
|
||||
|
||||
public final void setName(String name)
|
||||
{
|
||||
public final void setName(String name) {
|
||||
nameProperty().set(name);
|
||||
}
|
||||
|
||||
public final void setValue(String value)
|
||||
{
|
||||
public final void setValue(String value) {
|
||||
valueProperty().set(value);
|
||||
}
|
||||
|
||||
|
||||
public final void setCat(String cat) {
|
||||
catProperty().set(cat);
|
||||
}
|
||||
|
||||
public final void setColor(String color)
|
||||
{
|
||||
|
||||
public final void setColor(String color) {
|
||||
colorProperty().set(color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
|
||||
/* JavaFX CSS - Leave this comment until you have at least one rule which uses -fx-Property */
|
||||
@@ -0,0 +1,677 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
generate_report.py – Erstellt einen deutschen HTML-Bericht aus einer SQLite-Datenbank
|
||||
(Schema: category, positionen, jobs) – erstellt mit jFxKasse.
|
||||
|
||||
Usage:
|
||||
python generate_report.py <database.db> [-o output.html]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SOFTWARE_NAME = "jFxKasse"
|
||||
SOFTWARE_URL = "https://git.mosad.xyz/localhorst/jFxKasse"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Data helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def load_db(path: str) -> sqlite3.Connection:
|
||||
con = sqlite3.connect(path)
|
||||
con.row_factory = sqlite3.Row
|
||||
return con
|
||||
|
||||
|
||||
def parse_time(t: str) -> datetime:
|
||||
return datetime.strptime(t.strip(), "%H:%M %d.%m.%Y")
|
||||
|
||||
|
||||
def fmt_dt(dt: datetime) -> str:
|
||||
return dt.strftime("%d.%m.%Y %H:%M Uhr")
|
||||
|
||||
|
||||
def fmt_dur(td: timedelta) -> str:
|
||||
total = int(td.total_seconds())
|
||||
h, rem = divmod(total, 3600)
|
||||
m = rem // 60
|
||||
return f"{h} Std. {m} Min."
|
||||
|
||||
|
||||
def fmt_eur(val) -> str:
|
||||
try:
|
||||
return f"{float(val):.2f} €"
|
||||
except Exception:
|
||||
return str(val)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Chart helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
CHARTJS = "https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"
|
||||
|
||||
_chart_id = 0
|
||||
|
||||
|
||||
def next_id(prefix="chart") -> str:
|
||||
global _chart_id
|
||||
_chart_id += 1
|
||||
return f"{prefix}_{_chart_id}"
|
||||
|
||||
|
||||
PALETTE = [
|
||||
"#c0392b",
|
||||
"#2980b9",
|
||||
"#27ae60",
|
||||
"#8e44ad",
|
||||
"#d35400",
|
||||
"#16a085",
|
||||
"#f39c12",
|
||||
"#2c3e50",
|
||||
"#7f8c8d",
|
||||
"#e74c3c",
|
||||
"#1abc9c",
|
||||
"#9b59b6",
|
||||
"#e67e22",
|
||||
"#34495e",
|
||||
"#95a5a6",
|
||||
]
|
||||
|
||||
|
||||
def bar_chart_html(
|
||||
labels: list,
|
||||
values: list,
|
||||
xlabel: str = "",
|
||||
ylabel: str = "",
|
||||
color: str = "#2c3e50",
|
||||
height: int = 280,
|
||||
) -> str:
|
||||
cid = next_id("bar")
|
||||
data = {
|
||||
"labels": labels,
|
||||
"datasets": [
|
||||
{
|
||||
"label": ylabel,
|
||||
"data": values,
|
||||
"backgroundColor": color,
|
||||
"borderRadius": 4,
|
||||
"borderSkipped": False,
|
||||
}
|
||||
],
|
||||
}
|
||||
opts = {
|
||||
"responsive": True,
|
||||
"maintainAspectRatio": False,
|
||||
"plugins": {"legend": {"display": False}},
|
||||
"scales": {
|
||||
"x": {
|
||||
"title": {
|
||||
"display": bool(xlabel),
|
||||
"text": xlabel,
|
||||
"font": {"size": 11},
|
||||
"color": "#555",
|
||||
},
|
||||
"grid": {"color": "#ebebeb"},
|
||||
"ticks": {"color": "#555", "font": {"size": 10}},
|
||||
},
|
||||
"y": {
|
||||
"title": {
|
||||
"display": bool(ylabel),
|
||||
"text": ylabel,
|
||||
"font": {"size": 11},
|
||||
"color": "#555",
|
||||
},
|
||||
"grid": {"color": "#ebebeb"},
|
||||
"ticks": {"color": "#555", "font": {"size": 10}},
|
||||
"beginAtZero": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
return (
|
||||
f'<div style="position:relative;height:{height}px;width:100%;margin:8px 0;">'
|
||||
f'<canvas id="{cid}"></canvas></div>'
|
||||
f"<script>(function(){{"
|
||||
f'var ctx=document.getElementById("{cid}").getContext("2d");'
|
||||
f'new Chart(ctx,{{type:"bar",'
|
||||
f"data:{json.dumps(data, ensure_ascii=False)},"
|
||||
f"options:{json.dumps(opts, ensure_ascii=False)}}});"
|
||||
f"}})();</script>"
|
||||
)
|
||||
|
||||
|
||||
def pie_chart_html(labels: list, values: list, height: int = 280) -> str:
|
||||
cid = next_id("pie")
|
||||
colors = [PALETTE[i % len(PALETTE)] for i in range(len(labels))]
|
||||
data = {
|
||||
"labels": labels,
|
||||
"datasets": [
|
||||
{
|
||||
"data": values,
|
||||
"backgroundColor": colors,
|
||||
"borderWidth": 2,
|
||||
"borderColor": "#fff",
|
||||
}
|
||||
],
|
||||
}
|
||||
opts = {
|
||||
"responsive": True,
|
||||
"maintainAspectRatio": False,
|
||||
"plugins": {
|
||||
"legend": {
|
||||
"position": "right",
|
||||
"labels": {
|
||||
"font": {
|
||||
"size": 11,
|
||||
"family": "'Source Code Pro','Courier New',monospace",
|
||||
},
|
||||
"color": "#2c2c2c",
|
||||
"padding": 14,
|
||||
"boxWidth": 14,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
return (
|
||||
f'<div style="position:relative;height:{height}px;width:100%;margin:8px 0;">'
|
||||
f'<canvas id="{cid}"></canvas></div>'
|
||||
f"<script>(function(){{"
|
||||
f'var ctx=document.getElementById("{cid}").getContext("2d");'
|
||||
f'new Chart(ctx,{{type:"pie",'
|
||||
f"data:{json.dumps(data, ensure_ascii=False)},"
|
||||
f"options:{json.dumps(opts, ensure_ascii=False)}}});"
|
||||
f"}})();</script>"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Analysis
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def analyse(con: sqlite3.Connection) -> dict:
|
||||
result = {}
|
||||
|
||||
result["categories"] = [
|
||||
dict(r)
|
||||
for r in con.execute("SELECT catid, catname FROM category ORDER BY catid")
|
||||
]
|
||||
|
||||
result["positionen"] = [
|
||||
dict(r)
|
||||
for r in con.execute(
|
||||
"SELECT p.posid, p.name, p.value, c.catname, p.color "
|
||||
"FROM positionen p LEFT JOIN category c ON p.cat=c.catid "
|
||||
"ORDER BY p.posid"
|
||||
)
|
||||
]
|
||||
|
||||
all_jobs = [dict(r) for r in con.execute("SELECT * FROM jobs ORDER BY jobid")]
|
||||
|
||||
times = [parse_time(j["time"]) for j in all_jobs]
|
||||
result["first_job"] = min(times)
|
||||
result["last_job"] = max(times)
|
||||
result["duration"] = result["last_job"] - result["first_job"]
|
||||
result["total_jobs"] = len(all_jobs)
|
||||
result["storniert_count"] = sum(1 for j in all_jobs if j["state"] == "storniert")
|
||||
result["verbucht_count"] = sum(1 for j in all_jobs if j["state"] == "verbucht")
|
||||
|
||||
vj = [j for j in all_jobs if j["state"] == "verbucht"]
|
||||
values = [float(j["jobvalue"]) for j in vj]
|
||||
result["sum_total"] = sum(values)
|
||||
result["avg_value"] = sum(values) / len(values) if values else 0
|
||||
|
||||
min_val = min(values)
|
||||
max_val = max(values)
|
||||
result["min_job"] = next(j for j in vj if float(j["jobvalue"]) == min_val)
|
||||
result["max_job"] = [j for j in vj if float(j["jobvalue"]) == max_val][-1]
|
||||
|
||||
# Overall hourly trend (verbucht, skip empty hours)
|
||||
hour_counts: dict[str, int] = defaultdict(int)
|
||||
for j in vj:
|
||||
dt = parse_time(j["time"])
|
||||
hour_counts[dt.strftime("%d.%m. %H:00")] += 1
|
||||
sorted_hours = sorted(
|
||||
hour_counts, key=lambda k: datetime.strptime(k, "%d.%m. %H:00")
|
||||
)
|
||||
result["trend_labels"] = sorted_hours
|
||||
result["trend_values"] = [hour_counts[k] for k in sorted_hours]
|
||||
|
||||
# Per-category aggregations (expand semicolon-separated fields)
|
||||
cat_units: dict[str, int] = defaultdict(int)
|
||||
cat_revenue: dict[str, float] = defaultdict(float)
|
||||
cat_pos_units: dict[str, dict[str, int]] = defaultdict(lambda: defaultdict(int))
|
||||
cat_pos_revenue: dict[str, dict[str, float]] = defaultdict(
|
||||
lambda: defaultdict(float)
|
||||
)
|
||||
cat_day_hour: dict[str, dict[str, dict[int, int]]] = defaultdict(
|
||||
lambda: defaultdict(lambda: defaultdict(int))
|
||||
)
|
||||
|
||||
for j in vj:
|
||||
dt = parse_time(j["time"])
|
||||
day = dt.strftime("%d.%m.%Y")
|
||||
qtys = j["positionen_quantity"].split(";")
|
||||
names = j["positionen_name"].split(";")
|
||||
vals = j["positionen_value"].split(";")
|
||||
cats = j["positionen_cat"].split(";")
|
||||
for qty, name, val, cat in zip(qtys, names, vals, cats):
|
||||
cat = cat.strip()
|
||||
name = name.strip()
|
||||
try:
|
||||
q = int(qty.strip())
|
||||
except ValueError:
|
||||
q = 1
|
||||
try:
|
||||
v = float(val.strip())
|
||||
except ValueError:
|
||||
v = 0.0
|
||||
cat_units[cat] += q
|
||||
cat_revenue[cat] += v * q
|
||||
cat_pos_units[cat][name] += q
|
||||
cat_pos_revenue[cat][name] += v * q
|
||||
cat_day_hour[cat][day][dt.hour] += q
|
||||
|
||||
result["cat_units"] = dict(cat_units)
|
||||
result["cat_revenue"] = dict(cat_revenue)
|
||||
result["cat_pos_units"] = {c: dict(p) for c, p in cat_pos_units.items()}
|
||||
result["cat_pos_revenue"] = {c: dict(p) for c, p in cat_pos_revenue.items()}
|
||||
result["cat_day_hour"] = {
|
||||
cat: {day: dict(hours) for day, hours in days.items()}
|
||||
for cat, days in cat_day_hour.items()
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CSS
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
CSS = """
|
||||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Source+Code+Pro:wght@400;600&display=swap');
|
||||
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
|
||||
:root{
|
||||
--ink:#1a1a1a;--paper:#f7f4ef;--rule:#d4c9b8;
|
||||
--red:#c0392b;--muted:#6b6560;--accent:#2c3e50;--white:#fff;
|
||||
}
|
||||
body{font-family:'Source Code Pro','Courier New',monospace;background:var(--paper);
|
||||
color:var(--ink);font-size:13px;line-height:1.7;}
|
||||
.page-wrap{max-width:980px;margin:0 auto;padding:48px 40px;}
|
||||
|
||||
.report-header{border-top:4px solid var(--ink);border-bottom:2px solid var(--rule);
|
||||
padding:28px 0 24px;margin-bottom:40px;}
|
||||
.report-header h1{font-family:'Playfair Display',Georgia,serif;font-size:2.1rem;
|
||||
letter-spacing:-0.02em;color:var(--ink);margin-bottom:4px;}
|
||||
.report-header .sub{color:var(--muted);font-size:11px;letter-spacing:0.08em;text-transform:uppercase;}
|
||||
.sw-link{display:inline-block;margin-top:10px;font-size:11px;color:var(--accent);
|
||||
text-decoration:none;border-bottom:1px solid var(--accent);opacity:.8;}
|
||||
.sw-link:hover{opacity:1;}
|
||||
.meta-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
|
||||
gap:8px 24px;margin-top:18px;padding-top:16px;border-top:1px solid var(--rule);}
|
||||
.meta-item{display:flex;flex-direction:column;gap:2px;}
|
||||
.meta-label{font-size:10px;text-transform:uppercase;letter-spacing:0.1em;color:var(--muted);}
|
||||
.meta-value{font-weight:600;color:var(--ink);font-size:12px;}
|
||||
|
||||
.section{margin-bottom:48px;}
|
||||
.section-title{font-family:'Playfair Display',Georgia,serif;font-size:1.25rem;
|
||||
border-bottom:2px solid var(--ink);padding-bottom:6px;margin-bottom:20px;color:var(--accent);}
|
||||
.section-sub{font-size:10px;text-transform:uppercase;letter-spacing:0.1em;
|
||||
color:var(--muted);margin-bottom:14px;margin-top:-10px;}
|
||||
|
||||
table{width:100%;border-collapse:collapse;font-size:12px;}
|
||||
th{background:var(--ink);color:var(--paper);text-align:left;padding:8px 12px;
|
||||
font-weight:600;letter-spacing:0.05em;font-size:10px;text-transform:uppercase;}
|
||||
td{padding:7px 12px;border-bottom:1px solid var(--rule);vertical-align:top;}
|
||||
tr:nth-child(even) td{background:rgba(0,0,0,.025);}
|
||||
|
||||
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(155px,1fr));
|
||||
gap:14px;margin-bottom:22px;}
|
||||
.kpi-card{border:1px solid var(--rule);padding:15px 16px;background:var(--white);position:relative;}
|
||||
.kpi-card::before{content:'';position:absolute;top:0;left:0;width:3px;height:100%;background:var(--red);}
|
||||
.kpi-label{font-size:10px;text-transform:uppercase;letter-spacing:0.1em;color:var(--muted);margin-bottom:3px;}
|
||||
.kpi-value{font-family:'Playfair Display',Georgia,serif;font-size:1.45rem;color:var(--ink);line-height:1.2;}
|
||||
.kpi-sub{font-size:10px;color:var(--muted);margin-top:4px;}
|
||||
|
||||
.dot{display:inline-block;width:10px;height:10px;border-radius:50%;
|
||||
margin-right:6px;vertical-align:middle;border:1px solid rgba(0,0,0,.15);}
|
||||
|
||||
.chart-grid-2{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:24px;margin-top:8px;}
|
||||
.chart-box{border:1px solid var(--rule);padding:18px 18px 14px;background:var(--white);}
|
||||
.chart-box-title{font-size:11px;font-weight:600;text-transform:uppercase;
|
||||
letter-spacing:0.08em;color:var(--accent);margin-bottom:3px;}
|
||||
.chart-box-note{font-size:11px;color:var(--muted);margin-bottom:8px;}
|
||||
|
||||
.rule{border:none;border-top:1px solid var(--rule);margin:32px 0;}
|
||||
|
||||
.report-footer{border-top:2px solid var(--ink);padding-top:14px;margin-top:48px;
|
||||
font-size:10px;color:var(--muted);display:flex;justify-content:space-between;flex-wrap:wrap;gap:4px;}
|
||||
.report-footer a{color:inherit;}
|
||||
|
||||
@media print{
|
||||
body{background:#fff;}
|
||||
.page-wrap{padding:20px;}
|
||||
.chart-grid-2{grid-template-columns:1fr 1fr;}
|
||||
.section,.kpi-grid,.chart-box{page-break-inside:avoid;}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# HTML builder
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def build_html(db_path: str, data: dict, export_time: datetime) -> str:
|
||||
p = Path(db_path)
|
||||
display_name = p.name
|
||||
file_size_kb = p.stat().st_size / 1024
|
||||
file_mtime = datetime.fromtimestamp(p.stat().st_mtime)
|
||||
|
||||
# ---- Header ----
|
||||
header = f"""
|
||||
<div class="report-header">
|
||||
<div class="sub">Auswertungsbericht · Kassensystem</div>
|
||||
<h1>{display_name}</h1>
|
||||
<a class="sw-link" href="{SOFTWARE_URL}" target="_blank">Erstellt mit {SOFTWARE_NAME}</a>
|
||||
<div class="meta-grid">
|
||||
<div class="meta-item"><span class="meta-label">Dateiname</span>
|
||||
<span class="meta-value">{display_name}</span></div>
|
||||
<div class="meta-item"><span class="meta-label">Dateigröße</span>
|
||||
<span class="meta-value">{file_size_kb:.1f} KB</span></div>
|
||||
<div class="meta-item"><span class="meta-label">Erstellt am</span>
|
||||
<span class="meta-value">{file_mtime.strftime('%d.%m.%Y %H:%M Uhr')}</span></div>
|
||||
<div class="meta-item"><span class="meta-label">Export um</span>
|
||||
<span class="meta-value">{export_time.strftime('%d.%m.%Y %H:%M Uhr')}</span></div>
|
||||
</div>
|
||||
</div>"""
|
||||
|
||||
# ---- Kategorien ----
|
||||
cat_rows = "".join(
|
||||
f'<tr><td>{c["catid"]}</td><td>{c["catname"]}</td></tr>'
|
||||
for c in data["categories"]
|
||||
)
|
||||
section_cat = f"""
|
||||
<div class="section">
|
||||
<div class="section-title">Kategorien</div>
|
||||
<table>
|
||||
<thead><tr><th>#</th><th>Kategoriename</th></tr></thead>
|
||||
<tbody>{cat_rows}</tbody>
|
||||
</table>
|
||||
</div>"""
|
||||
|
||||
# ---- Positionen ----
|
||||
pos_rows = "".join(
|
||||
f'<tr><td>{pos["posid"]}</td>'
|
||||
f'<td><span class="dot" style="background:{pos["color"]};"></span>{pos["name"]}</td>'
|
||||
f'<td>{fmt_eur(pos["value"])}</td>'
|
||||
f'<td>{pos["catname"] or "–"}</td></tr>'
|
||||
for pos in data["positionen"]
|
||||
)
|
||||
section_pos = f"""
|
||||
<div class="section">
|
||||
<div class="section-title">Positionen / Artikel</div>
|
||||
<table>
|
||||
<thead><tr><th>#</th><th>Bezeichnung</th><th>Preis</th><th>Kategorie</th></tr></thead>
|
||||
<tbody>{pos_rows}</tbody>
|
||||
</table>
|
||||
</div>"""
|
||||
|
||||
# ---- Jobs ----
|
||||
min_j = data["min_job"]
|
||||
max_j = data["max_job"]
|
||||
kpi_overview = f"""
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi-card"><div class="kpi-label">Erster Auftrag</div>
|
||||
<div class="kpi-value" style="font-size:.95rem;">{fmt_dt(data["first_job"])}</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Letzter Auftrag</div>
|
||||
<div class="kpi-value" style="font-size:.95rem;">{fmt_dt(data["last_job"])}</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Betriebsdauer</div>
|
||||
<div class="kpi-value" style="font-size:.95rem;">{fmt_dur(data["duration"])}</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Aufträge gesamt</div>
|
||||
<div class="kpi-value">{data["total_jobs"]}</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Verbucht</div>
|
||||
<div class="kpi-value" style="color:#27ae60;">{data["verbucht_count"]}</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Storniert</div>
|
||||
<div class="kpi-value" style="color:#c0392b;">{data["storniert_count"]}</div></div>
|
||||
</div>"""
|
||||
|
||||
kpi_deep = f"""
|
||||
<div class="kpi-grid">
|
||||
<div class="kpi-card"><div class="kpi-label">Gesamtumsatz</div>
|
||||
<div class="kpi-value">{fmt_eur(data["sum_total"])}</div>
|
||||
<div class="kpi-sub">nur verbuchte Aufträge</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Durchschnitt / Auftrag</div>
|
||||
<div class="kpi-value">{fmt_eur(data["avg_value"])}</div>
|
||||
<div class="kpi-sub">nur verbuchte Aufträge</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Kleinster Auftrag</div>
|
||||
<div class="kpi-value">{fmt_eur(min_j["jobvalue"])}</div>
|
||||
<div class="kpi-sub">Job #{min_j["jobid"]} · {min_j["time"]}</div></div>
|
||||
<div class="kpi-card"><div class="kpi-label">Größter Auftrag</div>
|
||||
<div class="kpi-value">{fmt_eur(max_j["jobvalue"])}</div>
|
||||
<div class="kpi-sub">Job #{max_j["jobid"]} · {max_j["time"]}</div></div>
|
||||
</div>"""
|
||||
|
||||
trend_chart = bar_chart_html(
|
||||
data["trend_labels"],
|
||||
data["trend_values"],
|
||||
xlabel="Stunde",
|
||||
ylabel="Anzahl Aufträge",
|
||||
color="#2c3e50",
|
||||
height=300,
|
||||
)
|
||||
|
||||
section_jobs = f"""
|
||||
<div class="section">
|
||||
<div class="section-title">Auftragsübersicht</div>
|
||||
<div class="section-sub">Alle Aufträge</div>
|
||||
{kpi_overview}
|
||||
<hr class="rule">
|
||||
<div class="section-sub">Tiefenanalyse – nur verbuchte Aufträge</div>
|
||||
{kpi_deep}
|
||||
<hr class="rule">
|
||||
<div class="section-sub">Trendverlauf – Aufträge pro Stunde (gesamte Datenbank)</div>
|
||||
{trend_chart}
|
||||
</div>"""
|
||||
|
||||
# ---- Pie 1: Einheiten je Kategorie ----
|
||||
cu = data["cat_units"]
|
||||
pie_units = pie_chart_html(
|
||||
[f"{k} ({v} Stk.)" for k, v in cu.items()], list(cu.values()), height=300
|
||||
)
|
||||
note_units = " · ".join(
|
||||
f"<strong>{k}</strong>: {v} Stk." for k, v in cu.items()
|
||||
)
|
||||
|
||||
# ---- Pie 2: Umsatz je Kategorie ----
|
||||
cr = data["cat_revenue"]
|
||||
pie_revenue = pie_chart_html(
|
||||
[f"{k} ({fmt_eur(v)})" for k, v in cr.items()], list(cr.values()), height=300
|
||||
)
|
||||
note_revenue = " · ".join(
|
||||
f"<strong>{k}</strong>: {fmt_eur(v)}" for k, v in cr.items()
|
||||
)
|
||||
|
||||
section_cat_pies = f"""
|
||||
<div class="section">
|
||||
<div class="section-title">Kategorienauswertung</div>
|
||||
|
||||
<div class="section-sub">Verkaufte Einheiten nach Kategorie (nur verbuchte Aufträge)</div>
|
||||
<div class="chart-box" style="margin-bottom:28px;">
|
||||
<div class="chart-box-title">Einheiten je Kategorie</div>
|
||||
<div class="chart-box-note">{note_units}</div>
|
||||
{pie_units}
|
||||
</div>
|
||||
|
||||
<div class="section-sub">Umsatz nach Kategorie (nur verbuchte Aufträge)</div>
|
||||
<div class="chart-box">
|
||||
<div class="chart-box-title">Umsatz je Kategorie</div>
|
||||
<div class="chart-box-note">{note_revenue}</div>
|
||||
{pie_revenue}
|
||||
</div>
|
||||
</div>"""
|
||||
|
||||
# ---- Pie 3: Positionen je Kategorie ----
|
||||
pos_pie_boxes = ""
|
||||
for cat, pos_dict in sorted(data["cat_pos_units"].items()):
|
||||
labels = list(pos_dict.keys())
|
||||
values = list(pos_dict.values())
|
||||
total = sum(values)
|
||||
note = " · ".join(
|
||||
f"<strong>{l}</strong>: {v}" for l, v in zip(labels, values)
|
||||
)
|
||||
pie = pie_chart_html(
|
||||
[f"{l} ({v})" for l, v in zip(labels, values)], values, height=260
|
||||
)
|
||||
pos_pie_boxes += f"""
|
||||
<div class="chart-box">
|
||||
<div class="chart-box-title">{cat}</div>
|
||||
<div class="chart-box-note">Gesamt: {total} Einheiten</div>
|
||||
{pie}
|
||||
<div class="chart-box-note" style="margin-top:10px;font-size:10px;line-height:1.8;">{note}</div>
|
||||
</div>"""
|
||||
|
||||
section_pos_pies = f"""
|
||||
<div class="section">
|
||||
<div class="section-title">Positionen je Kategorie</div>
|
||||
<div class="section-sub">Verkaufte Stückzahl pro Artikel, aufgeteilt nach Kategorie</div>
|
||||
<div class="chart-grid-2">
|
||||
{pos_pie_boxes}
|
||||
</div>
|
||||
</div>"""
|
||||
|
||||
# ---- Per-category trend per day ----
|
||||
cat_names = sorted(data["cat_day_hour"].keys())
|
||||
cat_trend_boxes = ""
|
||||
for i, cat in enumerate(cat_names):
|
||||
color = PALETTE[i % len(PALETTE)]
|
||||
days = data["cat_day_hour"][cat]
|
||||
for day in sorted(days, key=lambda d: datetime.strptime(d, "%d.%m.%Y")):
|
||||
hours = days[day]
|
||||
labels = [f"{h}:00" for h in sorted(hours)]
|
||||
values = [hours[h] for h in sorted(hours)]
|
||||
chart = bar_chart_html(
|
||||
labels,
|
||||
values,
|
||||
xlabel="Stunde",
|
||||
ylabel="Einheiten",
|
||||
color=color,
|
||||
height=220,
|
||||
)
|
||||
cat_trend_boxes += f"""
|
||||
<div class="chart-box">
|
||||
<div class="chart-box-title">{cat} · {day}</div>
|
||||
{chart}
|
||||
</div>"""
|
||||
|
||||
section_trends = f"""
|
||||
<div class="section">
|
||||
<div class="section-title">Trendverlauf pro Kategorie und Tag</div>
|
||||
<div class="section-sub">Verkaufte Artikel-Einheiten pro Stunde</div>
|
||||
<div class="chart-grid-2">
|
||||
{cat_trend_boxes}
|
||||
</div>
|
||||
</div>"""
|
||||
|
||||
# ---- Footer ----
|
||||
footer = f"""
|
||||
<div class="report-footer">
|
||||
<span>Exportiert am {export_time.strftime('%d.%m.%Y um %H:%M Uhr')}</span>
|
||||
<span>Quelle: {display_name} ·
|
||||
<a href="{SOFTWARE_URL}">{SOFTWARE_NAME}</a></span>
|
||||
</div>"""
|
||||
|
||||
return f"""<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Bericht – {display_name}</title>
|
||||
<script src="{CHARTJS}"></script>
|
||||
<style>{CSS}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-wrap">
|
||||
{header}
|
||||
{section_cat}
|
||||
<hr class="rule">
|
||||
{section_pos}
|
||||
<hr class="rule">
|
||||
{section_jobs}
|
||||
<hr class="rule">
|
||||
{section_cat_pies}
|
||||
<hr class="rule">
|
||||
{section_pos_pies}
|
||||
<hr class="rule">
|
||||
{section_trends}
|
||||
{footer}
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
p = argparse.ArgumentParser(
|
||||
prog="generate_report",
|
||||
description="Erstellt einen deutschen HTML-Bericht aus einer jFxKasse SQLite-Datenbank.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=(
|
||||
"Beispiele:\n"
|
||||
" python generate_report.py kassendaten.db\n"
|
||||
" python generate_report.py kassendaten.db -o bericht.html\n"
|
||||
),
|
||||
)
|
||||
p.add_argument("database", help="Pfad zur SQLite-Datenbankdatei")
|
||||
p.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
metavar="OUTPUT_HTML",
|
||||
help="Pfad zur Ausgabe-HTML-Datei (Standard: <dateiname>_bericht.html)",
|
||||
)
|
||||
return p
|
||||
|
||||
|
||||
def main():
|
||||
parser = build_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
db_path = Path(args.database)
|
||||
if not db_path.exists():
|
||||
parser.error(f"Datei nicht gefunden: {db_path}")
|
||||
|
||||
out_path = (
|
||||
Path(args.output)
|
||||
if args.output
|
||||
else db_path.with_name(db_path.stem + "_bericht.html")
|
||||
)
|
||||
|
||||
print(f"[lade] {db_path.name}")
|
||||
con = load_db(str(db_path))
|
||||
|
||||
print("[analyse] Daten werden ausgewertet …")
|
||||
data = analyse(con)
|
||||
con.close()
|
||||
|
||||
print("[render] HTML wird erstellt …")
|
||||
html = build_html(str(db_path), data, datetime.now())
|
||||
out_path.write_text(html, encoding="utf-8")
|
||||
print(f"[fertig] HTML gespeichert: {out_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
merge_db.py – Merge multiple SQLite databases into one output file.
|
||||
|
||||
Rules
|
||||
-----
|
||||
* Tables `category` and `positionen` are deduplicated by ALL columns
|
||||
(content-based identity, ignoring the local rowid/id column).
|
||||
* Table `jobs` rows from later files are appended with a re-sequenced
|
||||
`jobid` so IDs never clash across files.
|
||||
* Rows are inserted in file order: the first file wins on deduplication
|
||||
for reference tables; job rows from later files come after earlier ones.
|
||||
|
||||
Usage
|
||||
-----
|
||||
python merge_db.py -o merged.db 02.db 03.db [04.db ...]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def get_table_names(con: sqlite3.Connection) -> list[str]:
|
||||
cur = con.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
|
||||
return [row[0] for row in cur.fetchall()]
|
||||
|
||||
|
||||
def get_columns(con: sqlite3.Connection, table: str) -> list[str]:
|
||||
cur = con.execute(f'PRAGMA table_info("{table}")')
|
||||
return [row[1] for row in cur.fetchall()]
|
||||
|
||||
|
||||
def get_create_statement(con: sqlite3.Connection, table: str) -> str:
|
||||
cur = con.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name=?", (table,)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
return row[0] if row else None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Merge logic per table type
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def merge_dedup_table(
|
||||
src: sqlite3.Connection,
|
||||
dst: sqlite3.Connection,
|
||||
table: str,
|
||||
id_col: str,
|
||||
) -> tuple[int, int]:
|
||||
"""
|
||||
Merge a reference table using content-based deduplication.
|
||||
|
||||
Rows are compared on every column *except* `id_col`. If an identical
|
||||
row already exists in `dst`, it is skipped; otherwise it is inserted
|
||||
with a new auto-incremented id.
|
||||
|
||||
Returns (rows_read, rows_inserted).
|
||||
"""
|
||||
cols = get_columns(src, table)
|
||||
content_cols = [c for c in cols if c != id_col]
|
||||
|
||||
if not content_cols:
|
||||
return 0, 0
|
||||
|
||||
placeholders = ", ".join("?" * len(content_cols))
|
||||
select_content = ", ".join(f'"{c}"' for c in content_cols)
|
||||
|
||||
# Build a set of existing content tuples for fast dedup lookup
|
||||
existing: set[tuple] = set()
|
||||
for row in dst.execute(f'SELECT {select_content} FROM "{table}"'):
|
||||
existing.add(row)
|
||||
|
||||
src_rows = src.execute(f'SELECT {select_content} FROM "{table}"').fetchall()
|
||||
inserted = 0
|
||||
for row in src_rows:
|
||||
key = tuple(row)
|
||||
if key not in existing:
|
||||
dst.execute(
|
||||
f'INSERT INTO "{table}" ({select_content}) VALUES ({placeholders})',
|
||||
row,
|
||||
)
|
||||
existing.add(key)
|
||||
inserted += 1
|
||||
|
||||
return len(src_rows), inserted
|
||||
|
||||
|
||||
def merge_jobs_table(
|
||||
src: sqlite3.Connection,
|
||||
dst: sqlite3.Connection,
|
||||
table: str = "jobs",
|
||||
id_col: str = "jobid",
|
||||
) -> tuple[int, int]:
|
||||
"""
|
||||
Append job rows from `src` into `dst`, re-sequencing `id_col` so that
|
||||
new IDs start after the current maximum in `dst`.
|
||||
|
||||
Duplicate detection is done on all columns except `id_col`; rows with
|
||||
identical content are skipped.
|
||||
|
||||
Returns (rows_read, rows_inserted).
|
||||
"""
|
||||
cols = get_columns(src, table)
|
||||
content_cols = [c for c in cols if c != id_col]
|
||||
all_col_str = ", ".join(f'"{c}"' for c in cols)
|
||||
content_str = ", ".join(f'"{c}"' for c in content_cols)
|
||||
placeholders = ", ".join("?" * len(cols))
|
||||
|
||||
# Current max id in destination
|
||||
row = dst.execute(f'SELECT MAX("{id_col}") FROM "{table}"').fetchone()
|
||||
offset = (row[0] or 0)
|
||||
|
||||
# Existing content fingerprints for dedup
|
||||
existing: set[tuple] = set()
|
||||
for row in dst.execute(f'SELECT {content_str} FROM "{table}"'):
|
||||
existing.add(tuple(row))
|
||||
|
||||
src_rows = src.execute(f'SELECT {all_col_str} FROM "{table}"').fetchall()
|
||||
col_index = {c: i for i, c in enumerate(cols)}
|
||||
id_idx = col_index[id_col]
|
||||
|
||||
inserted = 0
|
||||
for row in src_rows:
|
||||
content_key = tuple(row[col_index[c]] for c in content_cols)
|
||||
if content_key in existing:
|
||||
continue # skip duplicate content
|
||||
new_id = row[id_idx] + offset
|
||||
new_row = list(row)
|
||||
new_row[id_idx] = new_id
|
||||
dst.execute(
|
||||
f'INSERT INTO "{table}" ({all_col_str}) VALUES ({placeholders})',
|
||||
new_row,
|
||||
)
|
||||
existing.add(content_key)
|
||||
inserted += 1
|
||||
|
||||
return len(src_rows), inserted
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Schema helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def ensure_table(dst: sqlite3.Connection, src: sqlite3.Connection, table: str) -> None:
|
||||
"""Create `table` in `dst` if it doesn't exist yet, using src's DDL."""
|
||||
exists = dst.execute(
|
||||
"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", (table,)
|
||||
).fetchone()
|
||||
if not exists:
|
||||
ddl = get_create_statement(src, table)
|
||||
if ddl:
|
||||
dst.execute(ddl)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main merge routine
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Map table name → (id_column, merge_strategy)
|
||||
# strategy: "dedup" → content-based dedup, new ids assigned
|
||||
# "jobs" → append with id re-sequencing + content dedup
|
||||
TABLE_CONFIG: dict[str, tuple[str, str]] = {
|
||||
"category": ("catid", "dedup"),
|
||||
"positionen": ("posid", "dedup"),
|
||||
"jobs": ("jobid", "jobs"),
|
||||
}
|
||||
|
||||
|
||||
def merge_databases(input_files: list[Path], output_file: Path) -> None:
|
||||
# Bootstrap: copy first file as the base for the output
|
||||
print(f"[init] Bootstrapping output from '{input_files[0].name}' …")
|
||||
shutil.copy2(input_files[0], output_file)
|
||||
|
||||
dst = sqlite3.connect(output_file)
|
||||
dst.execute("PRAGMA journal_mode=WAL")
|
||||
dst.execute("PRAGMA foreign_keys=OFF")
|
||||
|
||||
try:
|
||||
for src_path in input_files[1:]:
|
||||
print(f"\n[merge] Processing '{src_path.name}' …")
|
||||
src = sqlite3.connect(src_path)
|
||||
|
||||
src_tables = get_table_names(src)
|
||||
for table in src_tables:
|
||||
ensure_table(dst, src, table)
|
||||
config = TABLE_CONFIG.get(table)
|
||||
|
||||
if config is None:
|
||||
# Unknown table: fall back to content-based dedup without
|
||||
# special id handling – treat first column as the id.
|
||||
cols = get_columns(src, table)
|
||||
id_col = cols[0] if cols else None
|
||||
if id_col:
|
||||
read, ins = merge_dedup_table(src, dst, table, id_col)
|
||||
print(f" [{table}] (fallback dedup on '{id_col}') "
|
||||
f"read={read} inserted={ins}")
|
||||
else:
|
||||
id_col, strategy = config
|
||||
if strategy == "dedup":
|
||||
read, ins = merge_dedup_table(src, dst, table, id_col)
|
||||
else:
|
||||
read, ins = merge_jobs_table(src, dst, table, id_col)
|
||||
print(f" [{table}] strategy={strategy} "
|
||||
f"read={read} inserted={ins}")
|
||||
|
||||
dst.commit()
|
||||
src.close()
|
||||
|
||||
finally:
|
||||
dst.close()
|
||||
|
||||
print(f"\n[done] Output written to '{output_file}'")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="merge_db",
|
||||
description=(
|
||||
"Merge multiple SQLite databases into one output file.\n"
|
||||
"Files are merged in the order they are provided.\n"
|
||||
"Duplicate rows are detected by content and inserted only once."
|
||||
),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=(
|
||||
"Examples:\n"
|
||||
" python merge_db.py -o merged.db 02.db 03.db\n"
|
||||
" python merge_db.py -o merged.db 02.db 03.db 04.db 05.db\n"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"inputs",
|
||||
metavar="INPUT_DB",
|
||||
nargs="+",
|
||||
help="Input SQLite database files (in desired merge order)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o", "--output",
|
||||
metavar="OUTPUT_DB",
|
||||
required=True,
|
||||
help="Path for the merged output database (will be overwritten if it exists)",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = build_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
input_paths = [Path(p) for p in args.inputs]
|
||||
output_path = Path(args.output)
|
||||
|
||||
# Validate inputs
|
||||
for p in input_paths:
|
||||
if not p.exists():
|
||||
parser.error(f"Input file not found: {p}")
|
||||
if not p.is_file():
|
||||
parser.error(f"Not a file: {p}")
|
||||
|
||||
if len(input_paths) < 2:
|
||||
parser.error("At least two input files are required.")
|
||||
|
||||
if output_path.exists():
|
||||
print(f"[warn] Output file '{output_path}' already exists and will be overwritten.")
|
||||
|
||||
merge_databases(input_paths, output_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||