Projekt

Általános

Profil

NavDaV » Történet » Verzió 1

Zoltán Lehotai, 2026-05-05 09:10

1 1 Zoltán Lehotai
2
# NavDaV
3
4
NAV Online Számla → SQLite akkumulátor → on-demand xlsx render → WebDAV → Nextcloud External Storage
5
6
**Tartalom**
7
8
1.  [Mire való ez a rendszer?](#mire-valo)
9
2.  [Architektúra](#architektura)
10
3.  [Adatfolyam — egy fájlmegnyitás](#adatfolyam)
11
4.  [SQLite séma](#schema)
12
5.  [NAV bridge — delta + state](#nav-bridge)
13
6.  [Throttle és render-cache](#throttle)
14
7.  [xlsx renderer](#xlsx)
15
8.  [WebDAV provider](#webdav)
16
9.  [Production deploy és üzemeltetés](#bootstrap)
17
10. [Nextcloud bekötés](#nextcloud)
18
11. [Fájlok és tech stack](#fajlok)
19
12. [Iteratív állás és lezárt issue-k](#nyitott)
20
21
## Mire való ez a rendszer?
22
23
> **Production status (2026-05-04):** Docker-image multi-stage Rust + Python build, healthcheck, persistent volume; egysoros telepítő Gitea-ról (`install-from-gitea.sh`), idempotens frissítéssel; LAN-os Nextcloud External Storage bekötés, kanonikus üzemeltetési parancsokkal. Code-stasi review 36 találatából 35 lezárva (1 halasztott: Docker BuildKit secret mount). NAV CLI v1.5.1 alapszinten — 9 lezárt nav-projekt-issue, 33 strukturált top-level mező + 25 line-mező + ÁFA-rátarendszer-bontás (`vat_rate_summaries`). Magyar számviteli terminológia + ISO 80000-1 szerinti formázás.
24
25
A NAV Online Számla rendszerből minden bejövő és kimenő elektronikus számla automatikusan elérhető API-n keresztül. Az adat hasznos, de a NAV felülete nem alkalmas napi munkára: nincs gyors szűrés, exportálás, sem áttekintő nézet.
26
27
A NavDaV egyetlen virtuális Excel-fájlt szolgál ki — `szamlak.xlsx` — ami **minden megnyitáskor friss NAV-adatot** tartalmaz, és a Nextcloudba van bekötve External Storage-ként. A user csak megnyitja a fájlt böngészőben (Collabora) vagy desktop kliensből, és aktuális állapotot lát.
28
29
> **1. Lekér**
30
>
31
> Megnyitáskor a háttérben meghívja a NAV API-t (a `nav-online-invoice` Rust binárison keresztül) — csak a delta-ablakot, nem a teljes történetet.
32
33
> **2. Akkumulál**
34
>
35
> Az új eseményeket egy SQLite-ba upsertálja. A teljes történet a DB-ben, nincs az adat „elveszhet egy lekérdezés között" helyzet.
36
37
> **3. Renderel**
38
>
39
> Az SQLite-ból in-memory xlsx-et készít két lappal (Számlák, Tételek), autofilterrel, és visszaadja a HTTP válaszban.
40
41
**5802** számla \| **13549** tételsor \| **28** hónap \| **60s** throttle \| **2.2MB** xlsx \| **6** xlsx-lap
42
43
## Architektúra
44
45
Egyetlen Python processz fut localhoston — egy **WSGI WebDAV szerver** (`wsgidav` + `cheroot`). A Nextcloud ezt látja távoli WebDAV-erőforrásként és úgy kezeli, mintha egy könyvtár lenne benne egyetlen fájllal.
46
47
> **A négy réteg**
48
>
49
> 1.  **Rust binary** (`nav-online-invoice`) — a NAV Online Számla v3.0 API kliens. 35 napos chunk-szeleteléssel, retry-jal, JSON kimenettel. *A NavDaV csak subprocess-szel hívja, nem érti az XML-t.*
50
> 2.  **NAV bridge** (`nav_bridge.py`) — a Rust binary köré fonott Python réteg: subprocess hívás, JSON parse, SQLite UPSERT, delta-ablak, state-management, throttle.
51
> 3.  **xlsx renderer** (`render_xlsx.py`) — SQLite → in-memory bytes (`xlsxwriter`). Két lap, autofilter, freeze pane, dátum/szám-formázás.
52
> 4.  **WebDAV provider** (`webdav_server.py`) — egyetlen virtuális fájlt expose-ál, Basic auth, render-cache, ETag/Last-Modified.
53
54
### A két állapottároló
55
56
> **SQLite — perzisztens akkumulátor**
57
>
58
> `navdav.db`. Ide kerül minden NAV-tól kapott számla, idő múlásával csak nő (törlés nincs). Egy számla törlésével „elveszett" eseményt nem ismerünk — ez audit-szintű séma, nem aktív-állapot mirror.
59
60
> **Process-memória — render-cache**
61
>
62
> A legutóbb renderelt xlsx-bytes + a render alapjául szolgáló `last_success_ts`. Amíg az SQLite nem változott, ugyanaz a bytes szolgálja ki az összes GET-et. Restart után újrarenderel.
63
64
## Adatfolyam — egy fájlmegnyitás
65
66
A felhasználó megnyitja `szamlak.xlsx`-et a Nextcloudban → a Nextcloud egy HTTP `GET`-et küld a WebDAV szerverünknek. Innentől:
67
68
1.  **Auth** — HTTP Basic, `NAVDAV_USER` / `NAVDAV_PASSWORD` env-ből  
69
    *Hibás → 401, válasz nincs*
70
2.  **Throttle gate** — `maybe_refresh_delta()` egy module-szintű `threading.Lock`-ot vesz fel  
71
    *Ha last_check_ts \< 60s → skip (nincs NAV-hívás)*
72
3.  **Delta — \`valtozas --since last_valtozas_ts\`**  
73
    *insDate (NAV-érkezés) szerinti szűrés — késedelmes-beküldés-immunis. A \`valtozas\` parancs (NAV CLI v1.2.0) \`tetelek\`-szerű 30+ mezős JSON-t ad. Bootstrap (full sync) marad \`tetelek\`-alapú*
74
4.  **Két subprocess-hívás** — kimenő + bejövő irány külön  
75
    *nav-online-invoice -p -q valtozas --since \<ts\> -d kimeno -o json --file ... majd ugyanaz -d bejovo -val. Exit-100 (nincs új) is sikernek számít, üres listát ad. Bootstrap módban (\`--full\` vagy \`--from/--to\`) helyette tetelek -d kimeno -f X -t Y*
76
5.  **JSON parse + UPSERT** — `transaction_id` mint PK, `ON CONFLICT DO UPDATE`  
77
    *Számla előtti tételsorok törlése, friss insert (idempotens)*
78
6.  **State frissítés** — siker → `last_success_date`, `last_success_ts`; bármi → `last_check_ts`  
79
    *NAV-hiba esetén csak a last_check_ts frissül, hogy a throttle ne hammerezze a hibás API-t*
80
7.  **Render-cache döntés** — ha a state-beli `last_success_ts` egyezik a cache-belivel → cache HIT  
81
    *Egyébként render_xlsx.render(conn, BytesIO) , MD5 számolás, cache frissítés*
82
8.  **HTTP válasz** — Content-Length, ETag, Last-Modified, Content-Type, body  
83
    *A Nextcloud kliens ETag-alapján dönti el, letöltse-e újra (sok user, kevés sávszél)*
84
85
## SQLite séma
86
87
Három tábla: `invoices` (egy sor = egy NAV-esemény), `invoice_lines` (számlatételek, FK-on lóg), `state` (kulcs-érték a delta-ablak és throttle számára).
88
89
### invoices — primary key
90
91
A PK **`transaction_id`** — a NAV `<transactionId>` tagjéből, ami minden beküldési eseményhez egyedi köteg-ID. Egy CREATE → egy `transaction_id`; egy MODIFY/STORNO → **másik** `transaction_id`. Tehát az audit-lánc minden lépése külön sorba kerül a DB-ben.
92
93
> **Történeti megjegyzés:** az eredeti tervben content-hash dedupe szerepelt, mert a `tetelek -o json` kimenetben nem volt sem `batch_index`, sem stabil esemény-PK. A NAV-projekt \#2 issue-ja erre épült: a Rust oldal hozzáadta a `transaction_id`, `batch_index`, `modification_index`, `original_invoice_number` mezőket. Ezekkel a content-hash workaround feleslegessé vált.
94
95
### Mezők
96
97
<table>
98
<colgroup>
99
<col style="width: 33%" />
100
<col style="width: 33%" />
101
<col style="width: 33%" />
102
</colgroup>
103
<thead>
104
<tr class="header">
105
<th>Mező</th>
106
<th>Forrás</th>
107
<th>Megjegyzés</th>
108
</tr>
109
</thead>
110
<tbody>
111
<tr class="odd">
112
<td><code>transaction_id</code></td>
113
<td>NAV <code>&lt;transactionId&gt;</code></td>
114
<td><strong>PK</strong>. Beküldési köteg ID, számlánként egyedi</td>
115
</tr>
116
<tr class="even">
117
<td><code>invoice_number</code></td>
118
<td>NAV</td>
119
<td>Az emberek számára olvasható számlaszám (pl. <code>SZ/2026/00567</code>)</td>
120
</tr>
121
<tr class="odd">
122
<td><code>direction</code></td>
123
<td>(NavDaV)</td>
124
<td><code>OUTBOUND</code> / <code>INBOUND</code> — a <code>kimeno</code> / <code>bejovo</code> Rust-irányból</td>
125
</tr>
126
<tr class="even">
127
<td><code>invoice_operation</code></td>
128
<td>NAV</td>
129
<td><code>CREATE</code> / <code>MODIFY</code> / <code>STORNO</code></td>
130
</tr>
131
<tr class="odd">
132
<td><code>modification_index</code></td>
133
<td>NAV <code>&lt;modificationIndex&gt;</code></td>
134
<td>Több MODIFY ugyanahhoz a számlához → 1, 2, …; CREATE-nél <code>NULL</code></td>
135
</tr>
136
<tr class="even">
137
<td><code>original_invoice_number</code></td>
138
<td>NAV <code>&lt;originalInvoiceNumber&gt;</code></td>
139
<td>MODIFY/STORNO esetén az eredeti CREATE számlaszáma</td>
140
</tr>
141
<tr class="odd">
142
<td><code>batch_index</code></td>
143
<td>NAV <code>&lt;index&gt;</code></td>
144
<td>Pozíció a NAV-beküldési kötegen belül (audit-info)</td>
145
</tr>
146
<tr class="even">
147
<td><code>supplier_*, customer_*</code></td>
148
<td>NAV digest XML</td>
149
<td>Iránytól függően az egyik a partner, a másik mi</td>
150
</tr>
151
<tr class="odd">
152
<td><code>customer_vat_status</code></td>
153
<td>NAV per-számla XML <code>&lt;customerVatStatus&gt;</code></td>
154
<td><code>DOMESTIC</code> / <code>OTHER</code> (EU-s) / <code>PRIVATE_PERSON</code>. A digest-ben nincs, csak a per-számla XML-ben — a <code>tetelek</code> path tölti, a <code>mind</code>/<code>kimeno</code>/<code>bejovo</code> nem</td>
155
</tr>
156
<tr class="even">
157
<td><code>invoice_net_amount(_huf)</code><br />
158
<code>invoice_vat_amount(_huf)</code><br />
159
<code>invoice_gross_amount(_huf)</code></td>
160
<td>NAV (digest a net+vat-ot, per-számla XML a gross-t)</td>
161
<td>String-ként tárolva (decimal pontosság miatt). SIMPLIFIED-en a net+vat <code>NULL</code> — a NAV nem ad áfa-bontást, csak gross-t</td>
162
</tr>
163
<tr class="odd">
164
<td><code>invoice_accounting_delivery_date</code></td>
165
<td>NAV per-számla XML</td>
166
<td>Számviteli teljesítés napja (gyakran ≠ <code>invoice_delivery_date</code>)</td>
167
</tr>
168
<tr class="even">
169
<td><code>payment_date</code></td>
170
<td>NAV per-számla XML <code>&lt;paymentDate&gt;</code></td>
171
<td>Fizetési határidő — utalás-tervezéshez</td>
172
</tr>
173
<tr class="odd">
174
<td><code>payment_method</code></td>
175
<td>NAV per-számla XML <code>&lt;paymentMethod&gt;</code></td>
176
<td>TRANSFER / CASH / CARD / VOUCHER / OTHER</td>
177
</tr>
178
<tr class="even">
179
<td><code>supplier_address</code><br />
180
<code>customer_address</code></td>
181
<td>NAV per-számla XML</td>
182
<td>Összevont string (pl. <code>HU 6727 Szeged, Algyői út 19 A.</code>) — partner-nyilvántartás bővítéshez</td>
183
</tr>
184
<tr class="odd">
185
<td><code>supplier_bank_account_number</code></td>
186
<td>NAV per-számla XML</td>
187
<td>A kibocsátó bankszámlaszáma — irányfüggetlen (NAV csak a supplier-é). Bejövőn fizetési cél, kimenőn a saját</td>
188
</tr>
189
<tr class="even">
190
<td><code>community_vat_number</code></td>
191
<td>NAV per-számla XML</td>
192
<td>EU adóazonosító (pl. <code>HU24696371</code>). A kibocsátóé. INBOUND-on csak EU-s/külföldi szállítóknál van kitöltve</td>
193
</tr>
194
<tr class="odd">
195
<td><code>exchange_rate</code></td>
196
<td>NAV per-számla XML</td>
197
<td>Pénznem-árfolyam HUF-ra. <code>"1"</code> HUF-számláknál</td>
198
</tr>
199
<tr class="even">
200
<td><code>invoice_appearance</code></td>
201
<td>NAV per-számla XML</td>
202
<td>ELECTRONIC / PAPER / EDI / UNKNOWN — Sztv. tárolási előírás</td>
203
</tr>
204
<tr class="odd">
205
<td><code>completeness_indicator</code></td>
206
<td>NAV per-számla XML</td>
207
<td>NAV digital invoice teljesszerűségi flag (string <code>"true"</code>/<code>"false"</code>)</td>
208
</tr>
209
<tr class="even">
210
<td><code>invoice_issue_date</code><br />
211
<code>invoice_delivery_date</code></td>
212
<td>NAV</td>
213
<td>ISO dátum string</td>
214
</tr>
215
<tr class="odd">
216
<td><code>currency</code></td>
217
<td>NAV</td>
218
<td><code>HUF</code>, <code>EUR</code>, …</td>
219
</tr>
220
<tr class="even">
221
<td><code>raw_json</code></td>
222
<td>(NavDaV)</td>
223
<td>A teljes JSON sorrendezett alakja — debug és későbbi remap miatt</td>
224
</tr>
225
<tr class="odd">
226
<td><code>ins_ts</code></td>
227
<td>(NavDaV)</td>
228
<td>UTC timestamp az első insert-kor (UPDATE nem módosítja)</td>
229
</tr>
230
</tbody>
231
</table>
232
233
### state — kulcs-érték
234
235
| Kulcs               | Mit jelent                                                                                                                                                            |
236
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
237
| `last_check_ts`     | UTC ISO timestamp az utolsó NAV-hívási **kísérletről** (siker és hiba is). Throttle bemenete.                                                                         |
238
| `last_valtozas_ts`  | UTC ISO timestamp — az utolsó sikeres delta-refresh **indítási** ideje. A következő `valtozas --since` ettől szűr. Insdate-alapú delta (késedelmes-beküldés-immunis). |
239
| `last_success_date` | Visszafelé kompatibilitásra a \`--full\`/\`--from\`/\`--to\` (tetelek-alapú) refresh-hez.                                                                             |
240
| `last_success_ts`   | UTC ISO timestamp az utolsó sikeres refresh-ről. A render-cache invalidáció bemenete.                                                                                 |
241
242
## NAV bridge — delta + state
243
244
`nav_bridge.py` mind library, mind CLI. A library oldalt a WebDAV provider importálja, a CLI cron-ból futtatható ha érdemesnek látjuk.
245
246
### Public API
247
248
| Függvény                                     | Mit csinál                                                           |
249
|----------------------------------------------|----------------------------------------------------------------------|
250
| `init_db(path)`                              | SQLite kapcsolat + séma (`schema.sql`) végrehajtás                   |
251
| `call_nav(direction, frm, to, ...)`          | Subprocess-hívás a Rust binary-ra, JSON visszaadás                   |
252
| `upsert_invoices(conn, invoices, direction)` | UPSERT az `invoices` + tételsor-újraírás                             |
253
| `compute_delta_window(conn)`                 | (frm, to) — vagy `last_success_date − 2`, vagy `2024-01-01` ha üres  |
254
| `refresh(conn, frm, to, ...)`                | A teljes lánc: két irány fetch + upsert + state update. Hibát feldob |
255
| `maybe_refresh_delta(conn)`                  | Throttled, lock-olt verzió. **Ezt hívja a WebDAV provider.**         |
256
257
### maybe_refresh_delta — döntési fa
258
259
- GET érkezik a /szamlak.xlsx-re
260
  - \_refresh_lock acquire
261
    - last_check_ts létezik és \< 60s?
262
      - IGEN → throttled, return {throttled: True} (nincs NAV-hívás)
263
      - NEM → refresh() futtatás
264
        - SIKER → last_success_date + last_success_ts + last_check_ts frissítés
265
        - HIBA → record_check_failure() → csak last_check_ts frissül
266
  - \_refresh_lock release
267
268
A lock + state-tábla kombináció így működik 5 párhuzamos GET esetén: az 1. szál bejut, futtatja a refresh-t (~1-2 mp), kilép. A 2-5. szálak közben a lockon várnak; mire bejutnak, a `last_check_ts` már friss → throttled. Egyetlen NAV-roundtrip per cluster.
269
270
### Hibakezelés filozófia
271
272
> **Stale \> nincs.** Ha a NAV API hibázik, a NavDaV **nem dob 5xx-et**. A DB-ben tárolt utolsó ismert állapotot rendereli, és serve-eli a stale xlsx-et. A `last_check_ts` így is frissül, hogy a throttle ne tegye végtelen ciklusba a hibázó API-t.
273
274
## Throttle és render-cache
275
276
Két különböző cache van, ne keverjük össze:
277
278
> **Throttle (NAV-hívás cache)**
279
>
280
> **Cél:** a NAV API ne kapjon 60 másodpercen belül több hívást.
281
>
282
> **Bemenet:** `state.last_check_ts` (DB-ben, restart-tűrő).
283
>
284
> **Ablak:** 60 mp (`THROTTLE_SECONDS`).
285
>
286
> **Ha hit:** a kód a régi DB-tartalmat fogja renderelni — friss API-hívás nem történik.
287
288
> **Render-cache (xlsx-bytes cache)**
289
>
290
> **Cél:** ne renderelje újra az xlsx-et minden GET-re ha az adat nem változott.
291
>
292
> **Bemenet:** `state.last_success_ts` vs cache-ben tárolt érték.
293
>
294
> **Ablak:** nincs idő — amíg a state nem mozdul, hit.
295
>
296
> **Ha hit:** ugyanazt a bytes-objektumot adjuk vissza, ETag is azonos → Nextcloud nem tölti újra.
297
298
Tipikus szekvencia (két párhuzamos GET 5 mp különbséggel):
299
300
| Idő  | Esemény | NAV-hívás      | Render                                         | Bytes-méret      |
301
|------|---------|----------------|------------------------------------------------|------------------|
302
| T+0  | 1\. GET | igen           | igen (state változott)                         | 1.45 MB          |
303
| T+5  | 2\. GET | nem (throttle) | nem (cache hit)                                | 1.45 MB (cached) |
304
| T+90 | 3\. GET | igen           | függ — ha új esemény jött, igen; egyébként nem | —                |
305
306
### Beépített delta-scheduler
307
308
A NavDaV szerver indít egy daemon thread-et (`delta_scheduler.DeltaScheduler`), amely periodikusan meghívja a `nav_bridge.maybe_refresh_delta`-t, függetlenül attól hogy érkezett-e kliens-GET. Cél: hosszú inaktivitás után az első kliens-megnyitás **ne** találkozzon nagy NAV-hátralékkal és ezért percekig tartó delta-fetch + render láncolattal.
309
310
| Paraméter        | Érték                           | Forrás                                                    |
311
|------------------|---------------------------------|-----------------------------------------------------------|
312
| Default periódus | 1800 s (30 perc)                | `delta_scheduler.DEFAULT_INTERVAL_SECONDS`                |
313
| CLI flag         | `--delta-interval <sec>`        | `webdav_server.py`                                        |
314
| Env var          | `NAVDAV_DELTA_INTERVAL_SECONDS` | docker-compose.yml-ben állítható                          |
315
| Letiltás         | `--delta-interval=0`            | például manuális cron mellett                             |
316
| Min. érték       | 60 s                            | `DeltaScheduler.__init__` ValueError-t dob ennél kisebbre |
317
318
A scheduler tick a meglévő throttle-on át megy: ha közben kliens-GET már lefuttatta a delta-frissítést a 60s-os ablakon belül, a tick `throttled`-et kap és kihagy. Tehát a scheduler nem duplázza a NAV-hívást aktív klienshasználat mellett — csak inaktivitás idején tartja "melegen" a DB-t.
319
320
> **Hibakezelés.** A scheduler tick minden Exception-t a logba ír (`navdav.scheduler` logger), majd a következő tick-en újrapróbálja. Egyetlen tick-hiba nem állítja le a folyamatos frissítést — ez a daemon "fail-soft" működés.
321
322
## xlsx renderer
323
324
`render_xlsx.py`. `xlsxwriter` backend, in-memory mód (`{"in_memory": True}`), `BytesIO` output. Két lap, autofilter, freeze pane.
325
326
### Lap: Számlák (egy sor = egy NAV-esemény)
327
328
28 könyvelői oszlop (issue \#6 megoldása után). Technikai mezők (transaction_id, batch_index, modification_index, ins_ts) az SQLite-ban maradnak, de az xlsx-be nem kerülnek.
329
330
| \#    | Oszlop                        | Forrás                                                        | Formátum                                                                                            |
331
|-------|-------------------------------|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
332
| 1     | Irány                         | `direction`                                                   | magyar címke (Bejövő / Kimenő)                                                                      |
333
| 2     | Számlaszám                    | `invoice_number`                                              | —                                                                                                   |
334
| 3     | Művelet                       | `invoice_operation`                                           | CREATE / MODIFY / STORNO                                                                            |
335
| 4     | Eredeti számla                | `original_invoice_number`                                     | STORNO/MODIFY-nál a CREATE száma                                                                    |
336
| 5     | Utólagos művelet              | subquery: ugyanezen invoice_number-re hivatkozó STORNO/MODIFY | pl. `STORNO SZ/2026/00405`; láncolat esetén pontosvesszővel                                         |
337
| 6     | Kelt                          | `invoice_issue_date`                                          | `yyyy.mm.dd.`                                                                                       |
338
| 7     | Teljesítés                    | `invoice_delivery_date`                                       | `yyyy.mm.dd.`                                                                                       |
339
| 8     | Számv. teljesítés             | `invoice_accounting_delivery_date`                            | `yyyy.mm.dd.` — Sztv. szerinti könyvelési teljesítés napja                                          |
340
| 9     | Fizetési határidő             | `payment_date`                                                | `yyyy.mm.dd.`                                                                                       |
341
| 10–11 | Partner / Adószám             | iránytól függő (supplier vagy customer)                       | —                                                                                                   |
342
| 12    | Közösségi adószám             | `community_vat_number`                                        | Áfa tv. szerinti közösségi adóazonosító — a kibocsátóé                                              |
343
| 13    | Partner címe                  | iránytól függő (supplier_address vagy customer_address)       | összevont string                                                                                    |
344
| 14    | Bankszámlaszám                | `supplier_bank_account_number`                                | A kibocsátóé — INBOUND-on a fizetési cél, OUTBOUND-on a saját                                       |
345
| 15    | Adóalany státusza             | `customer_vat_status`                                         | DOMESTIC / OTHER / PRIVATE_PERSON                                                                   |
346
| 16    | Bizonylattípus                | `invoice_category`                                            | NORMAL / SIMPLIFIED / AGGREGATE — Sztv. és Áfa tv. szerinti számla-típus                            |
347
| 17    | Megjelenési forma             | `invoice_appearance`                                          | ELECTRONIC / PAPER / EDI / UNKNOWN — NAV hivatalos terminus                                         |
348
| 18    | Teljességi mutató             | `completeness_indicator`                                      | NAV XSD-ből: `true` ha az adatszolgáltatás maga a számla; `false` ha külön számladokumentum létezik |
349
| 19    | Pénznem                       | `currency`                                                    | ISO 4217 kód                                                                                        |
350
| 20    | Árfolyam                      | `exchange_rate`                                               | HUF-ra; `1` HUF-számláknál                                                                          |
351
| 21    | Fizetési mód                  | `payment_method`                                              | TRANSFER / CASH / CARD / VOUCHER / OTHER                                                            |
352
| 22–23 | Nettó / ÁFA (eredeti pénznem) | `invoice_net_amount` / `invoice_vat_amount`                   | per-row: HUF→`#,##0\ "Ft";[Red]-…;-`, egyéb→`#,##0.00;[Red]-…;-`                                    |
353
| 24    | ÁFA-kulcs                     | tételsorok homogén-rátája                                     | `0%` homogén esetben; `vegyes` string vegyes ráták esetén; üres ha nincs ráta-adat                  |
354
| 25    | Bruttó (eredeti pénznem)      | `invoice_gross_amount` vagy `net+vat`                         | mint Nettó/ÁFA — per-row HUF/deviza                                                                 |
355
| 26–28 | Nettó / ÁFA / Bruttó (Ft)     | NAV HUF mezők                                                 | `#,##0\ "Ft";[Red]-#,##0\ "Ft";-` — ISO 80000-1 szerint a mértékegység jel az érték után, szóközzel |
356
357
Autofilter, freeze az 1. sor + 2 oszlop, sorrendezés: `invoice_issue_date DESC, invoice_number DESC`.
358
359
> **Pénzformátum konzisztencia (per-row dinamika):** ugyanaz a számérték minden helyen ugyanúgy formázódik. A „eredeti pénznem" oszlopok per-row választanak: HUF-számlán integer „Ft" formátum (mint a HUF oszlopok), nem-HUF számlán 2-tizedes deviza formátum. Tilos ugyanazt a HUF értéket az egyik oszlopban `4 245 000 Ft`, a másikban `4 245 000.00`-ként mutatni.
360
361
> **Könyvelői munkafájl-szemlélet** — az xlsx pontosan azt tartalmazza amit a NAV ad. SIMPLIFIED számláknál a NAV nem közöl nettó/ÁFA bontást (sem invoice-szinten, sem tételsoronként), csak bruttót — ezeknél a Nettó és ÁFA üresen marad, és a Kategória oszlop jelzi az okot. Silent fallback (pl. „feltételezzünk 27%-ot") tilos: hibás könyvelési adatot adna.
362
363
### Lap: Tételek (egy sor = egy számlatétel)
364
365
26 oszlop (NAV CLI v1.5.1, 11+ új tétel-szintű mezővel): számla-szintű kontextus (irány, számlaszám, kelt, tétel-teljesítés, partner) → tételsor (sor#, megnevezés, cikkszám, jelleg, mennyiség, mértékegység, saját mértékegység) → összegek (egységár, nettó, ÁFA-kulcs, ÁFA, bruttó, ÁFA-tartalom) → pénznem és HUF-mezők (Nettó/ÁFA/Bruttó (Ft)) → flag-ek (Előleg, Kedvezmény %, Kedvezmény, Közvetített). 13 549 sor; rendezés: `invoice_issue_date DESC, invoice_number DESC, line_number ASC`. A pénzformátum ugyanaz a per-row HUF/deviza választás mint a Számlák lapon. A mennyiség formátuma `#,##0.###`.
366
367
> **Bruttó / ÁFA-tartalom oszlopok jelenlétének NAV-szabályos eloszlása:** a `Bruttó` és `Bruttó (Ft)` oszlopok **csak a SIMPLIFIED kategóriájú számlák tételsorain vannak kitöltve** — ennek oka, hogy a NAV az egyszerűsített számláknál tételsor szinten **csak bruttót közöl** (nincs nettó/ÁFA-bontás). NORMAL és AGGREGATE számláknál a NAV tételsoronként **nettó + ÁFA-t ad** (és nem bruttót), így a sor-bruttó ezeknél a `Nettó + ÁFA` összegként számolható. Ugyanez vonatkozik az `ÁFA-tartalom` oszlopra: ez egyszerűsített számláknál a bruttóba foglalt áfa-arány, NORMAL/AGGREGATE-en üres. Nem exporthiány, hanem a magyar Áfa tv. és a NAV digital invoice szerkezet közvetlen leképezése.
368
369
### Lap: ÁFA-bontás (egy sor = egy ÁFA-rátarendszer egy számlán)
370
371
A NAV \`\<summaryByVatRate\>\` strukturált bontása: minden számlához ÁFA-rátánként egy sor. Vegyes-rátás számláknál (pl. 27% és 5% külön) egy számlára több sor jut. A NavDaV az invoice-szintű `vat_rate_summaries` JSON-tömbből bontja ki — kötelező az Áfa-bevallás előkészítéséhez (ÁFA-soronkénti összesítés). Oszlopok: Irány, Számlaszám, Kelt, Partner, ÁFA-kulcs, Nettó, ÁFA, Bruttó (eredeti pénznem) + Nettó/ÁFA/Bruttó HUF + Pénznem. 5 800 számla → 5 931 sor (98,5% kitöltöttség, vegyes-rátás eseteknél több sor).
372
373
### Lap: Duplikáció-gyanú (egy sor = egy gyanús csoport)
374
375
Azokat a számla-csoportokat sorolja fel, ahol **azonos partner**, **azonos kelt** és **azonos bruttó (Ft)** mellett ≥2 **élő** számla szerepel — vagyis CREATE vagy MODIFY események, amelyekhez **nincs utólagos STORNO**. A stornózott CREATE-eket és magukat a STORNO-eseményeket a szűrő kihagyja, így a listában csak azok az esetek maradnak, ahol valódi (nem-stornózott) duplikáció gyanúja áll fenn.
376
377
Oszlopok: Db, Irány, Partner, Kelt, Bruttó (Ft), Számlaszámok (csoportosítva pontosvesszővel), Művelet(ek), Pénznem. Rendezés: bruttó abszolút érték desc, db desc.
378
379
### Lap: Időbeli kérdés (egy sor = egy számla)
380
381
Azon számlák, ahol `payment_date < invoice_issue_date` — a fizetési határidő a kelt elé esik. Oszlopok: Irány, Számlaszám, Művelet, Kelt, Fiz. határidő, Eltérés (nap, negatív), Partner, Bruttó (Ft), Pénznem. Rendezés: az eltérés nap-ban (legnagyobb negatív → legkisebb negatív). Lehetséges okok: utólag kibocsátott számla, dátumelírás, vagy STORNO/MODIFY ahol a NAV az eredeti számla payment_date-jét visszaadja.
382
383
### Lap: Partner-egyezés (egy sor = egy csoport)
384
385
Két irányú adózóazonosítási inkonzisztencia: **1 adószám → több név** (ugyanannak a partnernek többféle névírása), és **1 név → több adószám** (ugyanaz a név alatt két különböző cég, pl. jogutódlás vagy hiba). A saját adószám (24696371) tipikusan dominál — a partnerek a saját adószámunkat 40+ formában szerepeltetik a számlán (rövidítések, kis/nagybetű, cégforma-rövidítés stb.). Oszlopok: Típus (1 adószám → több név / 1 név → több adószám), Csoport (adószám vagy név), Variáns(ok), Db előfordulás.
386
387
> **Számformátum:** a NAV JSON minden numerikus értéket string-ként ad (`"104700"`). A renderer `Decimal()`-en keresztül konvertál float-tá az xlsx cella számértékéhez (lebegőpontos hiba ellen). A `vat_percentage` 0.27 jön — Excel `0%` formátum 27%-ként mutatja.
388
389
## WebDAV provider
390
391
`webdav_server.py`. `wsgidav` 4.x DAV provider + `cheroot` WSGI szerver, kötés **localhost-only** (`127.0.0.1:8080`). Read-only.
392
393
### Erőforrás-fa
394
395
    /                                   ← RootCollection (DAVCollection)
396
    └── szamlak.xlsx                     ← XlsxResource (DAVNonCollection)
397
398
A `NavDavProvider.get_resource_inst(path, environ)` dönti el, melyik osztályt példányosítja a kért útvonalra. Bármi más → 404.
399
400
### PROPFIND vs GET — fontos distinkció
401
402
A Nextcloud (és minden WebDAV kliens) gyakran küld `PROPFIND`-okat metaadat-frissítésre, anélkül hogy a tartalmat letöltené. Ezek **nem trigger-elhetnek NAV-hívást**, különben a NAV API-t ártatlan könyvtárlistázás miatt is hammerelnénk.
403
404
- XlsxResource.\_\_init\_\_(path, environ)
405
  - REQUEST_METHOD == "GET"?
406
    - IGEN → \_trigger_refresh() — maybe_refresh_delta() hívás
407
    - NEM (PROPFIND, HEAD, OPTIONS) → refresh skip
408
  - \_ensure_cache() — render xlsx ha cache üres vagy state mozdult
409
410
A `HEAD` esetén — bár headers-only választ kérünk — szintén nem indul refresh; a méret/ETag a cache-ből jön. Ha a cache még üres (első kérés a szerver indulása óta), `_ensure_cache()` renderel egyet — de NAV-hívás akkor is kimarad.
411
412
### HTTP fejlécek
413
414
| Fejléc         | Forrás                                | Példa                                                               |
415
|----------------|---------------------------------------|---------------------------------------------------------------------|
416
| Content-Type   | fix                                   | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
417
| Content-Length | `len(_cache["bytes"])`                | `1451511`                                                           |
418
| ETag           | md5(content)                          | `3ab628c5d57e138082b71b167d68d5af`                                  |
419
| Last-Modified  | `last_success_ts` → unix → HTTP dátum | `Thu, 30 Apr 2026 20:06:55 GMT`                                     |
420
421
### Authentication
422
423
HTTP Basic, `SimpleDomainController` egy user-mappinggel. A user/jelszó környezeti változókból:
424
425
    NAVDAV_USER=<username>        # default: navdav
426
    NAVDAV_PASSWORD=<password>     # KÖTELEZŐ — nélküle a szerver nem indul
427
428
Digest auth kikapcsolva, csak Basic. Localhost-only kötés miatt elfogadható (TLS-t a Nextcloud bekötés sem igényel ezen a szakaszon).
429
430
## Bootstrap és üzemeltetés
431
432
Két támogatott deploy-mód: **Docker** (production-ready, ajánlott) és **natív** (fejlesztés / sandbox VM). Mindkettő idempotens — ismételt futtatás esetén frissítés.
433
434
### Production deploy — egysoros, Gitea-ról
435
436
Előfeltételek a target gépen: **Docker engine + docker compose plugin**, a futtató user a `docker` csoport tagja, `curl` elérhető. Semmi más rendszer-csomag (Python, Rust, git, sqlite) nem szükséges — minden a konténerben.
437
438
    curl -fsSL "http://kpeter:<TOKEN>@192.168.2.210:3000/kpeter/navdav/raw/branch/master/install-from-gitea.sh" | \
439
        GITEA_TOKEN=<TOKEN> bash
440
441
A `install-from-gitea.sh` 8 lépésben telepít:
442
443
1.  Előfeltétel-ellenőrzés (docker, compose, daemon)
444
2.  Deploy mappa: `~/navdav`
445
3.  `docker-compose.yml` letöltése Gitea-ról (curl, nincs git-clone — kevesebb host-csomag)
446
4.  `nav.env` — interaktív tty-n bekérdezi az 5 NAV credential-t; pipe esetén utasítást ad kézi feltöltésre
447
5.  `.env` — random 24-karakteres NavDaV-jelszó + `GITEA_TOKEN` (build-időre, a NAV CLI klónozásához)
448
6.  `docker compose build --pull` + `up -d --force-recreate`
449
7.  Healthcheck-megvárás (max 60s)
450
8.  Bootstrap, ha üres a DB (idempotens — második futtatáson kihagyva)
451
452
Záróképernyő: a Nextcloud External Storage-konfigurációhoz szükséges 3 érték (URL, user, password). A deploy mappában (`~/navdav`) marad: `docker-compose.yml`, `nav.env`, `.env`. A SQLite DB és cached xlsx a `navdav-data` Docker volume-ban perzisztál.
453
454
### Frissítés — ugyanaz a parancs
455
456
Az `install-from-gitea.sh` ismételt futtatása frissítésnek minősül: friss `docker-compose.yml` letöltődik, image rebuild (Docker layer cache miatt gyors), konténer `--force-recreate`-tel újraindul. `nav.env`, `.env` és a `navdav-data` volume megmarad.
457
458
### Docker-image szerkezete
459
460
| Réteg                                 | Tartalom                                                                                                    |
461
|---------------------------------------|-------------------------------------------------------------------------------------------------------------|
462
| Stage 1 — `rust:slim-bookworm`        | NAV CLI Rust-bináris fordítása (build-context-ben klónozva a Gitea-ról; csak a végbinárist viszi tovább)    |
463
| Stage 2 — `python:3.12-slim-bookworm` | Python runtime + `xlsxwriter` + `wsgidav` + `cheroot` + `sqlite3`; a NavDaV kód a build-context-ből másolva |
464
| Volume                                | `/data` — SQLite DB, cached xlsx                                                                            |
465
| Bind mount                            | `./nav.env → /opt/nav/.env` (RO) — NAV credentials a Rust binárisnak                                        |
466
| Env-file                              | `./.env` — NavDaV credentials + GITEA_TOKEN (csak build-time)                                               |
467
| Hálózat                               | port 8081, kötés `0.0.0.0:8081 → conténer 8081` (LAN-elérhetőség Nextcloud-szerver felé)                    |
468
| Healthcheck                           | 30s interval, TCP socket connect-test localhost:8081                                                        |
469
| Restart policy                        | `unless-stopped` — daemon-restartolásnál is feláll automatikusan                                            |
470
471
### Kanonikus üzemeltetési parancsok
472
473
| Cél                                     | Parancs                                                                                     |
474
|-----------------------------------------|---------------------------------------------------------------------------------------------|
475
| Status                                  | `docker compose ps`                                                                         |
476
| Logok                                   | `docker compose logs -f --tail=30`                                                          |
477
| Manuális bootstrap (ha kell)            | `docker compose exec navdav python bootstrap_import.py --from 2024-01-01 --to "$(date -I)"` |
478
| Manuális delta-refresh (CLI, throttled) | `docker compose exec navdav python nav_bridge.py`                                           |
479
| Restart                                 | `docker compose restart`                                                                    |
480
| Stop (volume megmarad)                  | `docker compose down`                                                                       |
481
| Stop + adat-törlés                      | `docker compose down -v` ⚠️ a teljes `navdav-data` volume is törlődik                       |
482
| Smoke test                              | `curl -s -u "navdav:$PASS" -I http://localhost:8081/szamlak.xlsx | head -5`                 |
483
484
### Backup
485
486
A `navdav-data` Docker volume tartalmazza a teljes történeti NAV-adatot. Backup tipikusan crontab-ból:
487
488
    # Minden éjjel 02:00-kor — SQLite online backup, file-konzisztens snapshot
489
    0 2 * * * docker compose -f $HOME/navdav/docker-compose.yml exec -T navdav \
490
        sqlite3 /data/navdav.db ".backup '/data/backups/navdav-$(date +\%Y\%m\%d).db'"
491
492
A volume-mount-on belül a `/data/backups/` mappa a host-on perzisztál. Ha az adat elveszne, a `bootstrap_import.py` egy parancs alatt visszaállítja a NAV-ból (~3-5 perc).
493
494
### Natív telepítés (alternatíva, fejlesztés)
495
496
Ha Docker nem elérhető vagy fejlesztői változó-tesztek kellenek: a `deploy.sh` script a Rust toolchain-t és Python venv-et közvetlenül a host-ra telepíti, `systemd`-unit-tal indít.
497
498
    cd ~/navdav
499
    sudo -E bash deploy.sh   # admin-jog kell apt + systemd-unit miatt
500
501
Részletek: a script kommentjeiben. Production-ra a Docker-út ajánlott.
502
503
## Nextcloud bekötés
504
505
A Nextcloud admin felületen **External Storage** (Settings → Administration → External storages):
506
507
| Mező             | Érték                                                             |
508
|------------------|-------------------------------------------------------------------|
509
| Name             | `NAV számlák` (vagy bármi)                                        |
510
| External storage | `WebDAV`                                                          |
511
| URL              | `http://192.168.2.211:8081/` (a NavDaV-VM, „web", LAN-os bekötés) |
512
| Username         | `NAVDAV_USER` értéke                                              |
513
| Password         | `NAVDAV_PASSWORD` értéke                                          |
514
| Available for    | érintett user vagy csoport                                        |
515
| Read-only        | igen — a NavDaV nem fogad el írást                                |
516
517
### Megnyitási módok
518
519
| Hogyan nyitja a user            | Mit lát                                                                                                                         |
520
|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
521
| Web UI → Collabora / OnlyOffice | A Nextcloud szerver olvas a WebDAV-ról minden megnyitáskor → mindig friss, tényleg minden alkalommal NAV-fetch (vagy throttled) |
522
| Desktop sync + Excel            | A kliens ETag-alapján dönt — ha a hash ugyanaz, csak cached fájlt nyit; ha új render, újra letölti                              |
523
| Virtual files                   | Ugyanaz mint a desktop — ETag-vezérelt                                                                                          |
524
525
## Fájlok és tech stack
526
527
| Fájl                             | Mit tartalmaz                                                                                            | Technológia                    |
528
|----------------------------------|----------------------------------------------------------------------------------------------------------|--------------------------------|
529
| `schema.sql`                     | SQLite séma — `invoices`, `invoice_lines`, `state`, indexek                                              | SQL WAL mode, FK on            |
530
| `nav_bridge.py`                  | NAV subprocess + UPSERT + state + throttle/lock + CLI                                                    | Python stdlib only             |
531
| `render_xlsx.py`                 | SQLite → in-memory xlsx, két lap, autofilter, freeze                                                     | Python + `xlsxwriter`          |
532
| `webdav_server.py`               | WebDAV provider (DAVProvider, DAVCollection, DAVNonCollection) + render-cache + Basic auth               | Python + `wsgidav` + `cheroot` |
533
| `bootstrap_import.py`            | Vékony wrapper a `nav_bridge.refresh()` körül, default `2024-01-01 → ma`                                 | Python                         |
534
| `requirements.txt`               | Csak runtime deps: `xlsxwriter`, `wsgidav`, `cheroot`                                                    | —                              |
535
| `Dockerfile`                     | Multi-stage build: Rust-stage NAV CLI + Python-stage NavDaV runtime; env-paraméteres path-ok             | Docker                         |
536
| `docker-compose.yml`             | Production deploy konfig: remote Gitea-context build, healthcheck, volumes, port mapping, restart policy | Compose                        |
537
| `install-from-gitea.sh`          | Egysoros telepítő (curl \| bash); idempotens; `nav.env`+`.env` generálás, build, healthcheck, bootstrap  | Bash                           |
538
| `deploy.sh`                      | Natív (Docker nélküli) deploy alternatíva — Rust + Python venv + systemd                                 | Bash                           |
539
| `navdav.db` (vagy Docker volume) | SQLite akkumulátor — **nincs Git-ben**. Docker módban a `navdav-data` volume-ban                         | —                              |
540
541
### Külső függőség (nem ebben a repóban)
542
543
> **/home/petitan/nav (Rust binary)**
544
>
545
> Rust A `nav-online-invoice` crate. A NavDaV minden subprocess-hívást ennek a release build-jére intéz: `/home/petitan/nav/target/release/nav-online-invoice`. Az `.env` ott található (NAV credential-ok), ezért a subprocess `cwd=/home/petitan/nav`-val indul.
546
>
547
> A NavDaV két subcommand-ot használ: `valtozas -d <dir> --since <ts>` a delta-frissítéshez (insDate-alapú szűrés, késedelmes-beküldés-immunis), és `tetelek -d <dir> -f X -t Y` a bootstrap (teljes történeti betöltés) céljára. NAV CLI minimum verzió: v1.2.0 (a \`valtozas\` 32 mezős teljes outputja az ISSUES \#7 megoldásával jött).
548
549
## Nyitott pontok és iteratív állás
550
551
### Iteratív implementációs terv
552
553
| \#  | Lépés                                                                                                            | Állapot                                                            |
554
|-----|------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
555
| 1   | Bootstrap-import script (Rust JSON → SQLite)                                                                     | kész                                                               |
556
| 2   | xlsx render (SQLite → xlsx, CLI-tesztelhető)                                                                     | kész                                                               |
557
| 3   | NAV bridge (delta + state, cron-kompatibilis)                                                                    | kész                                                               |
558
| 4   | Throttle + lock                                                                                                  | kész                                                               |
559
| 5   | WebDAV provider, Basic auth, ETag/mtime                                                                          | kész                                                               |
560
| 6   | Nextcloud External Storage bekötés (LAN-bekötés)                                                                 | kész                                                               |
561
| 7   | Docker-image (multi-stage Rust+Python), docker-compose, healthcheck                                              | kész                                                               |
562
| 8   | Egysoros telepítő Gitea-ról (`install-from-gitea.sh`)                                                            | kész                                                               |
563
| 9   | Code review (code-stasi 36 találat) — silent fallback eliminálás, FK PRAGMA, throttle CLI-n, sheet konzisztencia | kész (1 elem halasztva: Docker token-leak — BuildKit secret mount) |
564
565
### Megfigyelt apróságok
566
567
> **1. Nem-determinisztikus xlsx bytes.** Két egymás utáni render eltérő MD5-öt és 1 byte-os méretkülönbséget produkál ugyanazon adat mellett. Az xlsx zip belső metadatája (xlsxwriter által beágyazott creation timestamp) okozza, nem az adat. Hatás: a Nextcloud kliens minden render után egyszer újratölti a fájlt. Ha zavaró, a renderer determinizálható (xlsxwriter `set_properties()` + fix időbélyeg, vagy a zip-en belüli `core.xml` postprocess).
568
569
> **2. Cross-window duplikátumok.** A Rust binary 35 napos chunk-szeleteléséből egyes számlák két szomszédos ablakban is megjelennek a határnál. Az UPSERT a `transaction_id`-vel ezeket helyesen deduplikálja — nem hiba, ezt akarjuk.
570
571
> **3. MODIFY/STORNO az éles adatban (2024-01..2026-05) nem volt.** A séma (`modification_index`, `original_invoice_number` oszlopok) felkészült rá, de az audit-lánc xlsx-ben való vizuális elrendezése csak akkor finomítható tovább, ha lesz is rajta tényleges esemény.
572
573
### NAV projektben felvett és lezárt issue-k
574
575
A NavDaV közvetlenül a `nav-online-invoice` Rust binárisra épül. A fejlesztés során három egymást követő issue került felvételre és megoldásra a NAV projektben — mindegyik közvetlenül érintett egy NavDaV mezőt vagy működést.
576
577
| \#                          | Probléma                                                                                                                                                                                                                                               | NavDaV-hatás                                                                                                                                        | Megoldás                                                                                                                                                                                                                                                                                                              |
578
|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
579
| [`#2`](../../nav/ISSUES.md) | `tetelek` JSON-ból hiányzott stabil esemény-PK                                                                                                                                                                                                         | content-hash workaround vagy gyenge `(invoice_number, op)` kulcs                                                                                    | lezárva 2026-04-30 — négy új mező: `transaction_id`, `batch_index`, `modification_index`, `original_invoice_number`                                                                                                                                                                                                   |
580
| [`#3`](../../nav/ISSUES.md) | `tetelek` üres `customer_name`-et adott amikor a NAV XML-ben `&amp;` volt                                                                                                                                                                              | 31 OUTBOUND számla partner-mezője üres maradt (~0,8% a 28 hónapos adatra)                                                                           | lezárva 2026-04-30 — block-rekonstrukcióban a raw bytes megmarad, az XML-escape-ek életben maradnak az újra-parsolásig. Regression unit teszt hozzáadva                                                                                                                                                               |
581
| [`#4`](../../nav/ISSUES.md) | `tetelek` JSON-ból hiányzott a `customer_vat_status`                                                                                                                                                                                                   | nem lehetett egyetlen lekérdezésből kiszűrni a 12 EU-s + magánszemély false-positive-ot a downstream hibalistákban                                  | lezárva 2026-04-30 — pontosítás: a NAV digest-ben nincs ez a tag, csak a per-számla XML-ben → új mező az `InvoiceWithLines`-on (analóg az `invoice_delivery_date`-hez)                                                                                                                                                |
582
| [`#5`](../../nav/ISSUES.md) | `tetelek` üres net+vat SIMPLIFIED-en, hiányzó `invoice_gross_amount` mindenhol                                                                                                                                                                         | 87 SIMPLIFIED + 24 AGGREGATE számla összegei üresek voltak az xlsx-ben                                                                              | lezárva 2026-05-01 — két új mező: `invoice_gross_amount`, `invoice_gross_amount_huf`. SIMPLIFIED-en a net+vat továbbra is `NULL` (a NAV maga nem ad), de a gross most ki van töltve                                                                                                                                   |
583
| [`#6`](../../nav/ISSUES.md) | `tetelek`-ből hiányzott 10 könyvelői alapmező (payment_date, payment_method, exchange_rate, invoice_appearance, supplier/customer címek, invoice_accounting_delivery_date, community_vat_number, completeness_indicator, supplier_bank_account_number) | könyvelői munkafájlból hiányzott a 10 alapmező — utalás-tervezés, főkönyvi feladás, partner-nyilvántartás bővítés ellehetetlenült                   | lezárva 2026-05-01 — új helper `extract_invoice_metadata()` a per-számla XML-ből 14 mezőt egyszerre kinyer; `extract_address()` a címekhez (silent fallback elkerülése). 10 új mező a `tetelek -o json`-ban + 1 mező (`supplier_bank_account_number`) a `szamla -o json`-ban. JSON ↔ CSV minden bővülés egy commitban |
584
| [`#7`](../../nav/ISSUES.md) | `valtozas` JSON csak digest-szintű 17 mezőt ad — nem használható tetelek-helyettesítőként                                                                                                                                                              | insDate-alapú késedelmes-beküldés-immunis delta-szinkron nem indítható helyileg adatvesztés nélkül                                                  | lezárva 2026-05-03 (v1.2.0) — közös `enrich_digests_with_invoice_data()` helper a tetelek és valtozas között, JSON kulcskészlet bit-pontosan azonos (33 mező + lines)                                                                                                                                                 |
585
| [`#8`](../../nav/ISSUES.md) | `tetelek`/`valtozas` JSON-ból hiányzik a tétel-szintű `<advanceData>` (előleg-jelölés)                                                                                                                                                                 | 351 előleg-tétel csak szöveg-alapon (\`line_description LIKE '%Előleg%'\`) volt szűrhető, strukturált megkülönböztetés nélkül                       | lezárva 2026-05-03 (v1.3.0) — `advance_indicator` minden tételsoron + audit-helper                                                                                                                                                                                                                                    |
586
| [`#9`](../../nav/ISSUES.md) | 11 új tétel-szintű mező + invoice-szintű `<summaryByVatRate>`                                                                                                                                                                                          | könyvelői xlsx-ben tételsoronkénti bruttó (NORMAL+SIMPLIFIED), kedvezmény, termékkód, jelleg, tétel-teljesítés; ÁFA-bevalláshoz rátarendszer-bontás | lezárva 2026-05-03 (v1.4.0+v2.0.0 fázis 4) — proaktív audit-bővítés és vat_rate_summaries                                                                                                                                                                                                                             |
587
588
Az issue-k megoldása után `bootstrap_import.py --from 2024-01-01` backfilleli minden korábbi adatot:
589
590
| Mérőszám                                           | Előtte          | Utána                                                                      |
591
|----------------------------------------------------|-----------------|----------------------------------------------------------------------------|
592
| Üres OUTBOUND `customer_name` adószámmal           | 31              | **0**                                                                      |
593
| `&`-tartalmú partner-nevek (28 hó, mindkét irány)  | 0 (silent fail) | **51**                                                                     |
594
| `customer_vat_status` kitöltöttség                 | nem létezett    | **5798/5798** (DOMESTIC: 5786, OTHER: 5, PRIVATE_PERSON: 7)                |
595
| SIMPLIFIED+AGGREGATE számlák Bruttó kitöltöttsége  | 0 / 111         | **111 / 111**                                                              |
596
| Fiz. határidő (`payment_date`) kitöltöttség        | 0 / 5798        | **5569 / 5798** (a 229 hiányzó NAV-tól sem jön — pl. készpénzes vagy régi) |
597
| Számlavezető bank (`supplier_bank_account_number`) | 0 / 5798        | **4861 / 5798** (1105 INBOUND + 3756 OUTBOUND)                             |
598
| Cím (`supplier_address`, `customer_address`)       | 0 / 5798        | **5798 / 5798** supplier_address; **5791 / 5798** customer_address         |
599
600
A NavDaV pipeline maga (Python, SQLite, xlsxwriter) az ellenőrzés során minden lépésben helyesen kezelte az `&` karaktert — a hiba a Rust digest-XML extractor block-rekonstrukciós ágában volt, a `tetelek -o json` emisszió forrásánál. A `szamla -o json` path mindvégig korrektül dekódolta ugyanazokat a számlákat.
601
602
------------------------------------------------------------------------
603
604
NavDaV — NAV Online Számla → Nextcloud Excel híd · Petitan Kft.