first commit
This commit is contained in:
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Pliki generowane przez Mavena
|
||||
target/
|
||||
pom.xml.bak
|
||||
|
||||
# Skompilowane pliki (jeśli kompilujesz ręcznie)
|
||||
bin/
|
||||
|
||||
# Pliki wynikowe generowane przez aplikację
|
||||
output/
|
||||
|
||||
# Ignorowanie ręcznie dodanych bibliotek JAR (zależności obsługuje Maven)
|
||||
*.jar
|
||||
|
||||
# Pliki logów
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Pliki konfiguracyjne IDE
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
.vscode/
|
||||
|
||||
# Pliki systemowe (macOS, Windows)
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
README.html
|
||||
src/main/java/*.bak*
|
||||
|
||||
REJESTR_SPRZEDAZY_VAT_DOKUMENTACJA.txt
|
||||
126
README.md
Normal file
126
README.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 🚀 Konwerter CSV do Optima XML
|
||||
|
||||
Cześć! 👋
|
||||
|
||||
To jest małe narzędzie backendowe (napisane w Javie), które konwertuje pliki CSV do formatu XML zgodnego z Comarch ERP Optima.
|
||||
|
||||
Twoim zadaniem będzie dorobienie do tego fajnego UI/UX, a to jest na razie sam "silnik", który możesz sobie uruchomić i testować.
|
||||
|
||||
## 🤓 Co jest potrzebne?
|
||||
|
||||
1. **Java (JDK)**: Wersja 11 lub nowsza.
|
||||
2. **Maven** (Zalecane): Projekt jest skonfigurowany przy użyciu Mavena. To najprostszy sposób, bo sam pobierze wszystkie potrzebne biblioteki (w tym `opencsv` do czytania CSV).
|
||||
* Aby sprawdzić, czy masz Mavena, wpisz w terminalu: `mvn -version`.
|
||||
|
||||
---
|
||||
|
||||
## 📂 Struktura Projektu
|
||||
|
||||
Struktura katalogów jest bardzo prosta:
|
||||
|
||||
```
|
||||
.
|
||||
├── input/ <-- TU WRZUCASZ PLIKI WEJŚCIOWE
|
||||
│ ├── kontrahenci.csv
|
||||
│ └── rejestr_sprzedazy.csv
|
||||
├── output/ <-- TUTAJ POJAWI SIĘ WYNIKOWY XML
|
||||
│ └── (na razie pusty)
|
||||
├── src/main/java/ <-- CAŁY KOD ŹRÓDŁOWY JAVY
|
||||
│ ├── CsvToOptimaXmlConverter.java (główny plik z logiką)
|
||||
│ └── ValidationException.java (nasz specjalny plik do błędów)
|
||||
└── pom.xml <-- "Mózg" projektu (plik konfiguracyjny Mavena)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ▶️ Jak to uruchomić (Sposób 1 - Zalecany: Maven)
|
||||
|
||||
To najłatwiejszy sposób. Będąc w głównym katalogu projektu (tam, gdzie jest plik `pom.xml`):
|
||||
|
||||
### 1. Kompilacja i pobranie bibliotek
|
||||
|
||||
```bash
|
||||
mvn compile
|
||||
```
|
||||
*Za pierwszym razem Maven połączy się z internetem i pobierze bibliotekę `opencsv`. Następnie skompiluje cały kod.*
|
||||
|
||||
### 2. Uruchomienie programu
|
||||
|
||||
```bash
|
||||
mvn exec:java
|
||||
```
|
||||
*To polecenie uruchomi główną klasę. Jeśli wszystko pójdzie dobrze, w terminalu zobaczysz komunikat o sukcesie, a w katalogu `output/` pojawi się plik `optima_import.xml`.*
|
||||
|
||||
### (Opcjonalnie) Czyszczenie projektu
|
||||
|
||||
```bash
|
||||
mvn clean
|
||||
```
|
||||
*Usuwa skompilowane pliki (katalog `target`), jeśli chcesz zacząć "na czysto".*
|
||||
|
||||
---
|
||||
|
||||
## ▶️ Jak to uruchomić (Sposób 2 - Ręcznie, bez Mavena)
|
||||
|
||||
Jeśli nie masz lub nie chcesz instalować Mavena, możesz to zrobić "klasycznie".
|
||||
|
||||
### 1. Ręczne pobranie biblioteki
|
||||
|
||||
Musisz ręcznie pobrać plik `.jar` biblioteki `opencsv`.
|
||||
* **Wersja:** `5.12.0` (lub nowsza)
|
||||
* **Pobierz z:** [Maven Central (kliknij `jar` obok "Downloads")](https://repo1.maven.org/maven2/com/opencsv/opencsv/5.12.0/opencsv-5.12.0.jar)
|
||||
* **Co zrobić:** Pobrany plik `opencsv-5.12.0.jar` umieść w głównym katalogu projektu (obok `pom.xml` i `input`).
|
||||
|
||||
### 2. Ręczna kompilacja
|
||||
|
||||
Będąc w głównym katalogu, utwórz katalog na skompilowane klasy:
|
||||
```bash
|
||||
mkdir bin
|
||||
```
|
||||
Następnie skompiluj kod, wskazując bibliotekę i katalog docelowy:
|
||||
```bash
|
||||
javac -d bin -cp .:opencsv-5.12.0.jar src/main/java/*.java
|
||||
```
|
||||
|
||||
### 3. Ręczne uruchomienie
|
||||
|
||||
Teraz uruchom program, wskazując w "classpath" zarówno bibliotekę, jak i katalog `bin`:
|
||||
```bash
|
||||
java -cp .:opencsv-5.12.0.jar:bin CsvToOptimaXmlConverter
|
||||
```
|
||||
*(Uwaga: na Windowsie zamiast `:` użyj `;` jako separatora w `classpath`)*
|
||||
|
||||
---
|
||||
|
||||
## 🚦 Ważne informacje
|
||||
|
||||
### Separator CSV
|
||||
|
||||
Program jest obecnie ustawiony na domyślny separator CSV, czyli **przecinek (`,`)**. Wszystkie pliki `.csv` w katalogu `input/` muszą używać przecinków, aby zostały poprawnie wczytane.
|
||||
|
||||
### Walidacja ("Test Pani Marisi")
|
||||
|
||||
Program nie jest "głupi". Zanim zacznie konwersję, sprawdza pliki CSV:
|
||||
* Weryfikuje, czy wszystkie **obowiązkowe kolumny** (np. `AKRONIM` u kontrahenta, `POZ_NETTO` na fakturze) są wypełnione.
|
||||
* Jeśli czegoś brakuje, program **zatrzyma się** i wyświetli w terminalu bardzo czytelny, "ludzki" komunikat o błędzie (np. `Błąd w danych (w fakturze o numerze 'FV/2/2024')! Brakuje wartości w kolumnie: 'POZ_NETTO'.`).
|
||||
|
||||
Dzięki temu od razu wiesz, co i gdzie poprawić w pliku CSV.
|
||||
|
||||
---
|
||||
|
||||
## 🗒️ TODO (Co dalej?)
|
||||
|
||||
Lista zadań do zrobienia, aby ten projekt był jeszcze lepszy:
|
||||
|
||||
* [ ] **Zbudować graficzny interfejs (UI)**, który będzie zawierał:
|
||||
* [ ] Przycisk do wyboru pliku `kontrahenci.csv`.
|
||||
* [ ] Przycisk do wyboru pliku `rejestr_sprzedazy.csv`.
|
||||
* [ ] Przycisk "Start", który uruchamia konwersję.
|
||||
* [ ] Czytelny komunikat (labelka, okienko pop-up) informujący o sukcesie lub wyświetlający błąd walidacji.
|
||||
|
||||
* [ ] **Rozbudować walidację** danych wejściowych (zgodnie z dokumentacją Comarch):
|
||||
* [ ] **Walidacja typów danych** (np. czy `POZ_NETTO` jest liczbą, a `DATA_WYSTAWIENIA` poprawną datą).
|
||||
* [ ] **Walidacja długości pól** (np. czy `AKRONIM` nie przekracza 20 znaków).
|
||||
* [ ] **Walidacja logiki biznesowej** (np. sprawdzenie, czy w rejestrze sprzedaży `POZ_NETTO * (POZ_STAWKA_VAT / 100)` faktycznie równa się `POZ_VAT`, z uwzględnieniem zaokrągleń).
|
||||
|
||||
Powodzenia! Daj znać, jakbyś miała jakieś problemy.
|
||||
BIN
Recording 2026-01-07 151248.mp4
Normal file
BIN
Recording 2026-01-07 151248.mp4
Normal file
Binary file not shown.
43
dependency-reduced-pom.xml
Normal file
43
dependency-reduced-pom.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.twojafirma</groupId>
|
||||
<artifactId>csv-konwerter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<mainClass>OptimaConverterApp</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer>
|
||||
<mainClass>OptimaConverterApp</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<properties>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
</project>
|
||||
14
input/kontrahenci.csv
Normal file
14
input/kontrahenci.csv
Normal file
@@ -0,0 +1,14 @@
|
||||
ID_ZRODLA,AKRONIM,FINALNY,ADRES_STATUS,ADRES_NAZWA1,ADRES_NAZWA2,ADRES_KRAJ,ADRES_MIASTO,ADRES_ULICA,ADRES_NR_DOMU,ADRES_NR_LOKALU,ADRES_KOD_POCZTOWY,ADRES_POCZTA,ADRES_NIP,ADRES_TELEFON1,ADRES_EMAIL
|
||||
KH-001,ABC-POL,Nie,aktualny,ABC Polska Sp. z o.o.,,Polska,Warszawa,Aleje Jerozolimskie,120,,00-001,Warszawa,7531116070,221234567,biuro@abcpolska.pl
|
||||
KH-002,MEBLOMIX,Nie,aktualny,MEBLOMIX Jan Kowalski,,Polska,Kraków,Wielicka,40,12,30-552,Kraków,7532350589,129876543,jan.kowalski@meblomix.pl
|
||||
KH-003,NOWAK,Tak,aktualny,Adam Nowak,,Polska,Nysa,Rynek,5,,48-300,Nysa,7531493878,774123123,adam.nowak@prywatny.pl
|
||||
KH-004,TRANSPOL,Nie,aktualny,Trans-Pol S.A.,Oddział Zachodni,Polska,Wrocław,Logistyczna,1,,55-040,Bielany Wrocławskie,7551831137,713334455,logistyka@transpol.com.pl
|
||||
KH-005,ZIELINSKA,Tak,aktualny,Ewa Zielińska,,Polska,Gdańsk,Długa,22,3,80-831,Gdańsk,6891156409,589998877,ewa.zielinska@gmail.com
|
||||
KH-006,BUDMAX,Nie,aktualny,BUDMAX Hurtownia Budowlana,Piotr Nowakowski,Polska,Poznań,Przemysłowa,50,,61-500,Poznań,7532200602,612223344,zamowienia@budmax-poznan.pl
|
||||
KH-007,TECH-MAX,Nie,aktualny,Technologie Max Sp.k.,,Polska,Warszawa,Prosta,10,,00-850,Warszawa,7532443405,,
|
||||
KH-008,MED-CENTRUM,Nie,aktualny,Centrum Medyczne Zdrowie,,Polska,Łódź,Piotrkowska,5,,90-004,Łódź,7532022419,,
|
||||
KH-009,KOWALSKI,Tak,aktualny,Jan Kowalski Detal,,Polska,Gdynia,Morska,1,,81-001,Gdynia,7471778441,,
|
||||
KH-010,BUD-REM,Nie,aktualny,Usługi Remontowe,,Polska,Katowice,Węglowa,3,,40-100,Katowice,7541436623,,
|
||||
KH-011,EXP-IM,Nie,aktualny,Export Import Ltd,,Polska,Kraków,Floriańska,12,,31-021,Kraków,7531927549,,
|
||||
KH-012,SZKOLA,Nie,aktualny,Szkoła Językowa,,Polska,Poznań,Szkolna,3,,61-832,Poznań,7532439272,,
|
||||
KH-013,LOGISTYK,Nie,aktualny,Logistyka i Transport,,Polska,Wrocław,Autostradowa,99,,54-424,Wrocław,7532446177,,
|
||||
|
18
input/rejestr_sprzedazy.csv
Normal file
18
input/rejestr_sprzedazy.csv
Normal file
@@ -0,0 +1,18 @@
|
||||
REJESTR,NUMER,DATA_WYSTAWIENIA,DATA_SPRZEDAZY,TERMIN,KONTRAHENT_KOD,KONTRAHENT_NAZWA,KONTRAHENT_NIP,ADRES_MIASTO,ADRES_ULICA,POZYCJE_NETTO,POZYCJE_VAT,POZYCJE_STAWKA,POZYCJE_RODZAJ,PLATNOSCI_FORMA,PLATNOSCI_KWOTA,PLATNOSCI_TERMIN,KOLUMNA_KPR
|
||||
SPRZEDAŻ,FS/1/01/2026,2026-01-02,2026-01-02,2026-01-09,ABC-POL,ABC Polska Sp. z o.o.,7531116070,Warszawa,Aleje Jerozolimskie 120,1000.00,230.00,23,usługi,przelew,1230.00,2026-01-09,Sprzedaż
|
||||
SPRZEDAŻ,FS/2/01/2026,2026-01-03,2026-01-03,2026-01-03,MEBLOMIX,MEBLOMIX Jan Kowalski,7532350589,Kraków,Wielicka 40,200.00|50.00,46.00|11.50,23|23,towary|towary,gotówka,307.50,2026-01-03,Sprzedaż
|
||||
SPRZEDAŻ,FS/3/01/2026,2026-01-05,2026-01-05,2026-01-19,BUDMAX,BUDMAX Hurtownia Budowlana,7532200602,Poznań,Przemysłowa 50,500.00|1000.00|250.00|100.00|50.00,115.00|230.00|57.50|23.00|11.50,23|23|23|23|23,towary|towary|towary|usługi|usługi,przelew|gotówka,1737.00|600.00,2026-01-19|2026-01-05,Sprzedaż
|
||||
SPRZEDAŻ,FS/4/01/2026,2026-01-07,2026-01-07,2026-01-14,TRANSPOL,Trans-Pol S.A.,7551831137,Wrocław,Logistyczna 1,100.00|100.00,5.00|8.00,5|8,towary|towary,przelew,213.00,2026-01-14,Sprzedaż
|
||||
SPRZEDAŻ,FS/5/01/2026,2026-01-08,2026-01-08,2026-01-08,ZIELINSKA,Ewa Zielińska,6891156409,Gdańsk,Długa 22,3000.00|500.00|150.00,690.00|115.00|34.50,23|23|23,usługi|towary|towary,karta,4489.50,2026-01-08,Sprzedaż
|
||||
SPRZEDAŻ,FS/6/01/2026,2026-01-10,2026-01-10,2026-01-24,ABC-POL,ABC Polska Sp. z o.o.,7531116070,Warszawa,Aleje Jerozolimskie 120,5000.00|200.00|100.00|50.00,1150.00|46.00|23.00|11.50,23|23|23|23,usługi|towary|towary|towary,przelew,6580.50,2026-01-24,Sprzedaż
|
||||
SPRZEDAŻ,FS/7/01/2026,2026-01-12,2026-01-12,2026-01-12,NOWAK,Adam Nowak,7531493878,Nysa,Rynek 5,1000.00,80.00,8,towary,gotówka,1080.00,2026-01-12,Sprzedaż
|
||||
SPRZEDAŻ,FS/8/01/2026,2026-01-15,2026-01-15,2026-01-29,BUDMAX,BUDMAX Hurtownia Budowlana,7532200602,Poznań,Przemysłowa 50,50.00|50.00|50.00|50.00|50.00|50.00,11.50|11.50|11.50|11.50|11.50|11.50,23|23|23|23|23|23,towary|towary|towary|towary|towary|towary,przelew|gotówka,300.00|69.00,2026-01-29|2026-01-15,Sprzedaż
|
||||
SPRZEDAŻ,FS/9/01/2026,2026-01-18,2026-01-18,2026-01-25,MEBLOMIX,MEBLOMIX Jan Kowalski,7532350589,Kraków,Wielicka 40,1200.00|300.00,276.00|69.00,23|23,towary|usługi,przelew,1845.00,2026-01-25,Sprzedaż
|
||||
SPRZEDAŻ,FS/10/01/2026,2026-01-20,2026-01-20,2026-01-27,ZIELINSKA,Ewa Zielińska,6891156409,Gdańsk,Długa 22,400.00|400.00|400.00|400.00|400.00,92.00|92.00|92.00|92.00|92.00,23|23|23|23|23,usługi|usługi|usługi|usługi|usługi,przelew,2460.00,2026-01-27,Sprzedaż
|
||||
SPRZEDAŻ,FS/11/01/2026,2026-01-21,2026-01-21,2026-01-28,TECH-MAX,Technologie Max Sp.k.,7532443405,Warszawa,Prosta 10,100.00|200.00|300.00,23.00|16.00|0.00,23|8|0,towary|towary|towary,przelew,639.00,2026-01-28,Sprzedaż
|
||||
SPRZEDAŻ,FS/12/01/2026,2026-01-22,2026-01-22,2026-02-05,MED-CENTRUM,Centrum Medyczne Zdrowie,7532022419,Łódź,Piotrkowska 5,1500.00|50.00,0.00|11.50,zw|23,usługi|towary,przelew,1561.50,2026-02-05,Sprzedaż
|
||||
SPRZEDAŻ,FS/13/01/2026,2026-01-23,2026-01-23,2026-01-23,KOWALSKI,Jan Kowalski Detal,7471778441,Gdynia,Morska 1,123.45|67.89,28.39|15.61,23|23,towary|towary,gotówka|karta,100.00|135.34,2026-01-23|2026-01-23,Sprzedaż
|
||||
SPRZEDAŻ,FS/14/01/2026,2026-01-24,2026-01-24,2026-02-24,BUD-REM,Usługi Remontowe,7541436623,Katowice,Węglowa 3,10000.00,2300.00,23,usługi,przelew|przelew,6150.00|6150.00,2026-01-31|2026-02-28,Sprzedaż
|
||||
SPRZEDAŻ,FS/15/01/2026,2026-01-25,2026-01-25,2026-01-25,EXP-IM,Export Import Ltd,7531927549,Kraków,Floriańska 12,2000.00|100.00,0.00|23.00,np|23,towary|usługi,przelew,2123.00,2026-01-25,Sprzedaż
|
||||
SPRZEDAŻ,FS/16/01/2026,2026-01-26,2026-01-26,2026-01-26,SZKOLA,Szkoła Językowa,7532439272,Poznań,Szkolna 3,800.00|45.00,0.00|2.25,zw|5,usługi|towary,gotówka,847.25,2026-01-26,Sprzedaż
|
||||
SPRZEDAŻ,FS/17/01/2026,2026-01-27,2026-01-27,2026-02-10,LOGISTYK,Logistyka i Transport,7532446177,Wrocław,Autostradowa 99,50.00|50.00|50.00|50.00,11.50|4.00|2.50|0.00,23|8|5|0,usługi|usługi|usługi|usługi,przelew,218.00,2026-02-10,Sprzedaż
|
||||
|
1085
optima_import.xml
Normal file
1085
optima_import.xml
Normal file
File diff suppressed because it is too large
Load Diff
57
pom.xml
Normal file
57
pom.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.twojafirma</groupId>
|
||||
<artifactId>csv-konwerter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.opencsv</groupId>
|
||||
<artifactId>opencsv</artifactId>
|
||||
<version>5.12.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<mainClass>OptimaConverterApp</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>OptimaConverterApp</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
615
src/main/java/OptimaConverterApp.java
Normal file
615
src/main/java/OptimaConverterApp.java
Normal file
@@ -0,0 +1,615 @@
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// Importy OpenCSV
|
||||
import com.opencsv.CSVParser;
|
||||
import com.opencsv.CSVParserBuilder;
|
||||
import com.opencsv.CSVReader;
|
||||
import com.opencsv.CSVReaderBuilder;
|
||||
import com.opencsv.exceptions.CsvValidationException;
|
||||
|
||||
// Importy XML
|
||||
import org.w3c.dom.CDATASection;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
public class OptimaConverterApp extends JFrame {
|
||||
|
||||
// --- Klasa wyjątku wewnętrznego ---
|
||||
public static class ValidationException extends Exception {
|
||||
public ValidationException(String message) { super(message); }
|
||||
}
|
||||
|
||||
// Pola interfejsu
|
||||
private JTextField txtKontrahenci;
|
||||
private JTextField txtRejestr;
|
||||
private JTextArea txtLogi;
|
||||
private JButton btnKonwertuj;
|
||||
|
||||
// Zmienne na ścieżki
|
||||
private File fileKontrahenci;
|
||||
private File fileRejestr;
|
||||
|
||||
// Stałe Optima
|
||||
private static final String WERSJA_STRUKTURY = "2.00";
|
||||
private static final String BAZA_ZRD_ID = "CSV_IMPORT";
|
||||
private static final String BAZA_DOC_ID = "KSIEG";
|
||||
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
// ignorujemy błędy wyglądu
|
||||
}
|
||||
new OptimaConverterApp().setVisible(true);
|
||||
});
|
||||
}
|
||||
|
||||
public OptimaConverterApp() {
|
||||
super("David Ali - s26567@st.pans.nysa.pl - Konwerter CSV do Optimy");
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setSize(800, 650);
|
||||
setLocationRelativeTo(null);
|
||||
setLayout(new BorderLayout(10, 10));
|
||||
|
||||
// Panel górny
|
||||
JPanel pnlInput = new JPanel(new GridBagLayout());
|
||||
pnlInput.setBorder(BorderFactory.createTitledBorder("Krok 1: Wybierz pliki"));
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
// Wiersz 1: Kontrahenci
|
||||
gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0.1;
|
||||
pnlInput.add(new JLabel("Kontrahenci (CSV):"), gbc);
|
||||
|
||||
txtKontrahenci = new JTextField();
|
||||
txtKontrahenci.setEditable(false);
|
||||
gbc.gridx = 1; gbc.weightx = 0.8;
|
||||
pnlInput.add(txtKontrahenci, gbc);
|
||||
|
||||
JButton btnWybierzKontr = new JButton("Wybierz...");
|
||||
gbc.gridx = 2; gbc.weightx = 0.1;
|
||||
pnlInput.add(btnWybierzKontr, gbc);
|
||||
|
||||
// Wiersz 2: Rejestr
|
||||
gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0.1;
|
||||
pnlInput.add(new JLabel("Rejestr VAT (CSV):"), gbc);
|
||||
|
||||
txtRejestr = new JTextField();
|
||||
txtRejestr.setEditable(false);
|
||||
gbc.gridx = 1; gbc.weightx = 0.8;
|
||||
pnlInput.add(txtRejestr, gbc);
|
||||
|
||||
JButton btnWybierzRejestr = new JButton("Wybierz...");
|
||||
gbc.gridx = 2; gbc.weightx = 0.1;
|
||||
pnlInput.add(btnWybierzRejestr, gbc);
|
||||
|
||||
add(pnlInput, BorderLayout.NORTH);
|
||||
|
||||
// Panel środkowy
|
||||
txtLogi = new JTextArea();
|
||||
txtLogi.setEditable(false);
|
||||
txtLogi.setFont(new Font("Monospaced", Font.PLAIN, 12));
|
||||
JScrollPane scrollPane = new JScrollPane(txtLogi);
|
||||
scrollPane.setBorder(BorderFactory.createTitledBorder("Logi przetwarzania"));
|
||||
add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Panel dolny
|
||||
JPanel pnlButton = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
||||
btnKonwertuj = new JButton("KROK 2: GENERUJ XML");
|
||||
btnKonwertuj.setFont(new Font("Arial", Font.BOLD, 14));
|
||||
btnKonwertuj.setBackground(new Color(200, 240, 200));
|
||||
btnKonwertuj.setEnabled(false);
|
||||
pnlButton.add(btnKonwertuj);
|
||||
add(pnlButton, BorderLayout.SOUTH);
|
||||
|
||||
// --- AKCJE ---
|
||||
|
||||
btnWybierzKontr.addActionListener(e -> {
|
||||
File f = chooseFile("Plik Kontrahentów");
|
||||
if (f != null) {
|
||||
fileKontrahenci = f;
|
||||
txtKontrahenci.setText(f.getAbsolutePath());
|
||||
log("Kontrahenci: " + f.getName());
|
||||
checkReady();
|
||||
}
|
||||
});
|
||||
|
||||
btnWybierzRejestr.addActionListener(e -> {
|
||||
File f = chooseFile("Plik Rejestru VAT");
|
||||
if (f != null) {
|
||||
fileRejestr = f;
|
||||
txtRejestr.setText(f.getAbsolutePath());
|
||||
log("Rejestr VAT: " + f.getName());
|
||||
checkReady();
|
||||
}
|
||||
});
|
||||
|
||||
btnKonwertuj.addActionListener(e -> {
|
||||
txtLogi.setText("");
|
||||
runConversion();
|
||||
});
|
||||
|
||||
log("Witaj. Wybierz pliki CSV.");
|
||||
}
|
||||
|
||||
private void checkReady() {
|
||||
if (fileKontrahenci != null || fileRejestr != null) {
|
||||
btnKonwertuj.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private File chooseFile(String title) {
|
||||
JFileChooser fc = new JFileChooser(new File(System.getProperty("user.dir")));
|
||||
fc.setDialogTitle(title);
|
||||
fc.setFileFilter(new FileNameExtensionFilter("Pliki CSV", "csv"));
|
||||
if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
return fc.getSelectedFile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void log(String msg) {
|
||||
txtLogi.append(msg + "\n");
|
||||
txtLogi.setCaretPosition(txtLogi.getDocument().getLength());
|
||||
}
|
||||
|
||||
// --- GŁÓWNA LOGIKA KONWERSJI ---
|
||||
|
||||
private void runConversion() {
|
||||
JFileChooser fc = new JFileChooser(new File(System.getProperty("user.dir")));
|
||||
fc.setDialogTitle("Zapisz plik XML");
|
||||
fc.setSelectedFile(new File("optima_import.xml"));
|
||||
fc.setFileFilter(new FileNameExtensionFilter("Plik XML", "xml"));
|
||||
|
||||
if (fc.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) return;
|
||||
File outputFile = fc.getSelectedFile();
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
SwingUtilities.invokeLater(() -> btnKonwertuj.setEnabled(false));
|
||||
log(">>> Start konwersji...");
|
||||
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
Document doc = docBuilder.newDocument();
|
||||
|
||||
Element rootElement = doc.createElement("ROOT");
|
||||
rootElement.setAttribute("xmlns", "http://www.comarch.pl/cdn/optima/offline");
|
||||
doc.appendChild(rootElement);
|
||||
|
||||
// 1. Kontrahenci
|
||||
if (fileKontrahenci != null) {
|
||||
processKontrahenci(doc, rootElement, fileKontrahenci);
|
||||
}
|
||||
|
||||
// 2. Rejestr VAT
|
||||
if (fileRejestr != null) {
|
||||
processRejestrSprzedazy(doc, rootElement, fileRejestr);
|
||||
}
|
||||
|
||||
writeXml(doc, outputFile);
|
||||
|
||||
log(">>> SUKCES! Plik zapisany: " + outputFile.getName());
|
||||
JOptionPane.showMessageDialog(this, "Gotowe!", "Sukces", JOptionPane.INFORMATION_MESSAGE);
|
||||
|
||||
} catch (Exception e) {
|
||||
log("!!! BŁĄD: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(this, "Wystąpił błąd:\n" + e.getMessage(), "Błąd", JOptionPane.ERROR_MESSAGE);
|
||||
} finally {
|
||||
SwingUtilities.invokeLater(() -> btnKonwertuj.setEnabled(true));
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
// --- CZYTANIE CSV ---
|
||||
private List<Map<String, String>> readCsvFile(File file) throws IOException, CsvValidationException, ValidationException {
|
||||
List<Map<String, String>> records = new ArrayList<>();
|
||||
|
||||
CSVParser parser = new CSVParserBuilder().withSeparator(',').build();
|
||||
|
||||
try (CSVReader reader = new CSVReaderBuilder(new FileReader(file, StandardCharsets.UTF_8))
|
||||
.withCSVParser(parser)
|
||||
.build()) {
|
||||
|
||||
String[] header = reader.readNext();
|
||||
if (header == null) return records;
|
||||
|
||||
// Usuwanie BOM
|
||||
if (header.length > 0 && header[0].startsWith("\uFEFF")) {
|
||||
header[0] = header[0].substring(1);
|
||||
}
|
||||
|
||||
int lineNumber = 1; // Licznik linii (1 to nagłówek)
|
||||
String[] line;
|
||||
while ((line = reader.readNext()) != null) {
|
||||
lineNumber++;
|
||||
|
||||
// WALIDACJA LICZBY KOLUMN
|
||||
if (line.length != header.length) {
|
||||
throw new ValidationException(
|
||||
"Błąd krytyczny w wierszu " + lineNumber + ": " +
|
||||
"Nieprawidłowa liczba kolumn. Oczekiwano: " + header.length +
|
||||
", otrzymano: " + line.length + ". Sprawdź separatory w pliku CSV."
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, String> row = new HashMap<>();
|
||||
for (int i = 0; i < header.length; i++) {
|
||||
row.put(header[i].trim(), line[i].trim());
|
||||
}
|
||||
records.add(row);
|
||||
}
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
// --- PROCESOWANIE KONTRAHENTÓW ---
|
||||
private void processKontrahenci(Document doc, Element rootElement, File csvFile) throws Exception {
|
||||
log("Przetwarzam kontrahentów...");
|
||||
Element kontrahenciNode = doc.createElement("KONTRAHENCI");
|
||||
addRequiredHeaderTags(doc, kontrahenciNode);
|
||||
|
||||
List<Map<String, String>> rows = readCsvFile(csvFile);
|
||||
|
||||
int count = 0;
|
||||
for (Map<String, String> row : rows) {
|
||||
count++;
|
||||
String nazwa = row.get("ADRES_NAZWA1");
|
||||
log("Przetwarzam: " + nazwa);
|
||||
|
||||
Element kontrahent = doc.createElement("KONTRAHENT");
|
||||
|
||||
createCdataElement(doc, kontrahent, "ID_ZRODLA", row.get("ID_ZRODLA"));
|
||||
createCdataElement(doc, kontrahent, "AKRONIM", row.get("AKRONIM"));
|
||||
createCdataElement(doc, kontrahent, "FINALNY", row.get("FINALNY"));
|
||||
createCdataElement(doc, kontrahent, "OPIS", row.get("OPIS"));
|
||||
|
||||
Element adresy = doc.createElement("ADRESY");
|
||||
Element adres = doc.createElement("ADRES");
|
||||
|
||||
createCdataElement(doc, adres, "STATUS", row.get("ADRES_STATUS"));
|
||||
createCdataElement(doc, adres, "NAZWA1", row.get("ADRES_NAZWA1"));
|
||||
createCdataElement(doc, adres, "NAZWA2", row.get("ADRES_NAZWA2"));
|
||||
createCdataElement(doc, adres, "KRAJ", row.get("ADRES_KRAJ"));
|
||||
createCdataElement(doc, adres, "MIASTO", row.get("ADRES_MIASTO"));
|
||||
createCdataElement(doc, adres, "ULICA", row.get("ADRES_ULICA"));
|
||||
createCdataElement(doc, adres, "NR_DOMU", row.get("ADRES_NR_DOMU"));
|
||||
createCdataElement(doc, adres, "NR_LOKALU", row.get("ADRES_NR_LOKALU"));
|
||||
createCdataElement(doc, adres, "KOD_POCZTOWY", row.get("ADRES_KOD_POCZTOWY"));
|
||||
createCdataElement(doc, adres, "POCZTA", row.get("ADRES_POCZTA"));
|
||||
createCdataElement(doc, adres, "NIP", row.get("ADRES_NIP"));
|
||||
createCdataElement(doc, adres, "TELEFON1", row.get("ADRES_TELEFON1"));
|
||||
createCdataElement(doc, adres, "EMAIL", row.get("ADRES_EMAIL"));
|
||||
|
||||
adresy.appendChild(adres);
|
||||
kontrahent.appendChild(adresy);
|
||||
kontrahenciNode.appendChild(kontrahent);
|
||||
}
|
||||
|
||||
if (kontrahenciNode.hasChildNodes()) {
|
||||
rootElement.appendChild(kontrahenciNode);
|
||||
}
|
||||
log("-> Kontrahenci OK: " + rows.size() + " szt.");
|
||||
}
|
||||
|
||||
// --- PROCESOWANIE FAKTUR ---
|
||||
private void processRejestrSprzedazy(Document doc, Element rootElement, File csvFile) throws Exception {
|
||||
Element rejestryNode = doc.createElement("REJESTRY_SPRZEDAZY_VAT");
|
||||
addRequiredHeaderTags(doc, rejestryNode);
|
||||
|
||||
// Tu wywoła się nasz nowy, bezpieczny readCsvFile
|
||||
List<Map<String, String>> rows = readCsvFile(csvFile);
|
||||
|
||||
if (rows.isEmpty()) {
|
||||
log("!!! UWAGA: Pusty plik rejestru lub zły separator.");
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (Map<String, String> row : rows) {
|
||||
count++; // To jest numer wiersza danych (nie linii w pliku)
|
||||
String numerFaktury = row.get("NUMER");
|
||||
|
||||
// --- SEKCJA WALIDACJI (Nowość) ---
|
||||
|
||||
// 1. Sprawdzenie czy kluczowe pola istnieją
|
||||
if (getSafe(row, "NUMER", "").isEmpty()) {
|
||||
throw new ValidationException("Błąd w wierszu " + count + ": Brak numeru faktury (kolumna NUMER).");
|
||||
}
|
||||
|
||||
// 2. Walidacja dat [cite: 12, 55]
|
||||
validateDate(row.get("DATA_WYSTAWIENIA"), "DATA_WYSTAWIENIA", count);
|
||||
validateDate(row.get("DATA_SPRZEDAZY"), "DATA_SPRZEDAZY", count);
|
||||
validateDate(row.get("TERMIN"), "TERMIN", count);
|
||||
|
||||
// 3. Walidacja liczb w pozycjach (obsługa znaku "|") [cite: 88, 89, 90]
|
||||
validateSplitNumeric(row.get("POZYCJE_NETTO"), "POZYCJE_NETTO", count);
|
||||
validateSplitNumeric(row.get("POZYCJE_VAT"), "POZYCJE_VAT", count);
|
||||
//validateSplitNumeric(row.get("POZYCJE_STAWKA"), "POZYCJE_STAWKA", count);
|
||||
validateSplitVatRates(row.get("POZYCJE_STAWKA"), count);
|
||||
|
||||
// 4. Walidacja płatności [cite: 126]
|
||||
validateSplitNumeric(row.get("PLATNOSCI_KWOTA"), "PLATNOSCI_KWOTA", count);
|
||||
|
||||
// --- KONIEC WALIDACJI ---
|
||||
|
||||
log("Przetwarzam: " + numerFaktury);
|
||||
|
||||
Element rejestr = doc.createElement("REJESTR_SPRZEDAZY_VAT");
|
||||
|
||||
// ... (reszta kodu bez zmian: tworzenie XML) ...
|
||||
// Skopiuj tutaj resztę oryginalnego kodu z tej metody,
|
||||
// zaczynając od: createCdataElement(doc, rejestr, "ID_ZRODLA", ...
|
||||
|
||||
createCdataElement(doc, rejestr, "ID_ZRODLA", UUID.randomUUID().toString());
|
||||
createCdataElement(doc, rejestr, "MODUL", "Rejestr Vat");
|
||||
createCdataElement(doc, rejestr, "TYP", "Rejestr sprzedazy");
|
||||
createCdataElement(doc, rejestr, "REJESTR", getSafe(row, "REJESTR", "SPRZEDAŻ"));
|
||||
|
||||
createCdataElement(doc, rejestr, "DATA_WYSTAWIENIA", row.get("DATA_WYSTAWIENIA"));
|
||||
createCdataElement(doc, rejestr, "DATA_SPRZEDAZY", row.get("DATA_SPRZEDAZY"));
|
||||
createCdataElement(doc, rejestr, "TERMIN", row.get("TERMIN"));
|
||||
createCdataElement(doc, rejestr, "DATA_DATAOBOWIAZKUPODATKOWEGO", row.get("DATA_SPRZEDAZY"));
|
||||
createCdataElement(doc, rejestr, "NUMER", numerFaktury);
|
||||
|
||||
createCdataElement(doc, rejestr, "KOREKTA", "Nie");
|
||||
createCdataElement(doc, rejestr, "FINALNY", "Tak");
|
||||
createCdataElement(doc, rejestr, "PODATNIK_CZYNNY", "Tak");
|
||||
createCdataElement(doc, rejestr, "TYP_PODMIOTU", "kontrahent");
|
||||
|
||||
createCdataElement(doc, rejestr, "PODMIOT", row.get("KONTRAHENT_KOD"));
|
||||
createCdataElement(doc, rejestr, "PODMIOT_NIP", row.get("KONTRAHENT_NIP"));
|
||||
createCdataElement(doc, rejestr, "NAZWA1", row.get("KONTRAHENT_NAZWA"));
|
||||
createCdataElement(doc, rejestr, "NIP", row.get("KONTRAHENT_NIP"));
|
||||
createCdataElement(doc, rejestr, "MIASTO", row.get("ADRES_MIASTO"));
|
||||
createCdataElement(doc, rejestr, "ULICA", row.get("ADRES_ULICA"));
|
||||
|
||||
createCdataElement(doc, rejestr, "TYP_PLATNIKA", "kontrahent");
|
||||
createCdataElement(doc, rejestr, "PLATNIK", row.get("KONTRAHENT_KOD"));
|
||||
|
||||
createCdataElement(doc, rejestr, "WALUTA", "PLN");
|
||||
createCdataElement(doc, rejestr, "KURS_WALUTY", "NBP");
|
||||
createCdataElement(doc, rejestr, "NOTOWANIE_WALUTY_ILE", "1");
|
||||
createCdataElement(doc, rejestr, "NOTOWANIE_WALUTY_ZA_ILE", "1");
|
||||
createCdataElement(doc, rejestr, "DATA_KURSU", row.get("DATA_WYSTAWIENIA"));
|
||||
|
||||
// --- POZYCJE (SPLIT) ---
|
||||
Element pozycjeNode = doc.createElement("POZYCJE");
|
||||
|
||||
String sNettoAll = getSafe(row, "POZYCJE_NETTO", "0");
|
||||
String sVatAll = getSafe(row, "POZYCJE_VAT", "0");
|
||||
String sStawkaAll = getSafe(row, "POZYCJE_STAWKA", "23");
|
||||
String sRodzajAll = getSafe(row, "POZYCJE_RODZAJ", "towary");
|
||||
String sKolumnaKpr = getSafe(row, "KOLUMNA_KPR", "Sprzedaż");
|
||||
|
||||
List<String> listNetto = splitValues(sNettoAll);
|
||||
List<String> listVat = splitValues(sVatAll);
|
||||
List<String> listStawka = splitValues(sStawkaAll);
|
||||
List<String> listRodzaj = splitValues(sRodzajAll);
|
||||
|
||||
int countPos = listNetto.size();
|
||||
|
||||
for (int i = 0; i < countPos; i++) {
|
||||
Element poz = doc.createElement("POZYCJA");
|
||||
|
||||
String valNetto = getListVal(listNetto, i);
|
||||
String valVat = getListVal(listVat, i);
|
||||
String valStawkaRaw = getListVal(listStawka, i); // Oryginalna wartość z CSV
|
||||
String valRodzaj = getListVal(listRodzaj, i);
|
||||
|
||||
createCdataElement(doc, poz, "LP", String.valueOf(i + 1));
|
||||
|
||||
// --- LOGIKA OBSŁUGI ZW / NP ---
|
||||
String stawkaDoXml = valStawkaRaw;
|
||||
String statusVat = "opodatkowana";
|
||||
|
||||
if ("zw".equalsIgnoreCase(valStawkaRaw)) {
|
||||
// Dla "zw": Stawka w XML to 0.00, Status to "zwolniona"
|
||||
stawkaDoXml = "0.00";
|
||||
statusVat = "zwolniona";
|
||||
} else if ("np".equalsIgnoreCase(valStawkaRaw)) {
|
||||
// Dla "np": Stawka w XML to 0.00, Status to "nie podlega"
|
||||
stawkaDoXml = "0.00";
|
||||
statusVat = "nie podlega";
|
||||
}
|
||||
|
||||
// Zapisujemy przetworzone wartości
|
||||
createCdataElement(doc, poz, "STAWKA_VAT", stawkaDoXml); //
|
||||
createCdataElement(doc, poz, "STATUS_VAT", statusVat); //
|
||||
// --------------------------------
|
||||
|
||||
createCdataElement(doc, poz, "NETTO", valNetto);
|
||||
createCdataElement(doc, poz, "VAT", valVat);
|
||||
createCdataElement(doc, poz, "NETTO_SYS", valNetto);
|
||||
createCdataElement(doc, poz, "VAT_SYS", valVat);
|
||||
createCdataElement(doc, poz, "NETTO_SYS2", valNetto);
|
||||
createCdataElement(doc, poz, "VAT_SYS2", valVat);
|
||||
createCdataElement(doc, poz, "RODZAJ_SPRZEDAZY", valRodzaj);
|
||||
createCdataElement(doc, poz, "UWZ_W_PROPORCJI", "tak");
|
||||
createCdataElement(doc, poz, "KOLUMNA_KPR", sKolumnaKpr);
|
||||
|
||||
pozycjeNode.appendChild(poz);
|
||||
}
|
||||
rejestr.appendChild(pozycjeNode);
|
||||
|
||||
// --- PŁATNOŚCI (SPLIT) ---
|
||||
Element platnosciNode = doc.createElement("PLATNOSCI");
|
||||
|
||||
String sPlatForma = getSafe(row, "PLATNOSCI_FORMA", "gotówka");
|
||||
String sPlatKwota = getSafe(row, "PLATNOSCI_KWOTA", "0");
|
||||
String sPlatTermin = getSafe(row, "PLATNOSCI_TERMIN", row.get("TERMIN"));
|
||||
|
||||
List<String> listPlatForma = splitValues(sPlatForma);
|
||||
List<String> listPlatKwota = splitValues(sPlatKwota);
|
||||
List<String> listPlatTermin = splitValues(sPlatTermin);
|
||||
|
||||
int countPlat = listPlatKwota.size();
|
||||
|
||||
for (int j = 0; j < countPlat; j++) {
|
||||
Element plat = doc.createElement("PLATNOSC");
|
||||
|
||||
String valForma = getListVal(listPlatForma, j);
|
||||
String valKwota = getListVal(listPlatKwota, j);
|
||||
String valTermin = getListVal(listPlatTermin, j);
|
||||
|
||||
createCdataElement(doc, plat, "TERMIN_PLAT", valTermin);
|
||||
createCdataElement(doc, plat, "FORMA_PLATNOSCI_PLAT", valForma);
|
||||
createCdataElement(doc, plat, "KWOTA_PLAT", valKwota);
|
||||
createCdataElement(doc, plat, "WALUTA_PLAT", "PLN");
|
||||
createCdataElement(doc, plat, "WALUTA_DOK", "PLN");
|
||||
createCdataElement(doc, plat, "KURS_WALUTY_PLAT", "NBP");
|
||||
createCdataElement(doc, plat, "NOTOWANIE_WALUTY_ILE_PLAT", "1");
|
||||
createCdataElement(doc, plat, "NOTOWANIE_WALUTY_ZA_ILE_PLAT", "1");
|
||||
createCdataElement(doc, plat, "KWOTA_PLN_PLAT", valKwota);
|
||||
createCdataElement(doc, plat, "KIERUNEK", "przychód");
|
||||
createCdataElement(doc, plat, "PODLEGA_ROZLICZENIU", "tak");
|
||||
|
||||
createCdataElement(doc, plat, "PLATNOSC_TYP_PODMIOTU", "kontrahent");
|
||||
createCdataElement(doc, plat, "PLATNOSC_PODMIOT", row.get("KONTRAHENT_KOD"));
|
||||
|
||||
platnosciNode.appendChild(plat);
|
||||
}
|
||||
rejestr.appendChild(platnosciNode);
|
||||
rejestryNode.appendChild(rejestr);
|
||||
}
|
||||
|
||||
rootElement.appendChild(rejestryNode);
|
||||
log("-> Rejestr VAT OK: " + count + " dok.");
|
||||
}
|
||||
|
||||
// --- METODY POMOCNICZE ---
|
||||
|
||||
private List<String> splitValues(String raw) {
|
||||
if (raw == null || raw.isEmpty()) return new ArrayList<>();
|
||||
String[] parts = raw.split("\\|");
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String p : parts) {
|
||||
result.add(p.trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateDate(String dateStr, String fieldName, int rowNum) throws ValidationException {
|
||||
if (dateStr == null || dateStr.trim().isEmpty()) return; // Puste pola pomijamy (chyba że są wymagane, to sprawdzamy osobno)
|
||||
|
||||
// Wymagany format RRRR-MM-DD
|
||||
if (!dateStr.matches("\\d{4}-\\d{2}-\\d{2}")) {
|
||||
throw new ValidationException(
|
||||
"Błąd w wierszu " + rowNum + ", kolumna '" + fieldName + "': " +
|
||||
"Nieprawidłowy format daty ('" + dateStr + "'). Wymagane: RRRR-MM-DD."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateNumeric(String numStr, String fieldName, int rowNum) throws ValidationException {
|
||||
if (numStr == null || numStr.trim().isEmpty()) return;
|
||||
|
||||
try {
|
||||
// Optima oczekuje kropki jako separatora dziesiętnego w XML, ale w CSV użytkownik może mieć przecinek
|
||||
String safeNum = numStr.replace(",", ".");
|
||||
Double.parseDouble(safeNum);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ValidationException(
|
||||
"Błąd w wierszu " + rowNum + ", kolumna '" + fieldName + "': " +
|
||||
"Wartość '" + numStr + "' nie jest liczbą."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Do walidacji pól, które są rozdzielane znakiem pipe "|" (np. netto|netto|netto)
|
||||
private void validateSplitNumeric(String rawValue, String fieldName, int rowNum) throws ValidationException {
|
||||
List<String> parts = splitValues(rawValue);
|
||||
for (String part : parts) {
|
||||
validateNumeric(part, fieldName, rowNum);
|
||||
}
|
||||
}
|
||||
|
||||
// Specjalna walidacja dla stawek VAT (akceptuje liczby oraz "zw" i "np")
|
||||
private void validateSplitVatRates(String rawValue, int rowNum) throws ValidationException {
|
||||
List<String> parts = splitValues(rawValue);
|
||||
for (String part : parts) {
|
||||
String val = part.toLowerCase();
|
||||
// Jeśli to "zw" lub "np" - jest OK, idziemy dalej
|
||||
if (val.equals("zw") || val.equals("np")) {
|
||||
continue;
|
||||
}
|
||||
// W przeciwnym razie musi to być liczba
|
||||
try {
|
||||
Double.parseDouble(val.replace(",", "."));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ValidationException(
|
||||
"Błąd w wierszu " + rowNum + ", kolumna 'POZYCJE_STAWKA': " +
|
||||
"Wartość '" + part + "' jest nieprawidłowa. Dozwolone: liczby (np. 23), 'zw' lub 'np'."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getListVal(List<String> list, int index) {
|
||||
if (list == null || list.isEmpty()) return "";
|
||||
if (index >= list.size()) return list.get(list.size() - 1);
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
private String getSafe(Map<String, String> row, String key, String defaultVal) {
|
||||
String val = row.get(key);
|
||||
if (val == null || val.trim().isEmpty()) return defaultVal;
|
||||
return val.trim();
|
||||
}
|
||||
|
||||
private void addRequiredHeaderTags(Document doc, Element parentNode) {
|
||||
Element wersja = doc.createElement("WERSJA");
|
||||
wersja.setTextContent(WERSJA_STRUKTURY);
|
||||
parentNode.appendChild(wersja);
|
||||
|
||||
Element bazaZrd = doc.createElement("BAZA_ZRD_ID");
|
||||
bazaZrd.setTextContent(BAZA_ZRD_ID);
|
||||
parentNode.appendChild(bazaZrd);
|
||||
|
||||
Element bazaDoc = doc.createElement("BAZA_DOC_ID");
|
||||
bazaDoc.setTextContent(BAZA_DOC_ID);
|
||||
parentNode.appendChild(bazaDoc);
|
||||
}
|
||||
|
||||
private void createCdataElement(Document doc, Element parent, String tagName, String text) {
|
||||
Element element = doc.createElement(tagName);
|
||||
if (text != null && !text.isEmpty()) {
|
||||
CDATASection cdata = doc.createCDATASection(text);
|
||||
element.appendChild(cdata);
|
||||
}
|
||||
parent.appendChild(element);
|
||||
}
|
||||
|
||||
private void writeXml(Document doc, File file) throws Exception {
|
||||
TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
Transformer transformer = transformerFactory.newTransformer();
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
|
||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
|
||||
DOMSource source = new DOMSource(doc);
|
||||
StreamResult result = new StreamResult(file);
|
||||
transformer.transform(source, result);
|
||||
}
|
||||
}
|
||||
9
src/main/java/ValidationException.java
Normal file
9
src/main/java/ValidationException.java
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Nasz własny, prosty typ wyjątku (błędu), który będzie używany
|
||||
* do przekazywania "przyjaznych dla użytkownika" komunikatów walidacji.
|
||||
*/
|
||||
public class ValidationException extends Exception {
|
||||
public ValidationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user