Metadata-Version: 2.5
Name: qtx-nav-m2m
Version: 0.1.3
Summary: Python client library for the NAV M2M REST API
Author: Pongracz Istvan
License: MIT
Requires-Python: >=3.11
Requires-Dist: httpx<1.0,>=0.28
Requires-Dist: pydantic<3.0,>=2.11
Provides-Extra: dev
Requires-Dist: pytest-asyncio<2.0,>=1.1; extra == 'dev'
Requires-Dist: pytest<9.0,>=8.4; extra == 'dev'
Requires-Dist: ruff<1.0,>=0.12; extra == 'dev'
Description-Content-Type: text/markdown

# qtx-nav-m2m


## Aktuális verzió

- `0.1.3`: a `bizonylat` service `*_from_path()` metódusai már kiolvassák a
  `bizonylat_tipus` és `bizonylat_verzio` értékét az XML namespace-ből, ha a
  hívó ezeket nem adja meg. Emellett bekerült a központi, maszkolt HTTP-logolás
  a `qtx_nav_m2m.http` loggeren, így a kérések és válaszok metaadatai
  biztonságosan naplózhatók.
- `0.1.2`: típusellenőrzési javítások kerültek be, különösen a `bizonylat` modellek
  Pylance-kompatibilis típusaiban.
- `0.1.1`: ebben a verzióban csak optimalizálás történt, funkcionális bővítés nem.

Python klienskönyvtár a NAV M2M REST API szolgáltatásaihoz.

## Cél

Az API-végpontok technikai részleteinek elfedése Python függvényekkel, valamint
a JSON válaszok típusos Python modellekké alakítása.

## Támogatott területek

- `common`: tokenkezelés, nonce beváltás, regisztráció aktiválása, NAV közös fájltár
- `bizonylat`: új formátumú bizonylatok kalkulációja, validációja és beküldése
- `adozo`: adózói adatok lekérdezése
- `document`: régi ÁNYK-formátumú bizonylatok kezelése, későbbre hagyva

## Megvalósítási állapot

A `common` végpontok elkészültek és unit tesztekkel ellenőrzöttek:

- token létrehozása
- nonce beváltása és regisztráció aktiválása
- fájl feltöltése a NAV közös fájltárába, majd a vírusellenőrzési állapot lekérdezése

A kliens futás közben is használható új tokennel és a regisztráció után összeálló
végleges aláírókulccsal. A `set_config()` új HTTP-klienst hoz létre, és eldobja a
régi tokeneket és a hozzájuk tartozó runtime állapotot.

## Egylépéses regisztráció

A teljes NAV-regisztrációt a kliens `register()` metódusa vezényli le. A `nonce`
továbbra is bemenő adat, mert ezt a NAV ideiglenes jelszóként kéri a nonce
végponton; a második kulcsrész már a NAV válaszából érkezik.

```python
result = client.register(
    key_first_part="...",
    nonce="...",
    activation_message_id="...",
)

signature_key = result.signature_key
```

Az aktiválás után a kliens automatikusan új access tokent kér, így a
`register()` visszatérésekor a kliens már közvetlenül használható.

## Adózó service

Az adózói adatlekérdezések a `client.adozo` szolgáltatáson keresztül érhetők el.
A kliens automatikusan generálja a `messageId` értéket, valamint a NAV
specifikáció szerinti signature-t.

Támogatott végpontok:

- összesített és tételes adószámla
- köztartozás-egyenleg
- hiányzó bevallások
- biztosítotti jogviszony és a lekérdezés státusza
- egyszerűsített foglalkoztatás egy foglalkoztatottra vagy foglalkoztatói listára
- köztartozásmentesség (KOMA)

Példa köztartozás-egyenleg lekérdezésére:

```python
from qtx_nav_m2m import M2mClient

with M2mClient(config) as client:
    response = client.adozo.get_koztartozas_egyenleg(
        adoalany_azonosito="12345678",
    )

    if response.koztartozas_egyenleg is not None:
        print(response.koztartozas_egyenleg.osszes_eloiras)
```

## Bizonylat service

Az új formátumú bizonylatok végpontjai a `client.bizonylat` szolgáltatáson
keresztül érhetők el:

- `create_kalkulacio` és `get_kalkulacio`
- `create_validacio` és `get_validacio`
- `create_bizonylat` és `get_bizonylat`

