Az InnVoice dokumentáció topic-mappába kerül, INNV topic-kóddal

- docs/INNVOICE.md → docs/INNVOICE/README.md, a három SQL runbook mellé
- INNVOICE_INCOMING.md: a tükör indoka, a vízjel, a TABLE_ID szerkezeti felismerése, a tárolás buktatói és az ÁFA-kimutatás szabályai
- a "Planned migration" szakasz a dokumentumból a TODO-ba kerül: jövő idő nem doksi-tartalom
- INNVOICE_ISSUES.md: az árva számla-JS és a degradált első betöltés miatt beragadó vízjel
- INNVOICE_TODO.md: ütemezett szinkron és a közvetlen piszkozat-számla beküldés
- INNV topic-kód a .github/TOPIC_CODES.md-ben, mindkét irányra
This commit is contained in:
2026-08-26 15:20:09 +02:00
parent 82cc1515ba
commit e195f75cd9
10 changed files with 314 additions and 42 deletions
+1
View File
@@ -16,4 +16,5 @@ Per-repo topic registry for this plugin, per the **per-repo extension convention
| `ORDD` | ORDER-DRAFT | AI-assisted intake channel: inbound free-text messages (Viber/email/dictation) parsed into order drafts, admin review/approval, then PreOrder creation that feeds the existing PRE-ORDER conversion. Covers parsing, product/partner resolution, learning mappings, the draft lifecycle and the approval entry point. | `docs/ORDERDRAFT/` |
| `MARGIN`| ÁRRÉS | Beszerzési önköltség és árrés: a rendelés-tételek ↔ beszerzések (költség-rétegek) hozzárendelése, a fuvar- és egyéb költségek tárolása és szétosztása, deviza- és áfakezelés az árrés szempontjából, valamint a kimutatás dokumentum / kamion / hónap bontásban. | `docs/MARGIN/` |
| `PMDATA`| TERMÉK-TÖRZSADAT | A termék mérési/logisztikai törzsadata (`IsMeasurable`, `Tare`, `AverageWeight`, `AverageWeightTreshold`, `CratesPerPallet`): tárolása, írási útjai, validálása és tömeges szerkesztése. A készlet-vezérelt értékek (`NetWeight`, `IncomingQuantity`) NEM ide tartoznak — azok a `STOCK` és a `PREO` topiké. | `docs/PMDATA/` |
| `INNV` | INNVOICE | Az InnVoice számlázó-rendszer integrációja, mindkét irányban: a kimenő rendelés-/számla-beküldés az admin rendelés-oldalról, és a szállítói (bejövő) számlák tükrözése, valamint az arra épülő havi ÁFA-kimutatás. A hozzáférés és a kvóta közös, ezért egy topik. A NAV-adatszolgáltatás NEM ide tartozik — az az `EKAER` topiké. | `docs/INNVOICE/` |
| `NOTIF` | ÉRTESÍTÉS | Vevői e-mail értesítők: a plugin saját `MessageTemplate`-jei, a nop token-provider kiváltása és a saját tokenek, a `MeasuringStatus`-átmenetekhez kötött kiváltás, valamint az ismétlés elleni jelölés. A nop natív értesítői NEM ide tartoznak. | `docs/NOTIFICATION/` |
-41
View File
@@ -1,41 +0,0 @@
# InnVoice integration — current state & planned migration
> Reference doc for `Nop.Plugin.Misc.FruitBankPlugin`. See [`README.md`](README.md) for the docs index.
> Scope: how the plugin pushes orders/invoices to the external **InnVoice** system from the admin order page.
## Where it lives
The admin **order edit** widget ([`Views/OrderAttributes.cshtml`](../Views/OrderAttributes.cshtml), rendered by `OrderAttributesViewComponent` into `AdminWidgetZones.OrderDetailsBlock`) hosts the InnVoice UI in its right-hand card *"Megrendelés beküldése Innvoice-ba"*.
Related server side: `InnVoiceApiService` (`Services/`), `InnVoiceOrderController` and `InvoiceController` (`Areas/Admin/Controllers/`).
## Current behaviour (order submission)
The active flow submits an **order** to InnVoice:
| UI | Endpoint |
|---|---|
| "Megrendelés beküldése" | `InnVoiceOrder/CreateOrder` |
| "Invoice adat ellenőrzése" | `InnVoiceOrder/GetOrderStatus` (also auto-run on page load) |
On success the card shows the InnVoice Order Table ID / Tech ID and a PDF link.
## Orphaned invoice JS (intentional, kept for the migration)
`OrderAttributes.cshtml` also contains a **full invoice-handling JS block** — handlers for `#createInvoiceBtn` / `#checkInvoiceBtn`, `displayInvoiceDetails(...)`, calls to `Invoice/CreateInvoice` and `Invoice/GetInvoiceStatus`, plus a page-load `GetInvoiceStatus` AJAX.
**These have no matching markup** (`#createInvoiceBtn`, `#invoiceDetails`, `#invoiceNumber`, `#invoicePdfLink`, … do not exist in the view), so the code is currently dead — the only live side effect is a silent, failing `GetInvoiceStatus` call on every order page load.
This is **not a bug to clean up yet**: it is groundwork for the planned migration below.
## Planned migration — direct invoice submission (draft)
We will switch the InnVoice integration from the current **order submission** to **direct invoice submission**, creating the invoice in **draft ("piszkozat") status** in InnVoice.
When we return to this:
- Wire the existing invoice JS to real buttons/markup in the InnVoice card (replacing or complementing the current order-submission buttons).
- Confirm `Invoice/CreateInvoice` creates the invoice in draft status.
- Decide the fate of the current `InnVoiceOrder/CreateOrder` order-submission path (keep, deprecate, or remove).
- Remove the wasted page-load `GetInvoiceStatus` call if it is not needed in the final design.
_Status: planned, not started. Recorded so the orphaned invoice code is understood as intentional._
@@ -0,0 +1,83 @@
# InnVoice — incoming (supplier) invoices
> Context: [`README.md`](README.md). Access and quota are stated there and not restated here.
Supplier invoices are mirrored from InnVoice into our own tables, and the monthly VAT report reads the mirror.
## Why a mirror and not a live query
- **The quota is narrow and lies.** A screen that queries on every open would be unpredictably incomplete, and nothing would say so.
- **The report survives an outage.** The mirror answers whether or not InnVoice is reachable.
- **The report needs data the API answers slowly** — several years, grouped by month.
## The watermark is the data
There is no "last synchronised" setting. The next incremental query asks from `MAX(IncomingInvoice.SourceUpdatedAt)` minus a one-minute safety margin.
- A half-finished run cannot skip invoices: what did not land also did not move the maximum.
- The safety margin exists because the provider does not document whether the `updatedtime` boundary is open or closed, nor what happens to rows modified within the same second. Re-processing a few rows is free — the upsert keys on `TABLE_ID` — while a silently missing invoice is not.
- **The hole:** a first load that ran degraded pins a wrong watermark and never heals on its own, because the watermark comes from the stored data. The escape hatch is the forced full load — [`MGFBANKPLUG-INNV-I-J8P5`](INNVOICE_ISSUES.md).
Full query only when the mirror is empty. `IncomingInvoiceSyncService.SyncAsync(forceFullLoad: true)` overrides that, and spends from the 10-per-30-days budget.
## Parsing the XML
**`TABLE_ID` is not unique in the document** — line items carry one too. Invoice elements are recognised structurally:
```csharp
document.Descendants().Where(element => element.Element("TABLE_ID") != null
&& !element.Ancestors().Any(ancestor => ancestor.Element("TABLE_ID") != null))
```
An invoice is an element with a `TABLE_ID` and **no ancestor** that has one. Matching on the element name instead parsed `<reszlet>` line items as invoices, which then collided on duplicate keys. The provider documents the fields, not the enclosing element names, so the shape has to be observed: `IncomingInvoiceController.Raw` returns the response unprocessed for exactly that.
Numbers and dates are read with **invariant culture** — the server may run under a Hungarian locale, where `1234.56` would read as thousands or fail.
## Endpoints used
| Method | URL | Note |
|---|---|---|
| `GetAllIncomingInvoicesAsync` | `/incominginvoice` | first load only — the 10-per-30-days query |
| `GetIncomingInvoicesByUpdateTimeAsync` | `/incominginvoice/updatedtime/{yyyyMMddHHmmss}` | the workhorse |
| `GetIncomingInvoicesByPerformanceDateAsync` | `/incominginvoice/teljesiteskelte/{yyyy.MM.dd.}` | targeted repair of one suspect day |
| `GetIncomingInvoiceByIdAsync` | `/incominginvoice/id/{tableId}` | does not spend from the id-less budget |
## Storage
`fbIncomingInvoice` (`IncomingInvoice`) + `fbIncomingInvoiceItem` (`IncomingInvoiceItem`), keyed on `InnVoiceTableId`. Header totals are stored as the provider states them; items carry the VAT rates.
Two facts the column list does not convey:
- **`ExchangeRate` may be 0** — the provider fills it only when the admin does. Zero means *unknown*, never 1. Treating it as 1 turns a EUR invoice into a HUF one at a fraction of its value; the same mistake in the margin feature produced 98.69% margins (`MGFBANKPLUG-MARGIN-B-T4M6`).
- **Only the first attachment is stored**, with `AttachmentCount` beside it. One PDF per invoice is the observed reality; the count is what makes the loss visible rather than silent.
`AttachmentUrl` carries a tenant-level access token. It is never rendered into the page, the page source or the CSV export — the viewer goes through `IncomingInvoiceController.Attachment(int id)`, which resolves the URL from our own database by invoice id and streams the bytes back **without a filename**, so the response is `inline` and embeddable. InnVoice serves it as `Content-Disposition: attachment`, which a browser downloads instead of displaying.
## Sync
`IncomingInvoiceSyncService.SyncAsync(bool forceFullLoad)`, driven from admin → *Bejövő számlák*:
| Button | Effect |
|---|---|
| Szinkronizálás | incremental from the watermark (full load if the mirror is empty) |
| Teljes újraszinkron | forced full load — spends from the 10-per-30-days budget |
| Nyers válasz | the unprocessed XML, for diagnosing structure |
Per-row `try`/`catch`: one bad invoice cannot take the rest of the batch with it. Without it the first collision dropped the remainder while the watermark had already moved, so the lost rows would not have come back on the next run. Rows without a `TABLE_ID` are skipped and **counted** (`SkippedCount`), failures counted separately (`FailedCount`) — a silent drop is as bad as a duplicate.
**Nothing runs this automatically.** The report does not sync on open, and says so in its header along with the mirror's age. Scheduling it: [`MGFBANKPLUG-INNV-T-M6K3`](INNVOICE_TODO.md).
## Monthly VAT report
`VatReportService` + admin → *ÁFA-kimutatás* (`VatReportController`, `Areas/Admin/Views/VatReport/Index.cshtml`).
**Internal orientation, not a tax return.** No reverse charge, no apportionment, no non-deductible items. The number exists to show the month's order of magnitude.
- **The month is the performance date** (`PerformanceDate`), not the invoice date.
- **Incoming = InnVoice invoices only.** Shipping costs (`fbShippingCost`) and supplier delivery notes are deliberately excluded: they also arrive through InnVoice, and counting both would double them.
- **Outgoing comes from our own side**, from the cost-consumption rows.
- **`DataAvailableFrom = 2026-08-01`** is a hard floor. The incoming side goes back years in the mirror; the outgoing side starts when the margin feature went live. An earlier month would show real incoming VAT against zero outgoing — a balance that looks like missing tax but is missing data.
- **Set-aside invoices** (`IsSetAside`) are shown, flagged.
- **Per-currency rows.** A non-HUF invoice contributes to the HUF total only through its `ExchangeRate`; with none, the row is listed under *"Ezekre kell árfolyam"* and excluded from the HUF figure rather than guessed. Only invoices with **non-zero VAT** need a rate — a 0% community acquisition changes nothing.
- **Rate buckets** are `SingleRate` / `Mixed` / `NoItems`, derived from the items but summing to the header total, so the breakdown has no rounding gap against the monthly figure. Header totals stay authoritative.
- **The text/currency filter touches the itemised list only** — never the monthly, per-currency or per-rate summaries. A supplier-filtered monthly VAT figure would state a half-truth while looking complete.
@@ -0,0 +1,31 @@
# INNVOICE — ISSUES
> ID format, Status vocabulary, type codes and archival are **not restated here** — see [`TOPIC_CODES.md`](../../.github/TOPIC_CODES.md) (→ framework registry).
Context: [`README.md`](README.md).
---
## MGFBANKPLUG-INNV-I-D4W9: Orphaned invoice JS on the order page costs one failing request per load
**Status:** Open · **Priority:** P3 · **Type:** I · **2026-08-26**
`Views/OrderAttributes.cshtml` carries a complete invoice-handling JS block — handlers for `#createInvoiceBtn` / `#checkInvoiceBtn`, `displayInvoiceDetails(...)`, calls to `Invoice/CreateInvoice` and `Invoice/GetInvoiceStatus` — with **no matching markup**: `#createInvoiceBtn`, `#invoiceDetails`, `#invoiceNumber`, `#invoicePdfLink` do not exist in the view.
The code is therefore dead except for one live side effect: a page-load `GetInvoiceStatus` AJAX call that fails silently on every order edit page.
> **Note:** the block is kept deliberately — it is groundwork for [`MGFBANKPLUG-INNV-T-B2N7`](INNVOICE_TODO.md). Deleting it is not the fix; wiring it, or removing the page-load call alone, is.
---
## MGFBANKPLUG-INNV-I-J8P5: A degraded first load pins a wrong watermark and cannot heal on its own
**Status:** Open · **Priority:** P2 · **Type:** I · **2026-08-26**
The incremental query starts from `MAX(IncomingInvoice.SourceUpdatedAt)`, which is derived from the stored data. If the first full load ran while the provider's 10-per-30-days budget was already spent, it returned deltas only — the mirror is partial, and the watermark now sits at the newest row that *did* arrive. Every later incremental run asks from there, so the missing older invoices are never requested again, and nothing in the data says they are missing.
**Affected:** `Services/IncomingInvoiceSyncService.cs``SyncAsync`.
**Mitigation in place:** the forced full load (`SyncAsync(forceFullLoad: true)`, admin → *Bejövő számlák* → "Teljes újraszinkron") re-requests everything and re-seats the watermark. It is manual and spends from the same budget, so it is a repair tool, not a guard.
**What would close this:** a check that does not depend on the watermark — comparing the mirror's invoice count for a settled past month against `/incominginvoice/teljesiteskelte/` for a sampled day in it, and reporting a mismatch.
@@ -0,0 +1,37 @@
# INNVOICE — TODO
> Companion to [`README.md`](README.md). Topic `INNV`, prefix `MGFBANKPLUG` → entry IDs `MGFBANKPLUG-INNV-T-<RAND>`.
> ID format, Status vocabulary, type codes and archival are **not restated here** — see [`TOPIC_CODES.md`](../../.github/TOPIC_CODES.md) (→ framework registry).
## Active entries
## MGFBANKPLUG-INNV-T-M6K3: Scheduled incremental sync for the incoming mirror
**Status:** Open · **Priority:** P2 · **Type:** T · **2026-08-26**
The mirror only refreshes when someone presses "Szinkronizálás" on admin → *Bejövő számlák*. The VAT report reads a mirror that may be days old; it states its age in the header, which makes the staleness visible but not smaller.
**Not on page load.** The id-less budget is 20 requests per hour and overrun degrades silently, so a sync per report open would exhaust it unpredictably and then serve incomplete data without saying so.
### Steps
- [ ] A nopCommerce `IScheduleTask` implementation calling `IncomingInvoiceSyncService.SyncAsync(false)`. **The plugin has none today** — this is the first, so registration on install and removal on uninstall are part of the work.
- [ ] Hourly period: 24 requests a day out of 480, and the report is never more than an hour behind.
- [ ] Failures reach the log with the counts the sync already produces (`FetchedCount`, `InsertedCount`, `UpdatedCount`, `SkippedCount`, `FailedCount`); a scheduled task that fails quietly is worse than the manual button.
---
## MGFBANKPLUG-INNV-T-B2N7: Switch the outbound direction from order submission to direct draft-invoice submission
**Status:** Open · **Priority:** P3 · **Type:** T · **2026-08-26**
The outbound path currently submits an **order** to InnVoice (`InnVoiceOrder/CreateOrder`). The intent is to submit the **invoice** directly, created in draft ("piszkozat") status.
The dead invoice JS in `Views/OrderAttributes.cshtml` is groundwork for this and is kept for that reason ([`MGFBANKPLUG-INNV-I-D4W9`](INNVOICE_ISSUES.md)).
### Steps
- [ ] Wire the existing invoice JS to real buttons and markup in the InnVoice card, replacing or complementing the order-submission buttons.
- [ ] Confirm `Invoice/CreateInvoice` creates the invoice in draft status.
- [ ] Decide the fate of the `InnVoiceOrder/CreateOrder` path — keep, deprecate or remove.
- [ ] Remove the page-load `GetInvoiceStatus` call if the final design does not need it.
@@ -0,0 +1,49 @@
/* =============================================================================
BEJÖVŐ SZÁMLÁK — csatolmány (bizonylatkép) hivatkozása
-----------------------------------------------------------------------------
Három oszlop a fbIncomingInvoice-ra. Idempotens: újrafuttatva nem csinál semmit.
🛡️ USE FruitBank_DEV;-re állítva. PROD-hoz írd át a USE-t a PROD adatbázis
nevére.
MIÉRT: az InnVoice a bejövő számla mellé adja a beszkennelt bizonylatot
(attachment/filename + url). A kimutatásból egy kattintással megnyithatóvá
tesszük — enélkül a szám mögé nézni külön rendszerbe kellene átmenni.
⚠️ CSAK AZ ELSŐ csatolmányt tároljuk, a darabszámmal együtt. Az API többet is
adhat, de a gyakorlatban egy PDF van számlánként; egy külön tábla ennyiért
nem áll meg. Ha egyszer valóban több kell, az AttachmentCount mutatja, hol
veszítünk információt — a mező azért van, hogy ez a veszteség LÁTHATÓ
legyen, ne néma.
⚠️ Az URL a bérlő-szintű hozzáférési tokent tartalmazza. Admin-only felületen
jelenítjük meg, és nem adjuk tovább.
Feltöltés: admin → Bejövő számlák → "Teljes újraszinkron" (a növekményes
lekérdezés csak a MÓDOSULT számlákat hozza).
============================================================================= */
USE FruitBank_DEV; -- PROD-hoz írd át a PROD adatbázis nevére!
SET NOCOUNT ON;
GO
IF COL_LENGTH('dbo.fbIncomingInvoice', 'AttachmentFileName') IS NULL
ALTER TABLE dbo.fbIncomingInvoice ADD AttachmentFileName nvarchar(400) NULL;
GO
IF COL_LENGTH('dbo.fbIncomingInvoice', 'AttachmentUrl') IS NULL
ALTER TABLE dbo.fbIncomingInvoice ADD AttachmentUrl nvarchar(1000) NULL;
GO
IF COL_LENGTH('dbo.fbIncomingInvoice', 'AttachmentCount') IS NULL
ALTER TABLE dbo.fbIncomingInvoice
ADD AttachmentCount int NOT NULL
CONSTRAINT DF_fbIncomingInvoice_AttachmentCount DEFAULT (0);
GO
/* Ellenőrzés — mindhárom oszlopnak léteznie kell (3). */
SELECT (CASE WHEN COL_LENGTH('dbo.fbIncomingInvoice', 'AttachmentFileName') IS NULL THEN 0 ELSE 1 END)
+ (CASE WHEN COL_LENGTH('dbo.fbIncomingInvoice', 'AttachmentUrl') IS NULL THEN 0 ELSE 1 END)
+ (CASE WHEN COL_LENGTH('dbo.fbIncomingInvoice', 'AttachmentCount') IS NULL THEN 0 ELSE 1 END)
AS AttachmentColumns;
GO
@@ -0,0 +1,49 @@
/* =============================================================================
BEJÖVŐ SZÁMLÁK — árfolyam oszlop
-----------------------------------------------------------------------------
Egy oszlop a fbIncomingInvoice-ra. Idempotens: újrafuttatva nem csinál semmit.
🛡️ USE FruitBank_DEV;-re állítva. PROD-hoz írd át a USE-t a PROD adatbázis
nevére.
MIÉRT: a devizás számlák áfáját forintosítani kell, különben a havi egyenleg
hiányos. Az InnVoice a számlán tárolja az árfolyamot (a felületén az
"Árfolyam (Ft)" mező, a "teljesítés napja" gombbal töltve) — a válaszban
Arfolyam néven jön, a dokumentációja nem említi.
⚠️ A DEFAULT 0 SZÁNDÉKOS, és NEM 1. A hiányzó árfolyamot 1-nek venni azt
jelentené, hogy egy eurós összeget forintnak olvasunk: a hiba mindig
ugyanabba az irányba visz, és nem tűnik hibának. A 0 = "nem ismert", és a
riport külön listán kéri be. (Ugyanez a lecke: MGFBANKPLUG-MARGIN-B-T4M6.)
A meglévő sorok feltöltése: admin → Bejövő számlák → "Teljes újraszinkron".
A növekményes lekérdezés csak a MÓDOSULT számlákat hozza, tehát a 6551 régi
sor magától nem kapná meg az értéket.
============================================================================= */
USE FruitBank_DEV; -- PROD-hoz írd át a PROD adatbázis nevére!
SET NOCOUNT ON;
GO
IF COL_LENGTH('dbo.fbIncomingInvoice', 'ExchangeRate') IS NULL
ALTER TABLE dbo.fbIncomingInvoice
ADD ExchangeRate decimal(18,6) NOT NULL
CONSTRAINT DF_fbIncomingInvoice_ExchangeRate DEFAULT (0);
GO
/* -----------------------------------------------------------------------------
Ellenőrzés — az oszlopnak léteznie kell (1), és látszik, hány devizás számla
vár még árfolyamra ÚGY, hogy az áfája nem nulla. Csak ezek torzítják a
forintos egyenleget: a 0%-os közösségi beszerzésnél az árfolyam a fizetendő
áfán semmit nem mozdít.
----------------------------------------------------------------------------- */
SELECT CASE WHEN COL_LENGTH('dbo.fbIncomingInvoice', 'ExchangeRate') IS NULL THEN 0 ELSE 1 END AS ExchangeRateColumnExists;
GO
IF COL_LENGTH('dbo.fbIncomingInvoice', 'ExchangeRate') IS NOT NULL
SELECT COUNT(*) AS NeedsExchangeRate
FROM dbo.fbIncomingInvoice
WHERE CurrencyCode NOT IN ('Ft', 'HUF')
AND VatAmount <> 0
AND ExchangeRate <= 0;
GO
@@ -0,0 +1,63 @@
# InnVoice integration
> Topic `INNV`, prefix `MGFBANKPLUG` — see [`TOPIC_CODES.md`](../../.github/TOPIC_CODES.md).
> Docs index: [`../README.md`](../README.md).
InnVoice is the external invoicing system. Two independent directions share one access:
| Direction | What moves | Entry point | Detail |
|---|---|---|---|
| **Outbound** | our orders / invoices → InnVoice | admin order edit page | this file, *Outbound* |
| **Incoming** | supplier invoices → our own mirror table | admin → *Bejövő számlák* | [`INNVOICE_INCOMING.md`](INNVOICE_INCOMING.md) |
## Files in this topic
| File | Content |
|---|---|
| `README.md` | this overview + the outbound direction |
| [`INNVOICE_INCOMING.md`](INNVOICE_INCOMING.md) | the supplier-invoice mirror and the monthly VAT report built on it |
| [`INNVOICE_ISSUES.md`](INNVOICE_ISSUES.md) | known issues |
| [`INNVOICE_TODO.md`](INNVOICE_TODO.md) | planned work |
| `INNVOICE_incoming_schema.sql` | runbook — creates the mirror tables |
| `INNVOICE_incoming_exchangerate.sql` | runbook — adds `ExchangeRate` to the mirror |
| `INNVOICE_incoming_attachment.sql` | runbook — adds the attachment columns to the mirror |
The three `.sql` files are runbooks: idempotent, re-runnable, `USE FruitBank_DEV` at the top — rewrite the `USE` for PROD.
## Access
Credentials live in `FruitBankSettings` (admin → Bővítmények → FruitBank plugin → Konfigurálás → InnVoice), never in source:
| Setting | Meaning |
|---|---|
| `InnVoiceCompanyName` | the `regnev` path segment |
| `InnVoiceUsername` / `InnVoicePassword` | HTTP Basic auth |
| `InnVoiceBaseUrl` | empty → `https://api.innvoice.hu` |
`InnVoiceCredentials.Require` reads them and **throws naming the missing setting** rather than returning an empty string: an empty username produces a 401, and a 401 reads as a network or password problem, not as an unconfigured plugin.
Request shape: `{baseUrl}/{companyName}/{resource}`.
## Quota
The provider caps requests, and **overrun is not an error** — the response simply carries less data. Both directions spend from the same budget:
- an id-less request: 20 per hour
- the full `incominginvoice` query: 10 per 30 days, after which it silently returns deltas only
Every design decision on the incoming side follows from that second line; see [`INNVOICE_INCOMING.md`](INNVOICE_INCOMING.md).
## Outbound — order submission
The admin **order edit** widget ([`Views/OrderAttributes.cshtml`](../../Views/OrderAttributes.cshtml), rendered by `OrderAttributesViewComponent` into `AdminWidgetZones.OrderDetailsBlock`) hosts the InnVoice card *"Megrendelés beküldése Innvoice-ba"*.
| UI | Endpoint |
|---|---|
| "Megrendelés beküldése" | `InnVoiceOrder/CreateOrder` |
| "Invoice adat ellenőrzése" | `InnVoiceOrder/GetOrderStatus` (also auto-run on page load) |
On success the card shows the InnVoice Order Table ID / Tech ID and a PDF link.
Server side: `InnVoiceApiService` (`Services/`), `InnVoiceOrderController` and `InvoiceController` (`Areas/Admin/Controllers/`).
`OrderAttributes.cshtml` also carries a full invoice-handling JS block with no matching markup — dead, deliberately kept, and it costs one failing request per order page load: [`MGFBANKPLUG-INNV-I-D4W9`](INNVOICE_ISSUES.md). The migration it is groundwork for: [`MGFBANKPLUG-INNV-T-B2N7`](INNVOICE_TODO.md).
+1 -1
View File
@@ -9,7 +9,6 @@ Topic documentation for the FruitBank-specific NopCommerce plugin (Layer 3 — c
- [`SCHEMA.md`](SCHEMA.md) — **Session-start mandatory read; read first for any domain question.** Generated, prose-free full type picture: every entity, DTO, enum, relationship.
- [`DOMAIN_MODEL.md`](DOMAIN_MODEL.md) — Business domain behavior
- [`MEASUREMENT.md`](MEASUREMENT.md) — Weight measurement logic (NetWeight formula, MeasuringStatus lifecycle)
- [`INNVOICE.md`](INNVOICE.md) — InnVoice integration: current order-submission flow, the orphaned invoice JS, and the planned migration to direct draft-invoice submission
- [`CONVENTIONS.md`](CONVENTIONS.md) — naming and XML documentation conventions
- [`PRODUCT_WEIGHT_DATA_DB_VALIDATION.md`](PRODUCT_WEIGHT_DATA_DB_VALIDATION.md) — product weight master-data validation (`AverageWeight`, `Tare`)
@@ -24,6 +23,7 @@ Topic documentation for the FruitBank-specific NopCommerce plugin (Layer 3 — c
- [`STOCK/`](STOCK/README.md) — stock movement: quantity + weight running balance, history and its consistency check (+ `STOCK_ISSUES.md`)
- [`MARGIN/`](MARGIN/README.md) — purchase cost and margin: cost layers, FIFO attribution at order completion, freight/extra distribution, and the report (+ `MARGIN_TODO.md` / `MARGIN_ISSUES.md`, and `MARGIN_schema.sql`)
- [`NOTIFICATION/`](NOTIFICATION/README.md) — customer e-mail notifications: the plugin's own message templates, the token-provider replacement, the `MeasuringStatus`-driven trigger and the repeat-guard flags (+ `NOTIFICATION_ISSUES.md`)
- [`INNVOICE/`](INNVOICE/README.md) — InnVoice integration, both directions: outbound order submission, and the mirrored supplier invoices that feed the monthly VAT report (+ `INNVOICE_INCOMING.md`, `INNVOICE_ISSUES.md` / `INNVOICE_TODO.md`, and three `.sql` runbooks)
- [`PMDATA/`](PMDATA/README.md) — product measurement/logistics master data (`IsMeasurable`, `Tare`, `AverageWeight`, `AverageWeightTreshold`, `CratesPerPallet`): storage, write paths, validation (+ `PMDATA_TODO.md`; the SQL runbook is the flat [`PRODUCT_WEIGHT_DATA_DB_VALIDATION.md`](PRODUCT_WEIGHT_DATA_DB_VALIDATION.md))
## Navigation