first commit

This commit is contained in:
David Ali
2026-01-08 18:17:47 +01:00
commit 8160bc3c23
10 changed files with 2002 additions and 0 deletions

35
.gitignore vendored Normal file
View 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
View 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.

Binary file not shown.

View 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
View 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,,
1 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
2 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
3 KH-002 MEBLOMIX Nie aktualny MEBLOMIX Jan Kowalski Polska Kraków Wielicka 40 12 30-552 Kraków 7532350589 129876543 jan.kowalski@meblomix.pl
4 KH-003 NOWAK Tak aktualny Adam Nowak Polska Nysa Rynek 5 48-300 Nysa 7531493878 774123123 adam.nowak@prywatny.pl
5 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
6 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
7 KH-006 BUDMAX Nie aktualny BUDMAX Hurtownia Budowlana Piotr Nowakowski Polska Poznań Przemysłowa 50 61-500 Poznań 7532200602 612223344 zamowienia@budmax-poznan.pl
8 KH-007 TECH-MAX Nie aktualny Technologie Max Sp.k. Polska Warszawa Prosta 10 00-850 Warszawa 7532443405
9 KH-008 MED-CENTRUM Nie aktualny Centrum Medyczne Zdrowie Polska Łódź Piotrkowska 5 90-004 Łódź 7532022419
10 KH-009 KOWALSKI Tak aktualny Jan Kowalski Detal Polska Gdynia Morska 1 81-001 Gdynia 7471778441
11 KH-010 BUD-REM Nie aktualny Usługi Remontowe Polska Katowice Węglowa 3 40-100 Katowice 7541436623
12 KH-011 EXP-IM Nie aktualny Export Import Ltd Polska Kraków Floriańska 12 31-021 Kraków 7531927549
13 KH-012 SZKOLA Nie aktualny Szkoła Językowa Polska Poznań Szkolna 3 61-832 Poznań 7532439272
14 KH-013 LOGISTYK Nie aktualny Logistyka i Transport Polska Wrocław Autostradowa 99 54-424 Wrocław 7532446177

View 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ż
1 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
2 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ż
3 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ż
4 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ż
5 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ż
6 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ż
7 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ż
8 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ż
9 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ż
10 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ż
11 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ż
12 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ż
13 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ż
14 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ż
15 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ż
16 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ż
17 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ż
18 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

File diff suppressed because it is too large Load Diff

57
pom.xml Normal file
View 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>

View 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);
}
}

View 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);
}
}