A létrehozó műveletek `bytes` típusú XML-t fogadnak. A kliens elvégzi az
opcionális GZIP tömörítést, a SHA-256 hash képzését, a NAV signature
előállítását és a Base64 kódolást. A státuszlekérdezésekhez az indító művelet
által visszaadott `ugy_azonosito` szükséges.

```python
with M2mClient(config) as client:
    response = client.bizonylat.create_validacio(
        bizonylat_tipus="T1042E",
        bizonylat_verzio="1.0",
        bizonylat_xml=b"<Bizonylat />",
    )

    if response.ugy_azonosito is not None:
        status = client.bizonylat.get_validacio(response.ugy_azonosito)
```

A `*_from_path()` kényelmi metódusok beolvassák a fájlt, majd a gyökérelem
namespace-éből kiolvassák a `bizonylat_tipus` és `bizonylat_verzio` értékét.
Ha ezek explicit meg vannak adva, akkor a kliens ellenőrzi, hogy egyeznek-e az
XML-lel, és eltérés esetén `ValueError` kivételt dob.

```python
with M2mClient(config) as client:
    response = client.bizonylat.create_validacio_from_path(
        Path("T1042E.xml"),
    )
```

## HTTP logolás

A kliens a központi HTTP-rétegben a `qtx_nav_m2m.http` loggerre ír szűrt,
maszkolt logeseményeket. A következő események jelennek meg:

- `nav_m2m_request_started`
- `nav_m2m_request_finished`
- `nav_m2m_request_failed`

Az érzékeny mezők, mint az `Authorization`, `signature`, `accessToken`,
`clientSecret` és `password` értéke `XXX` maszkkal kerül a logba. A nagy
payloadok, például a `bizonylatXml`, teljes tartalom helyett csak összefoglaló
metaadatként szerepelnek.

Példa a logolás bekapcsolására:

```python
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(name)s %(levelname)s %(message)s",
)
```

## Dokumentált specifikációs eltérés: bizonylat

A bizonylat specifikációk között két eltérés található:

- az OpenAPI leírás 30 másodperces, a DOCX specifikáció 60 másodperces
  szinkron válaszidőt említ;
- a signature műveletfüggő adata a bizonylat XML SHA-256 hash-e. A hash
  reprezentációját az OpenAPI nem részletezi, ezért a NAV általános
  interfészpéldáját követve a hexadecimális SHA-256 értéket használjuk.

Ezeket az eltéréseket nem fedjük el találgatással; az implementáció és a
tesztek ezt a döntést követik.

## HTTP-kliens és API-verziók

A `M2mClient` egy közös `M2mHttpClient` példányt használ, amelyet az összes
service megoszt. A token és a közös HTTP-beállítások így minden service számára
elérhetők.

A `common.filestore` nem ugyanaz, mint a `bizonylat` service:

- a `common.filestore` a NAV közös fájltárába tölt fel egy nyers fájlt, majd a
  fájlazonosító alapján a feldolgozási állapotot lehet lekérdezni;
- a `bizonylat` service XML-alapú NAV dokumentumküldést kezel, saját signature-
  és payload-szabályokkal.

Az API-verzió nem a klienshez kötött, hanem az egyes kéréseknél adható meg. Ez
lehetővé teszi, hogy a common végpontok a `/rest-api/1.1`, míg például az adozo
végpontok a saját specifikációjuk szerinti `/rest-api/1.0` útvonalon működjenek
ugyanazzal a HTTP-klienssel és tokennel.

## Projekt-előkészítés

```bash
python -m venv .venv
```

Windows PowerShell:

```powershell
.venv\Scripts\Activate.ps1
python -m pip install -U pip
python -m pip install -e ".[dev]"
```

## Dokumentációk elhelyezése

- A NAV DOCX specifikációk helye: `docs/specifications/`
- A Swagger/OpenAPI YAML fájlok helye: `openapi/`

Ezek a forrásanyagok nincsenek a kezdőcsomagba bemásolva.

## Forráskód és aktuális mappastruktúra

```text
qtx_nav_m2m/
├── main.py
├── openapi/
├── docs/
├── README.md
├── src/
│   └── qtx_nav_m2m/
│       ├── __init__.py
│       ├── client.py
│       ├── config.py
│       ├── core/
│       │   ├── authentication.py
│       │   ├── exceptions.py
│       │   ├── http_client.py
│       │   ├── message.py
│       │   └── signature.py
│       └── services/
│           ├── adozo/
│           │   ├── _base.py
│           │   ├── employment.py
│           │   ├── legal_relationship.py
│           │   ├── models/
│           │   ├── public_debt.py
│           │   ├── returns.py
│           │   ├── service.py
│           │   └── tax_account.py
│           ├── bizonylat/
│           │   ├── models/
│           │   └── service.py
│           └── common/
│               ├── __init__.py
│               ├── filestore.py
│               ├── models/
│               ├── registration.py
│               └── token.py
└── tests/
    └── test_package.py
```

