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