Files
gomag-vending/docs/relink-facturi-manuale.md
Claude Agent ccc6a933fa feat(scripts): line-level residual check in relink_manual_invoices
Linking the VANZARI header (ID_COMANDA) makes the app dashboard show an
order facturat, but ROA decides facturat at the LINE level
(PACK_FACTURARE.cursor_comanda matches invoiced qty on ID_ARTICOL + exact
PRET). When a manual invoice represented lines differently than the order
(e.g. per-VAT-rate discounts consolidated into one 0%-TVA line), the order
stays nefacturat in ROA despite the header link.

Add order_line_residual(): predicts the residual before --apply (via
extra_idv) and re-verifies after linking. Warns in the plan, the summary
counter, and post-apply when an order will still show nefacturat. The
script never touches COMENZI_ELEMENTE — those need a manual line fix.

Surfaced by orders 5419/5423 (web 492710430/492710513) on 2026-06-26.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 09:46:59 +00:00

134 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Reconciliere facturi manuale ROA ↔ comenzi GoMag
**Script:** [`scripts/relink_manual_invoices.py`](../scripts/relink_manual_invoices.py)
**Launcher Windows:** [`scripts/relink_manual_invoices.bat`](../scripts/relink_manual_invoices.bat)
## Cand se foloseste
Cand Oracle pool / sync-ul **a fost cazut** (ex. dupa o pana de curent la VENDING)
si operatorul a emis **facturi manual direct in ROA** in acel interval. Dupa ce
aplicatia revine, importeaza aceleasi comenzi web in `COMENZI`, **dar** factura
manuala nu se leaga de comanda → comanda ramane **nefacturata** in ROA si in
dashboard, desi factura exista.
Semnal: in dashboard apar comenzi „nefacturate" pentru clienti care au fost de
fapt facturati, iar in `VANZARI` exista facturi cu `ID_COMANDA IS NULL`.
## Cauza tehnica
| Flux | `VANZARI.ID_COMANDA` | `COMENZI.COMANDA_EXTERNA` |
|------|----------------------|----------------------------|
| Normal (app) | setat (leaga factura de comanda) | nr comanda GoMag |
| Manual (operator, in downtime) | **NULL** | — |
Dashboard-ul / cache-ul SQLite marcheaza o comanda „Facturat" doar daca exista
`VANZARI ... WHERE id_comanda = <comanda> AND sters = 0`
(vezi `invoice_service.check_invoices_for_orders`). Factura manuala cu
`ID_COMANDA NULL` nu e niciodata gasita → comanda apare nefacturata.
`ID_FACT` (documentul fiscal) si `COMENZI.COMANDA_EXTERNA` sunt deja completate;
**singura piesa lipsa e legatura `VANZARI → COMANDA`.**
## ⚠️ Facturi de depozit (walk-in) — NU se ating
Operatorul emite zilnic si **facturi manuale legitime din depozit** (~20+/zi),
fara nicio comanda online in spate. Acestea au tot `ID_COMANDA NULL`, dar **nu**
trebuie legate de nimic. De aceea matching-ul e **conservator**: orice nu e o
potrivire 1:1 sigura e raportat, niciodata legat automat.
## Cum potriveste
Pentru fiecare factura orfana (`VANZARI.ID_COMANDA NULL`, `sters=0`, in fereastra
`--days`) cauta o comanda GoMag nefacturata (in SQLite, cu `id_comanda` setat) cu:
1. **Total identic** (`TOTAL_CU_TVA``order_total`, toleranta 0.01 lei), apoi
2. **acelasi partener** (`ID_PART` = `id_partener`) **SAU**
3. **nume potrivit** (token-overlap, tolereaza SRL/SC si ordinea cuvintelor) —
acopera cazul in care operatorul a creat un **partener duplicat** la facturare.
Verifica si ca acea comanda e activa in ROA (`sters=0`) si **nu** are deja factura
(anti-dubla-facturare).
### Clasificare (in raport)
| Eticheta | Ce inseamna | Actiune |
|----------|-------------|---------|
| `LINK` | potrivire 1:1 sigura | leaga (la `--apply`) |
| `SKIP_NOMATCH` | nicio comanda online cu acel total | **factura de depozit — lasata neatinsa** |
| `SKIP_AMBIGUOUS` | mai multe comenzi plauzibile, sau total potrivit dar partener+nume diferit | raportat pentru verificare manuala |
| `SKIP_ALREADY` | comanda nu mai e activa / are deja factura | sarit |
La aplicare: `UPDATE VANZARI SET ID_COMANDA = <comanda>` + populeaza
`orders.factura_*` in SQLite, exact ca aplicatia (`update_order_invoice`).
### ⚠ Verificare reziduu de linie (legatura header nu e mereu suficienta)
Aplicatia / dashboard-ul marcheaza o comanda „Facturat" doar dupa **legatura header**
(`VANZARI.ID_COMANDA`). **ROA insa verifica la nivel de linie**: in
`PACK_FACTURARE.cursor_comanda`, cantitatea facturata se potriveste cu comanda pe
**`ID_ARTICOL` + `PRET` exact**, iar o linie e „de facturat" cand
`SIGN(CANTITATE) * (CANTITATE NVL(facturat,0)) > 0`.
Daca factura manuala a reprezentat liniile **altfel** decat comanda — tipic discounturi
comasate (ex. discounturile pe cote de TVA 11%/21% puse intr-o singura linie la 0% TVA) —
preturile nu se mai potrivesc, deci ROA arata comanda **tot nefacturata** desi headerul
e legat si dashboard-ul o vede facturata.
Scriptul **prezice** acest reziduu inainte de `--apply` (functia `order_line_residual`,
simuland factura ce urmeaza a fi legata) si il **re-verifica** dupa legare. Cand exista,
afiseaza `!! ATENTIE ...` cu liniile reziduale (ART / cantitate comanda / pret / facturat)
si un contor in rezumat. **Scriptul NU atinge `COMENZI_ELEMENTE`** — aceste cazuri se
corecteaza **manual in ROA** (aliniezi liniile comenzii la factura, ex. comasezi liniile
de discount ca in factura, pastrand valoarea totala).
## Utilizare
Ruleaza **pe serverul de productie VENDING** (are nevoie de Oracle prod +
`api/data/import.db` prod). Foloseste `app.config.settings` (deci `.env`-ul prod).
```bat
REM din C:\gomag-vending\scripts
relink_manual_invoices.bat REM dry-run, ultimele 3 zile (NU modifica)
relink_manual_invoices.bat --apply REM aplica, cu confirmare
relink_manual_invoices.bat --apply --yes REM aplica fara confirmare
relink_manual_invoices.bat --days 7 REM alta fereastra
```
Dublu-click pe `.bat` = dry-run. `.bat`-ul seteaza mediul Oracle thick-mode
(`TNS_ADMIN` + PATH instant client) ca `start.bat`.
Direct cu Python (echivalent):
```bat
set TNS_ADMIN=C:\roa\instantclient_11_2_0_2
set PATH=C:\app\Server\product\18.0.0\dbhomeXE\bin;%PATH%
C:\gomag-vending\venv\Scripts\python.exe scripts\relink_manual_invoices.py --apply
```
Din containerul de dev, peste SSH:
```bash
scp -P 22122 scripts/relink_manual_invoices.* gomag@79.119.86.134:C:/gomag-vending/scripts/
ssh -p 22122 gomag@79.119.86.134 'cmd /c "C:\gomag-vending\scripts\relink_manual_invoices.bat --days 3 < nul"'
```
**Workflow recomandat:** intai dry-run → verifica lista `LINK` si `SKIP_AMBIGUOUS`
→ apoi `--apply`. Cazurile `SKIP_AMBIGUOUS` se rezolva manual in ROA.
## Istoric
Codifica reconcilierea din **2026-06-26** (pana de curent la VENDING): pool cazut
~09:0710:25; 12 facturi manuale `TIP=1` (IDV 138191138203 → comenzi 54195430)
legate; 3 facturi de depozit corect excluse (CRISS VENDING, COFEE SEVEN TO GO,
PANDELE MIOARA); 2 parteneri duplicati semnalati (CERBU, MILITARU).
**Follow-up 2026-06-26 (reziduu de linie):** 2 din cele 12 comenzi (5419/web 492710430,
5423/web 492710513) au ramas nefacturate **in ROA** desi headerul era legat — factura
manuala comasase cele 2 linii de discount (ART 2077, split pe TVA 11%/21%) intr-una la
0% TVA, deci nu se potriveau pe `ID_ARTICOL+PRET`. Reparate manual prin alinierea
liniilor comenzii la factura (comasare in `COMENZI_ELEMENTE`, valoare discount pastrata).
De aici provine verificarea de reziduu de linie adaugata in script.
Vezi si: [oracle-schema-notes.md](oracle-schema-notes.md) (tabele `COMENZI`/`VANZARI`),
sectiunea „Facturi & Cache" din [README](../README.md).