## Smoke test

A gyökérben található `main.py` placeholder hitelesítési adatokkal meghívja a
token végpontot. Ezzel ellenőrizhető, hogy a NAV szerver elérhető-e:

```powershell
.venv\Scripts\python.exe main.py
```


## Végpontkapcsolatok és aszinkron párok

Nem minden NAV végpont önálló, egyszeri hívás. Több műveletnél az első kérés
csak elindítja a feldolgozást, és a végső eredményt egy külön státuszlekérdező
végponton lehet vagy kell lekérdezni.

Az alábbi összefoglaló azt mutatja meg, hogy egy adott hívás után

- kell-e további lekérdezés;
- milyen azonosítót kell megőrizni a folytatáshoz;
- melyik végponttal kérdezhető le a későbbi eredmény.

### Common

- `create_token`: önálló művelet, nincs hozzá státuszvégpont.
- `redeem_nonce`: önálló művelet, nincs hozzá státuszvégpont.
- `activate_user_registration`: önálló művelet, nincs hozzá státuszvégpont.
- `add_file` -> `get_file_status`: aszinkron pár. A feltöltés után a visszakapott
  `file_id` alapján kérdezhető le a vírusellenőrzési és feldolgozási állapot.

### Bizonylat

- `create_kalkulacio` -> `get_kalkulacio`: aszinkron pár. A létrehozó művelet
  `ugy_azonosito` értéket ad vissza; ezt kell továbbvinni a státuszlekérdezéshez.
- `create_validacio` -> `get_validacio`: aszinkron pár. A folytatáshoz az
  indító hívás `ugy_azonosito` értéke szükséges.
- `create_bizonylat` -> `get_bizonylat`: aszinkron pár. A beküldés utáni állapot
  és a végső eredmény az `ugy_azonosito` alapján kérdezhető le.

Ezeknél a műveleteknél a NAV szinkron választ is adhat, de ha a feldolgozás még
nem készült el, akkor a válasz `FOLYAMATBAN` státuszt tartalmazhat. Ilyenkor az
`ugy_azonosito` megőrzése kötelező.

### Adózó

- `get_osszesitett_adoszamla`: önálló művelet, nincs hozzá státuszvégpont.
- `get_teteles_adoszamla`: önálló művelet, nincs hozzá státuszvégpont.
- `get_koztartozas_egyenleg`: önálló művelet, nincs hozzá státuszvégpont.
- `get_hianyzo_bevallas`: önálló művelet, nincs hozzá státuszvégpont.
- `get_egyszerusitett_foglalkoztatas`: önálló művelet, nincs hozzá státuszvégpont.
- `get_egyszerusitett_foglalkoztatas_foglalkoztatott_lista`: önálló művelet,
  nincs hozzá státuszvégpont.
- `get_koztartozas_mentesseg`: önálló művelet, nincs hozzá státuszvégpont.
- `get_biztositotti_jogviszony_foglalkoztato_adat` ->
  `get_biztositotti_jogviszony_foglalkoztato_adat_statusz`: aszinkron pár. Ha az
  első válaszban még nincs teljes adattartalom, akkor a `request_id` alapján kell
  továbblépni, és a státuszvégpont `page_number` paramétert is kér.

Ennél a végpontnál a `request_id` és a lapozási adatok a válasz `request_data`
mezőjében jelenhetnek meg, ezért ezt a blokkot a kliensoldali feldolgozásnál
külön figyelni kell.

### Document

A `document` service nem klasszikus egy-az-egyhez aszinkron pár, hanem inkább
állapotgépként működik:

- `create_document` létrehozza és elővalidálja a dokumentumot;
- `update_document` státuszváltást kezdeményez, például `UNDER_SUBMIT`
  állapotba;
- `get_document` a dokumentum aktuális állapotát kérdezi le a
  `document_file_id` alapján.

Ezért itt a `create_document` -> `get_document` és az
`update_document` -> `get_document` kapcsolatokkal kell számolni, nem külön
dedikált státuszvégponttal.
