Files
roagest/docs/propuneri_achizitie_import.md
Marius Mutu ca21bfe7a2 Docs: flux achizitie import, propuneri sesizare, mod de lucru Claude
- docs/propuneri_achizitie_import.md: modificarile propuse (cu cod) pentru
  sesizarea achizitie import (explicatie TVA prin cautare, valute/cursuri
  per document, conturi 2xx la articole, negestionabile fara RULAJE)
- docs/flux-achizitie-import.md: notita durabila cu lantul fluxului
- docs/CLAUDE.md: modul de lucru (propuneri pentru VFP IDE, export Oracle)
- .gitignore: docs/local/ (credentiale neversionate)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 23:18:03 +03:00

304 lines
17 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.

# Propuneri de modificare — Sesizare ACHIZIȚIE IMPORT
> Fișier de lucru (se șterge/arhivează după implementare). Modificările se aplică **în VFP IDE**
> (clasele sunt în binare `.vcx`); după aplicare se rulează `vcx2txt.ps1` pentru refresh cache.
>
> Context flux (detalii durabile în `docs/flux-achizitie-import.md`):
> `gestiuni.vc2:2045-2171` → `DO achizitie_import WITH 208/209/220/221, llIntern IN ointroduceri.prg`
> → `ointroduceri.prg:1435-1617` construiește `poAct` (din `ACT`/`actactan`), cursorul liniilor de notă
> `introdc`, cursorul `jtva_coloane2`, apoi `CREATEOBJECT('IMPORT_nota')` (`:1599`).
> `import_nota.inainte_de_do_termin` lansează `import_nir` (grila de articole → `rul_temp`), iar salvarea
> finală trece prin `oscrie_in_fisiere` → `PACK_CONTAFIN.SCRIE_IN_ACT/SCRIE_IN_RUL/SCRIE_IN_STOC`.
> **`import_nota_original` (ointroduceri.vc2:8269) NU e instanțiat nicăieri — e copie de rezervă, nu se modifică.**
> Liniile de mai jos se referă la cache-ul text `D:\ROA\UTIL\foxbin2prg\_textcache_roagest\comun\clase\ointroduceri.vc2`
> (stadiul de la 08.07.2026, cu implementarea ta începută la punctul 1 deja inclusă).
---
## 1. Explicație TVA prin formular de căutare (în loc de dropdown)
**Stadiu:** ai făcut deja în `import_nota`: `Column4 (cExplicatieTva).CurrentControl = "Text1"`,
ai adăugat `Text1` cu `DblClick`/`InteractiveChange``do_modifica_explicatie_tva` (`:7628-7697`),
copiat după `frm_modific2024.do_modifica_explicatie_tva` (`omodificari.vc2:12829`).
`Combo1` + `Combo1.Valid` rămân în clasă, dezactivate (decizie: păstrate ca fallback).
**Probleme găsite în versiunea ta actuală** (comparat cu originalul `Combo1.Valid`, `:7962-8027`,
a cărui logică trebuie păstrată identic):
| # | Problema | Detaliu |
|---|----------|---------|
| A | **`lnPtva = (locauta.cota_tva + 100) / 100` e greșit** | Formula vine din registru jurnal, unde câmpul e `proc_tva` (1.21). În `introdc.ptva`, originalul `Combo1.Valid` salvează **cota brută** (`lnPtva = jtva_coloane2.cota_Tva`, ex. 21). Cu formula ta: (a) `ptva` primește 1.21 în loc de 21 → calculele de TVA din `cSuma.Text1.Valid` ies greșite; (b) testul `If lnPtva = 0` pentru cota 0% nu mai e adevărat niciodată (0% → 1), deci ramura de golire a liniei de TVA nu se mai execută. |
| B | **`lnId_tva = locauta.id_tva` — proprietatea nu există** | `caut_explicatie_tva` (`ocautare.prg:1951`) selectează doar `id_jtva_coloana, denumire, cota_tva` din `vjtva_coloane`. Obiectul întors NU are `.id_tva` → eroare la runtime. `id_tva` trebuie citit din `jtva_coloane2` după `LOCATE` (cursorul e deja deschis în acest flux, `ointroduceri.prg:1586-1594`, și conține coloana `ID_TVA`). LOCATE-ul există deja la tine (`:7647-7648`) dar nu-i folosești rezultatul și nu verifici `FOUND()`. |
| C | **`lnTipEx = iif(introdc.scd='4428',2,1)` evaluat pe linia greșită** | Pe linia de bază (impară) `scd` e contul de marfă (3xx/2xx), niciodată 4428 → mereu `tlTipEx=1`. 4426/4428 e pe **linia pereche de TVA** (para). Trebuie să te uiți la linia următoare când ești pe linia de bază. Alternativă mai simplă: `introdc.tva_incasare` (moștenit din `poAct`, vezi `ointroduceri.prg:1558`) — `IIF(introdc.tva_incasare=1, 2, 1)`. |
| D | Guard pe anulare | `Store "" To loCauta` în `caut_explicatie_tva` + acces direct `.id_jtva_coloana` poate da eroare dacă utilizatorul închide căutarea. Adaugă `Vartype(loCauta) <> 'O'` în test. |
**Cod propus (înlocuiește integral `import_nota.do_modifica_explicatie_tva`):**
```foxpro
PROCEDURE do_modifica_explicatie_tva
*!* cautare explicatie TVA prin formular (model: frm_modific2024 din omodificari.vcx),
*!* pastrand integral logica originala din Grid1.cExplicatieTva.Combo1.Valid
*!* (linia de baza + linia pereche de TVA + comutarea 4427/401)
LOCAL lnId, lnPtva, lnId_tva, lnId_jtva_coloanaTVA, lnCota_tva, lcExplicatie_TVA
LOCAL lcExplicatieTVA, lnRec, lnTipEx, loCauta
Select introdc
lnRec = Recno()
* tipul exigibil(1)/neexigibil(2) se decide dupa contul liniei de TVA (linia para);
* pe linia de baza (impara) scd e contul de marfa, deci ma uit la linia urmatoare
If Mod(lnRec,2) <> 0 And lnRec < Reccount('introdc')
Skip
lnTipEx = Iif(Alltrim(scd) == '4428', 2, 1)
Go lnRec
Else
lnTipEx = Iif(Alltrim(scd) == '4428', 2, 1)
Endif
loCauta = caut_explicatie_tva(Nvl(introdc.id_jtva_coloana,-1),,,m.lnTipEx)
If Vartype(loCauta) <> 'O' Or Empty(Nvl(loCauta.id_jtva_coloana,0))
Return
Endif
* ATENTIE: in introdc.ptva se salveaza COTA BRUTA (ex. 21), ca in Combo1.Valid.
* NU (cota+100)/100 ca in registru jurnal - acolo campul e proc_tva, alta semantica.
lnId = loCauta.id_jtva_coloana
lnPtva = loCauta.cota_tva
lcExplicatieTVA = loCauta.denumire
* id_tva (perechea pentru linia de TVA) NU vine din caut_explicatie_tva
* (selectul intoarce doar id_jtva_coloana, denumire, cota_tva) - il iau din jtva_coloane2
Select jtva_coloane2
Locate For id_jtva_coloana = lnId
If !Found()
Return
Endif
lnId_tva = jtva_coloane2.id_tva
Select introdc
Replace id_jtva_coloana With lnId, ptva With lnPtva, explicatie_tva With lcExplicatieTVA
If Mod(lnRec,2)<>0 && linie impara BAZA
Thisform.UpdateExplicatieSAFT()
If lnPtva = 0
Select introdc
Replace cu_tva With 0
Skip
Replace id_jtva_coloana With 0, ptva With 0, explicatie_tva With '', cu_tva With 0
Else && linia para TVA
Select jtva_coloane2
Locate For id_jtva_coloana = lnId_tva
If Found()
lnId_jtva_coloanaTVA = jtva_coloane2.id_jtva_coloana
lnCota_tva = jtva_coloane2.cota_Tva
lcExplicatie_TVA = jtva_coloane2.denumire
* PENTRU TAXARE INVERSA SI COMUNITATEA EUROPEANA: 4426 = 4427 PE LINIA DE TVA, ALTFEL 4426 = 401
Select introdc
Replace cu_tva With 1
Skip
Replace id_jtva_coloana With lnId_jtva_coloanaTVA, ptva With lnCota_tva, ;
explicatie_tva With lcExplicatie_TVA, cu_tva With 1
If Seek(lnId_jtva_coloanaTVA, "cJtvaCol4427", "id_jtva")
Replace scc With '4427' In introdc
Else
Replace scc With '401' In introdc
Endif
Thisform.UpdateExplicatieSAFT()
Endif
Endif
Endif
Thisform.grid1.cSuma.teXT1.Valid
ENDPROC
```
**Recomandări suplimentare:**
- În `Grid1.cExplicatieTva.Text1.InteractiveChange`, după apel, adaugă
`This.Value = introdc.explicatie_tva` — altfel caracterul tastat (care a declanșat evenimentul)
rămâne în buffer-ul textbox-ului și poate suprascrie explicația la părăsirea celulei:
```foxpro
PROCEDURE Grid1.cExplicatieTva.Text1.InteractiveChange
Thisform.do_modifica_explicatie_tva()
This.Value = introdc.explicatie_tva
ENDPROC
```
- **De testat mapping-ul `tlTipEx`**: comentariile din `caut_explicatie_tva` (`ocautare.prg:1936-1942`)
spun 1=JC exigibil / 2=JC neexigibil, dar filtrele din `DO CASE` (`:1962-1979`) arată invers la prima
citire (`case 1` filtrează `id_jtva_neex is not null`). Testează cu un document normal și unul cu TVA
la încasare (4428) și, dacă lista afișată e cea greșită, inversează 1↔2 în `lnTipEx`.
- Testează explicit: cotă 21%, cotă 0% (scutit), taxare inversă/CE (trebuie `scc='4427'` pe linia de TVA),
TVA la încasare (4428), anulare din formularul de căutare, apel de pe linia de TVA (para) direct.
---
## 2. Valute și cursuri diferite pentru transport / alte taxe
**Modelul de documente al fluxului** (clarificat de Marius): un NIR de import cuprinde
- **factura principală de achiziție** (marfă și/sau imobilizări/alte articole) — valuta și cursul ei
sunt cele din antet: `poAct.id_valuta` / `poAct.Curs`; ea rămâne referința în care se convertește tot;
- opțional **factura de transport** — poate fi în altă valută, cu alt curs;
- opțional **factura de taxe vamale** (comisionar/vamă) — de regulă în lei, dar poate fi și în valută;
- **Declarația vamală de import (DVI)** — cu TVA-urile aferente fiecărei facturi (plătite în lei în vamă).
Toate ajung ca linii în același cursor `introdc`, iar apartenența unei linii la un document e DEJA
modelată per rând prin `nract` / `serie_act` / `id_fact` (`ointroduceri.prg:1554-1561`). Tot per rând
există deja și coloanele `id_valuta` și `curs` (moștenite din `poAct` la construire) — dar codul
folosește peste tot `poAct.Curs`/`Thisform.oact.Curs` în loc de valorile rândului. Deci nu trebuie
schimbată structura: (a) faci editabile valuta+cursul, logic per **document** (aceleași valori pe
toate liniile cu același `nract`); (b) folosești cursul rândului în calcule. La copierea în
`actactan` (→ `ACT_TEMP` → `ACT`), cursul per rând se salvează deja natural.
**Modificări propuse (toate în `import_nota`):**
**(a) UI — în VFP IDE:** adaugă în `Grid1` două coloane noi lângă `cSumaVal`:
- `cValuta` — `ControlSource = "introdc.id_valuta"`, cu un Text1 read-only care afișează codul valutei
și `DblClick` care deschide căutarea pe `NOM_VALUTE` (există `caut_valuta`/echivalent în `ocautare.prg`;
dacă nu, un mic `SELECT id_valuta, nume_val FROM nom_valute` + `cauta_alfa`). La selectare, propagă
pe **toate liniile aceluiași document** (același `nract`), inclusiv pe linia pereche de TVA:
```foxpro
LOCAL lnNract
lnNract = introdc.nract
Update introdc Set id_valuta = loCauta.id_valuta Where nract = m.lnNract
* cursul se cere/ia imediat dupa schimbarea valutei
```
- `cCurs` — `ControlSource = "introdc.curs"`, editabil numeric (4 zecimale), doar pe linii cu
`in_valuta = 1`; la `Valid`, propagă la fel pe liniile cu același `nract`. Opțional: dacă există
funcție de curs BNR pe dată (caută `getcurs`/`curs_valutar` în COMUN), propune automat cursul la
data documentului respectiv (`introdc.dataact`), nu la data facturii principale.
Liniile facturii principale rămân needitabile pe aceste coloane (păstrează `poAct.id_valuta`/`poAct.Curs`) —
doar liniile celorlalte documente (transport, taxe vamale) au voie curs propriu. Liniile de TVA din DVI
sunt în lei (`in_valuta = 0`) și rămân pe mecanismul existent (`suma`, fără curs propriu).
**(b) Conversia — formula generală.** Contribuția unei linii la baza în valuta facturii principale:
```
linia in valuta (in_valuta=1): baza_val += ROUND(suma_val * introdc.curs / poAct.Curs, gnPval)
linia in lei (in_valuta=0): baza_val += ROUND(suma / poAct.Curs, gnPval) && neschimbat
```
Formula cu `introdc.curs` e corectă și pentru liniile în valuta principală (curs identic ⇒ raport 1),
deci poate înlocui uniform sumările existente.
Locuri de modificat:
1. **`inainte_de_do_termin`** (`ointroduceri.vc2:7699+`), sumările de baze (`:7792-7806`):
```foxpro
* inainte: Sum suma_val For ... in_valuta = 1 And participa_valuta To lnBazaVal
* dupa: Sum Round(suma_val * curs / poAct.Curs, gnPval) For ... in_valuta = 1 And participa_valuta To lnBazaVal
```
(idem pentru `lnTvaVal`; ramurile `in_valuta = 0` cu `Round(suma/poAct.Curs, gnPval)` rămân neschimbate).
2. **Verificarea lei↔valută la editare** (`ointroduceri.vc2:8214-8221`, `cSumaVal`/`cSuma` Valid):
```foxpro
* inainte: lnSumaDinVal = Round(suma_val * Thisform.oact.Curs, gnPC)
* dupa: lnSumaDinVal = Round(suma_val * introdc.curs, gnPC)
```
3. **`do_executa`** (`:7560-7598`, calculul procentelor stocate în `explicatia4`/`explicatia5`) —
verifică fiecare loc unde apare `poAct.Curs`/`oact.Curs` și înlocuiește cu cursul rândului unde
se convertește o sumă **a rândului**; `poAct.Curs` rămâne doar unde se convertește **totalul**
în valuta principală.
4. **Copierea în `actactan`** (`:7737-7782`): rândurile în lei primesc deja `Curs=0, id_valuta=0`
(`:7754`) — neschimbat; rândurile în valută își duc acum automat cursul propriu (corect).
5. **Afișaj**: `lb_curs` (`:7882-7883`) rămâne cursul facturii principale; opțional adaugă în caption
„(liniile pot avea curs propriu)".
**Ce NU se schimbă:** repartizarea pe articole în `import_nir` (`:6276-6316`) — lucrează cu
`procent_lei`/`procent_val` derivate din bazele de mai sus și cu `ncurs = poAct.Curs` pentru prețul
în lei al articolelor; odată ce bazele sunt corecte, restul rămâne valid.
**Testare:** factură USD + transport EUR + taxă în lei; verifică: NIR-ul valoric (lei și valută),
linia „DIFERENTE" (`:6329-6364`) să rămână nesemnificativă, nota contabilă echilibrată
(`verificare_note_contabile`), jurnalul de cumpărări.
---
## 3. Conturi 2xx (nu doar 3xx) la căutarea articolelor
Filtrul e în **`import_nir`** (nu `import_nota`), în două locuri:
**(a) `do_adauga`** (butonul „Caută articol", `ointroduceri.vc2:5949`):
```foxpro
* inainte:
SELECT DISTINCT scd FROM actactan WHERE LEFT(ALLTRIM(scd),1) = '3' AND suma <> 0 INTO CURSOR tconturi
* dupa:
SELECT DISTINCT scd FROM actactan WHERE INLIST(LEFT(ALLTRIM(scd),1),'2','3') AND suma <> 0 INTO CURSOR tconturi
```
Lista rezultată intră în `lcFiltru_init = [inactiv = 0 and cont in (...)]` (`:5958`) transmis la
`viz_catalog_articole()` — nimic altceva de schimbat acolo.
**(b) `do_modiparam`** (preluarea din XLS, `ointroduceri.vc2:6177`): aceeași înlocuire.
Atenție la contul implicit `'371'` pentru articole noi (`:6215`, `:6230`) — rămâne valid ca default
al `Inputbox`-ului, utilizatorul poate tasta alt cont; nu necesită modificare, dar verifică fluxul.
**Opțional (de discutat cu contabilul):** în loc de tot `2xx`, restrânge la clasele relevante
(ex. exclude 28x amortizări, 29x ajustări): `... WHERE (LEFT(ALLTRIM(scd),1) = '3' OR
INLIST(LEFT(ALLTRIM(scd),2),'20','21','22','23','26','27')) AND suma <> 0`.
---
## 4. Articolele negestionabile 2xx să nu se salveze în RULAJE
**Constatări:**
- Câmpul din nomenclator: `nom_articole.in_stoc` (1 = „Gestionabil"), prezent în cursorul întors de
`viz_catalog_articole` (`oproceduri_articole.prg:91-107`) — deci disponibil fără query suplimentar.
- Azi NU există nicio filtrare: tot ce e bifat în catalog intră în cursorul NIR → `rul_temp` →
`PACK_CONTAFIN.SCRIE_IN_RUL` care face `INSERT INTO RUL (SELECT ... FROM RUL_TEMP)`
(PACK_CONTAFIN.pck:1896-2015). **Filtrarea client-side e suficientă** — pachetul scrie doar ce
primește în `RUL_TEMP`.
**Modificare propusă — `import_nir.do_adauga`, în `SCAN FOR bifa = 1` (`ointroduceri.vc2:5989`):**
```foxpro
LOCAL lcSarite
lcSarite = ''
SELECT (lcCrsArt)
SCAN FOR bifa = 1
SCATTER NAME loArt
* articolele negestionabile cu cont 2xx nu intra pe NIR/rulaje -
* valoarea lor ramane doar pe linia de nota contabila (contul 2xx e deja debitat)
IF NVL(loArt.in_stoc,1) = 0 AND LEFT(ALLTRIM(loArt.cont),1) = '2'
lcSarite = lcSarite + IIF(EMPTY(lcSarite),'',', ') + ALLTRIM(loArt.denumire)
LOOP
ENDIF
* ... restul SCAN-ului neschimbat ...
ENDSCAN
IF !EMPTY(lcSarite)
amessagebox("Urmatoarele articole sunt negestionabile (cont 2xx) si NU au fost adaugate pe NIR" + ;
" (valoarea ramane doar pe nota contabila): " + lcSarite, 0+64, "Atentie")
ENDIF
```
**Și în `do_modiparam` (XLS):** la căutarea articolului după codmat (`:6193`), adaugă `in_stoc` în
select (`select id_articol, denumire, in_stoc from nom_articole ...`) și aplică același skip + mesaj
înainte de `Append Blank` în `rul_temp` (`:6246-6249`).
**Atenții:**
- Dacă vrei regula pentru **orice** articol negestionabil (nu doar 2xx), scoate condiția pe cont —
sesizarea cere explicit doar 2xx, așa că am păstrat-o.
- Liniile de notă 2xx aferente acestor articole nu trebuie să participe la repartizarea valorii pe
articolele rămase: la introducerea notei, debifează `participa_valuta` (coloana `cParticipaValuta`)
pe acele linii, altfel valoarea lor umflă `procent_lei`/`procent_val` și deci prețul de intrare al
mărfurilor 3xx. De verificat operațional; se poate automatiza ulterior (warning în `do_executa`
când o linie 2xx are `participa_valuta = 1`).
- Plasă de siguranță opțională în `import_nir.inainte_de_do_termin`, înainte de `oscrie_in_fisiere`
(`:6424`): verifică `rul_temp` contra `nom_articole.in_stoc = 0` (un `SELECT` prin `goExecutor` pe
lista de `id_articol`) și șterge rândurile găsite sau avertizează. Utilă doar dacă apar și alte căi de umplere a grilei.
---
## Ordine recomandată de aplicare și testare
1. **Punctul 3** (2 linii) + **Punctul 4** (localizat) — testabile imediat pe un NIR de probă.
2. **Punctul 1** — corectează cele 4 probleme (AD) din implementarea ta; testele de la secțiunea 1.
3. **Punctul 2** — cel mai amplu; întâi calculele (b), apoi UI (a); testele valutare de la secțiunea 2.
După fiecare rundă de editare în IDE: `Build` + rulare + `vcx2txt.ps1` pentru refresh cache, ca să pot
verifica codul aplicat. La final: intrare în `changelog_roagest.txt` (bump versiune, tag `:modificare:`/`:nou:`).