docs: compactare depanare/testare VFP (dedup + eliminare cazuri specifice)
depanare_testare_vfp.md 226->118 linii, testare-ui-vfp.md 133->90. - regula SAFETY/dialog modal si -A -T erau enuntate de 3 ori -> o data - cele 3 capcane "proces agatat" -> un singur algoritm de diagnostic in 3 pasi - studiul de caz CNRCRT si detaliile ROAGEST (jtva_coloane2, achizitie_import) reduse la regula generala + pointer catre scriptul de test - reguli_lucru pct.4 ramane doar pointer, fara detaliile duplicate de mediu - sters docs/bash.exe.stackdump (fisier de gunoi, negitat) Masuratorile si datele concrete pastrate integral. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqJ7D5ftMbChDyz5mpVkCd
This commit is contained in:
@@ -1,132 +1,95 @@
|
||||
# Testare UI VFP headless cu harness + screenshots
|
||||
|
||||
Harness reutilizabil pentru teste care afiseaza un formular REAL (fara Oracle/UI complet),
|
||||
executa actiuni si valideaza vizual prin capturi de ecran. Valabil pentru toate proiectele
|
||||
VFP din suita ROA; exemplele sunt pe ROAGEST. Alternativa la
|
||||
`testare-vfp-mcp.md` (care ruleaza programul principal intreg + windows-mcp): aici instantiem o
|
||||
singura clasa cu dependinte dummy, deci pornire rapida si izolata.
|
||||
Harness reutilizabil pentru teste care afiseaza un formular REAL (cu dependinte dummy, fara
|
||||
Oracle), executa actiuni si valideaza vizual prin capturi. Valabil pentru toate proiectele VFP
|
||||
din suita ROA. Alternativa la `testare-vfp-mcp.md` (care porneste programul principal intreg);
|
||||
capcanele generale de rulare headless (`-A -T`, SAFETY, script agatat) sunt in
|
||||
`depanare_testare_vfp.md`.
|
||||
|
||||
Fisiere in `COMUN\utile\Teste\`: `vfp_ui_harness.ps1` (orchestrator generic, parametrizat),
|
||||
`_precompile.ps1` (precompilare izolata), `ui_harness.prg` (proceduri VFP de handshake),
|
||||
`mock_amessagebox.prg` (mock dialog) — infrastructura sta in radacina `Teste\`; suitele de
|
||||
teste stau grupate pe subiect in subfoldere (ex. `achizitie_import\`). Exemplu complet:
|
||||
`achizitie_import\test_import_nota_ui.prg` + `.ps1` (wrapper).
|
||||
`_precompile.ps1` (precompilare izolata), `ui_harness.prg` (proceduri de handshake),
|
||||
`mock_amessagebox.prg` (mock dialog) — infrastructura sta in radacina `Teste\`; suitele stau in
|
||||
subfoldere pe subiect. Exemplu complet: `achizitie_import\test_import_nota_ui.prg` + `.ps1`.
|
||||
|
||||
**REGULA (Marius, 17/07/2026): testele pe aplicatii frontend exerseaza fluxul REAL al
|
||||
utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia (ex. "Adauga repere",
|
||||
do_adauga_factura prin dialogul real), NU introducerea directa a randurilor in cursoare/tabele
|
||||
(ex. Append in rul_temp/introdc). Popularea directa sare peste validari/filtre/efecte laterale
|
||||
si da PASS fals (caz real: articol cont 212 "adaugat" direct in rul_temp trecea testul, dar prin
|
||||
fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle,
|
||||
utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia, NU introducerea
|
||||
directa a randurilor in cursoare/tabele. Popularea directa sare peste validari/filtre/efecte
|
||||
laterale si da PASS fals (caz real: un articol "adaugat" direct in cursor trecea testul, dar
|
||||
prin fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle,
|
||||
dialoguri modale), nu pe pasii fluxului testat.
|
||||
|
||||
## Test UI nou in 5 pasi
|
||||
|
||||
1. `.prg` de test: `PUBLIC gcUILog, gcSyncDir`; `SET TALK OFF` (obligatoriu, vezi cap. c);
|
||||
`SET PATH/CLASSLIB/PROCEDURE` ca in aplicatie; incarca ULTIMELE
|
||||
`SET PROCEDURE TO ...\ui_harness.prg ADDITIVE` si (daca ai dialoguri)
|
||||
`...\mock_amessagebox.prg ADDITIVE`.
|
||||
2. Creeaza dummy-urile minime (`goExecutor`, `goApp`, etc.) + cursoarele pe care le cere
|
||||
Init-ul clasei (RecordSource-urile grilelor).
|
||||
3. Instantiaza clasa, `WindowType=0`, `Show()`, `WindowState=2`.
|
||||
4. Pe fiecare pas: fa actiunea, verifica efectul in date (loghezi PASS/FAIL cu un helper
|
||||
local), apoi `DO HarnessStep WITH <n>, '<mesaj>'` (scrie ready_n, asteapta cont_n de la
|
||||
orchestrator sau auto-continua dupa ~30s). La final `DO HarnessDone WITH 'done'`.
|
||||
5. Ruleaza: `powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...)`.
|
||||
PNG-urile ies in `screenshots\step_<n>_<eticheta>.png`; log in `<test>_log.txt`.
|
||||
1. `.prg` de test: `PUBLIC gcUILog, gcSyncDir`; `SET SAFETY OFF` + `SET TALK OFF` ca PRIMA
|
||||
linie (vezi capcana a); `SET PATH/CLASSLIB/PROCEDURE` ca in aplicatie; incarca ULTIMELE
|
||||
`ui_harness.prg` si (daca ai dialoguri) `mock_amessagebox.prg`, cu `ADDITIVE`.
|
||||
2. Creeaza dummy-urile minime (`goExecutor` cu metode reale, `goApp` pt. `ReadIni`, `poAct`
|
||||
pt. curs valutar) + cursoarele cerute de Init (RecordSource-urile grilelor).
|
||||
3. Instantiaza clasa, `WindowType=0` (modeless - `Show()` pe modal nu returneaza), `Show()`,
|
||||
`WindowState=2`.
|
||||
4. Pe fiecare pas: fa actiunea, verifica efectul in date (PASS/FAIL cu helper local), apoi
|
||||
`DO HarnessStep WITH <n>, '<mesaj>'` (scrie ready_n, asteapta cont_n de la orchestrator sau
|
||||
auto-continua dupa ~30s). La final `DO HarnessDone WITH 'done'`.
|
||||
5. `powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...)`.
|
||||
PNG-uri in `screenshots\step_<n>_<eticheta>.png`; log in `<test>_log.txt`.
|
||||
|
||||
## Capcane / deblocari (toate confirmate in sesiune)
|
||||
|
||||
a. **Lansarea orchestratorului**: prin tool cu timeout implicit (~120s) e omorat inainte de
|
||||
finalul pasilor. Ruleaza-l in background (run_in_background) + asteapta `done.txt`, sau
|
||||
timeout mare (300-600s). NU porni VFP separat de orchestrator - si-l lanseaza singur.
|
||||
b. **Asteptarea ready_0 prea scurta** omoara VFP exact inainte sa scrie semaforul. Cu `.fxp`
|
||||
cald START apare in ~2s, la rece mult mai mult (zeci de classlib-uri). Foloseste
|
||||
`-ReadyTimeoutSec` generos (implicit 180s). Retry NUMAI la "start ratat", nu la "start lent".
|
||||
c. **`SET SAFETY OFF` + `SET TALK OFF` ca PRIMUL lucru, inainte de primul `STRTOFILE`.**
|
||||
Cu SAFETY ON, prima scriere a log-ului (daca fisierul exista deja) scoate dialogul modal
|
||||
"...already exists, overwrite it?" care BLOCHEAZA rularea headless (VFP pare "instanta moarta"
|
||||
- proces viu, blocat pe dialog). Cu TALK ON, `SUM`/`CALCULATE` (ex. din `recalculeaza`) isi
|
||||
echo-eaza rezultatele pe desktop, ca text parazit peste formular in screenshots. Harness-ul
|
||||
are `HarnessInit`/`SET SAFETY OFF` defensiv in procedurile de scriere, dar testul trebuie
|
||||
sa faca `SET SAFETY OFF` oricum INAINTE de scrierea lui proprie de "START".
|
||||
d. **`vfp9 -A test.prg` deschide INTERMITENT editorul** in loc sa ruleze (log ramane gol).
|
||||
Lanseaza `.fxp`-ul PRECOMPILAT (`vfp9 -A test.fxp`) - se executa mereu.
|
||||
e. **Precompilarea (`COMPILE`+`QUIT`) atarna** dupa COMPILE; rulata in ACEEASI sesiune
|
||||
powershell care lanseaza apoi testul, otraveste lansarile (nu executa). Ruleaza precompilarea
|
||||
intr-un proces copil izolat (`Start-Process -Wait _precompile.ps1`).
|
||||
f. **Detectia "a pornit" pe mtime-ul log-ului > momentul lansarii**, nu pe "log ne-gol": un log
|
||||
stale tinut deschis de un `vfp9` zombi pacaleste verificarea. Omoara zombii inainte de fiecare
|
||||
lansare.
|
||||
g. **Formulare modale** (`WindowType`) - `Show()` nu returneaza si blocheaza testul. Seteaza
|
||||
`goObj.WindowType = 0` (modeless) pentru afisare non-modala automatizata.
|
||||
h. **Dummy minimali**: `goExecutor` (oExecute/oExecuta creeaza cursorul cerut), `goApp`
|
||||
(`ReadIni` pt. font din settings.ini), `poAct`/`oact` (curs valutar). Init-ul recreeaza
|
||||
cursoarele goale din Oracle daca nu-s pre-populate - pre-populeaza-le INAINTE de instantiere.
|
||||
i. **`LOCATE FOR camp == 'literal'` pe camp `C(n)` padded cu spatii nu gaseste nimic** (`==` e
|
||||
exact) - foloseste `ALLTRIM(camp) == 'literal'`.
|
||||
j. **Messagebox-uri de confirmare blocheaza testul headless**. Mock: `FUNCTION amessagebox`
|
||||
care returneaza direct valoarea butonului (6=Da). Incarca-l cu `SET PROCEDURE ... ADDITIVE`
|
||||
**PRIMUL** (inaintea `oproceduri_comune.prg`): la nume duplicat de procedura, VFP foloseste
|
||||
fisierul cautat PRIMUL in lista (verificat empiric: incarcat ultimul PIERDE, primul CASTIGA).
|
||||
Mock-ul umbreste `amessagebox` si din apelurile din metodele de clasa (ex. `do_sterge_articol`).
|
||||
k. **Cursor bound la o grila, recreat de Init -> grila pierde coloanele (eroare 1925).** Ex.:
|
||||
`import_nota.Init` recreeaza `rul_temp` din Oracle daca e gol la instantiere; GridArt ramane
|
||||
fara coloanele denumite. Fix: pune 1 rand placeholder in cursor INAINTE de instantiere (ca Init
|
||||
sa nu-l recreeze), apoi **`ZAP`** dupa `Show()` (NU `DELETE ALL`: acesta lasa un rand fizic
|
||||
sters si pointerul la EOF, iar cod gen `do_calculeaza_diferente` face `GO recno_EOF` -> eroare 5).
|
||||
l. **CREATEOBJECT/ADDPROPERTY se comporta gresit apelate DINTR-O PROCEDURA in acest runtime headless**
|
||||
(obiect returnat ca string de 30 spatii; `ADDPROPERTY` da eroare 11). Din programul PRINCIPAL
|
||||
merg. Construieste obiectele (ex. `toDlg` pt. `do_adauga_factura`) INLINE in main, nu intr-un
|
||||
helper. (Cauza exacta neclara; reproductibil.)
|
||||
m. **`gl406=.T.` (SAFT) activeaza cod care are nevoie de mock-uri suplimentare**, altfel crapa sau
|
||||
taxcode ramane mereu NULL: `GetTaxCode`/`GetTaxCodeIdPart` (oproceduri_comune.prg) apeleaza
|
||||
`update_jtva_coloane()` care ruleaza `goExecutor.oExecute("select * from vjtva_coloane ...", <cursor>)`
|
||||
- mock-ul de `oExecute` trebuie sa intercepteze `'VJTVA_COLOANE' $ UPPER(tcSql)` si sa returneze
|
||||
un cursor cu structura reala a view-ului (`id_jtva_coloana, denumire, jc, jv, coloana_jc,
|
||||
coloana_jv, cota_tva, id_tva, taxcode, taxcode_neexigibil, taxcode_n50, taxcode_n50_tvai,
|
||||
taxcode_n100, taxcode_regularizare, taxcode_tvai, afisat, ordine`) - codul apelant face singur
|
||||
`INDEX ON id_jtva_coloana TAG id_jtva` dupa, nu trebuie indexat in mock. Cand `id_partc`/`id_partd`
|
||||
pe rand nu e gol, `GetTaxCodeIdPart` cauta si codul fiscal al partenerului prin
|
||||
`goExecutor.oSelect2Value(sql, @toValue)` (`GetCodFiscalPartenerById`) - metoda lipseste din
|
||||
dummyexecutor-ul de baza, trebuie adaugata (returneaza cod fiscal gol -> `VERIFICA_RTVAI` sare
|
||||
peste apelul Oracle real de validare CIF).
|
||||
n. **Explicatia TVA de pe randul T ("in tandem" cu baza) NU se scrie prin cod separat** - e pur si
|
||||
simplu o A DOUA intrare in `jtva_coloane2`/`vjtva_coloane` (coloana `id_tva` de pe randul bazei
|
||||
punctand spre `id_jtva_coloana` al randului TVA); `aplica_explicatie_tva` (ointroduceri.prg
|
||||
~9807) citeste acea a doua intrare si o scrie pe rand T. Populeaza mock-ul cu ambele randuri
|
||||
(baza + TVA), nu doar cu unul si nu concatena manual "TVA " + denumirea bazei in test.
|
||||
o. **FARA FURT DE FOCUS (17/07/2026)**: `Graphics.CopyFromScreen` (captura ecranului intreg) fura
|
||||
focus si se corupe daca utilizatorul lucreaza in paralel - NU se mai foloseste. Capturile se fac
|
||||
cu `PrintWindow` (user32, P/Invoke) pe HANDLE-ul ferestrei vfp9 (`Process.MainWindowHandle`),
|
||||
flag `2` = `PW_RENDERFULLCONTENT` (fallback flag `0` daca iese goala); merge cu fereastra
|
||||
acoperita/in fundal, dar NU minimizata. Fereastra e mutata OFF-SCREEN (`SetWindowPos` cu
|
||||
`HWND_BOTTOM`, x=-4000, `SWP_NOACTIVATE`) imediat ce apare handle-ul si RE-IMPINSA la fiecare
|
||||
pas (VFP isi reactiveaza singur fereastra la `Show()`/dialoguri modale - o singura impingere la
|
||||
lansare nu e suficienta). Toate consolele powershell copil (precompilare) sunt `-WindowStyle
|
||||
Hidden` (sigur pentru consola; NU si pentru GUI VFP, care trebuie doar off-screen, nu ascuns -
|
||||
PrintWindow pe fereastra minimizata/ascunsa poate reda gol). Implementat in
|
||||
a. **`SET SAFETY OFF` + `SET TALK OFF` inaintea primului `STRTOFILE`.** Cu SAFETY ON, scrierea
|
||||
log-ului existent scoate un dialog modal care blocheaza rularea (proces viu, "instanta
|
||||
moarta"). Cu TALK ON, `SUM`/`CALCULATE` isi echo-eaza rezultatele peste formular in
|
||||
screenshots.
|
||||
b. **Lansarea orchestratorului**: prin tool cu timeout implicit (~120s) e omorat inainte de
|
||||
final. Ruleaza-l in background + asteapta `done.txt`, sau timeout 300-600s. NU porni VFP
|
||||
separat de orchestrator - si-l lanseaza singur.
|
||||
c. **Asteptarea `ready_0`**: cu `.fxp` cald START apare in ~2s, la rece mult mai mult (zeci de
|
||||
classlib-uri). `-ReadyTimeoutSec` generos (implicit 180s); retry NUMAI la "start ratat",
|
||||
nu la "start lent". Detectia "a pornit" se face pe mtime-ul log-ului > momentul lansarii
|
||||
(un log stale tinut de un vfp9 zombi pacaleste verificarea "log ne-gol") - omoara zombii
|
||||
inainte de fiecare lansare.
|
||||
d. **`vfp9 -A test.prg` deschide INTERMITENT editorul** in loc sa ruleze (log gol). Lanseaza
|
||||
`.fxp`-ul PRECOMPILAT. Precompilarea (`COMPILE`+`QUIT`) atarna dupa COMPILE si, rulata in
|
||||
ACEEASI sesiune powershell, otraveste lansarile ulterioare - ruleaza-o intr-un proces copil
|
||||
izolat (`Start-Process -Wait _precompile.ps1`).
|
||||
e. **Suitele NU se ruleaza in paralel**: `_precompile.ps1` face `Get-Process vfp9 | Stop-Process
|
||||
-Force` (omoara toate instantele), deci un test lansat in paralel il ucide pe cel in curs.
|
||||
f. **FARA FURT DE FOCUS (17/07/2026)**: `Graphics.CopyFromScreen` fura focus si se corupe daca
|
||||
utilizatorul lucreaza in paralel - nu se mai foloseste. Capturile se fac cu `PrintWindow`
|
||||
(user32, P/Invoke) pe `Process.MainWindowHandle`, flag `2` = `PW_RENDERFULLCONTENT`
|
||||
(fallback `0` daca iese goala); merge cu fereastra acoperita, dar NU minimizata. Fereastra
|
||||
se muta OFF-SCREEN (`SetWindowPos`, `HWND_BOTTOM`, x=-4000, `SWP_NOACTIVATE`) imediat ce
|
||||
apare handle-ul si SE RE-IMPINGE la fiecare pas (VFP se reactiveaza singur la
|
||||
`Show()`/dialoguri). Consolele powershell copil sunt `-WindowStyle Hidden` (sigur pentru
|
||||
consola, NU pentru GUI VFP - ascunsa, `PrintWindow` poate reda gol). Implementat in
|
||||
`vfp_ui_harness.ps1` (Take-Screenshot, Push-Offscreen, Wait-MainWindowHandle) si
|
||||
`_precompile.ps1` (acelasi Push-Offscreen pe fereastra tranzitorie de compilare).
|
||||
p. **Cursorul `introdc` FARA indexul `ord_doc` schimba documentul "principal" la sincronizari
|
||||
repetate** (sincronizeaza() apelat de mai multe ori, ex. la adaugarea a 2+ articole succesiv
|
||||
prin fluxul real): identificarea documentului principal (`Go Top` + `Locate For
|
||||
Inlist(tip_rand,'B','S') And !Empty(doc_key)`) e stabila in productie doar pentru ca
|
||||
`achizitie_import` (COMUN\programe\ointroduceri.prg:1616-1618) creeaza, INAINTE de
|
||||
`CREATEOBJECT('import_nota')`, indexul `ord_doc` (`Str(Nvl(nr_doc,0),8) +
|
||||
Icase(tip_rand='B','1', tip_rand='S','2', tip_rand='T','3', tip_rand='D','4', '5') +
|
||||
Padr(Nvl(scd,''),10)`) + `SET ORDER TO ord_doc`, care ordoneaza dupa `nr_doc` (S-urile
|
||||
principalei mostenesc nr_doc-ul ei) - documentul cu `nr_doc` minim ramane mereu acelasi. Un
|
||||
test care creeaza `introdc` fara acest index ramane pe ordinea FIZICA (recno): dupa ce
|
||||
principala e sparta (randul B sters, randurile S adaugate la coada), urmatorul apel de
|
||||
sincronizeaza() gaseste gresit ca "principal" documentul cu cel mai mic recno ramas si il
|
||||
sparge si pe el - cascada se repeta la fiecare apel ulterior. Fix: dupa popularea `introdc`,
|
||||
INAINTE de `CREATEOBJECT('import_nota', ...)`, adauga exact acelasi `INDEX ON ... TAG ord_doc`
|
||||
+ `SET ORDER TO ord_doc` ca in `achizitie_import`.
|
||||
q. **Suitele de teste NU se ruleaza in paralel**: `_precompile.ps1` face `Get-Process vfp9 |
|
||||
Stop-Process -Force` (omoara TOATE instantele vfp9 de pe masina), deci un test lansat in
|
||||
paralel ucide testul aflat in curs si corupe rezultatele. Ruleaza suitele strict secvential.
|
||||
r. **Fara SELECT-SQL pe cursorul legat de grid**: SELECT ... FROM introdc INTO CURSOR intr-un
|
||||
test, cat timp introdc e RecordSource-ul unui grid viu, poate omori procesul vfp9 silentios
|
||||
(fara eroare catchabila, fara semafor). Foloseste xBase nativ (COUNT FOR ... TO,
|
||||
CALCULATE, LOCATE) care nu comuta zona de lucru a grid-ului.
|
||||
`_precompile.ps1`.
|
||||
g. **Mock de messagebox**: `FUNCTION amessagebox` care returneaza direct valoarea butonului
|
||||
(6=Da), incarcat cu `SET PROCEDURE ... ADDITIVE` **PRIMUL**, inaintea fisierelor aplicatiei:
|
||||
la nume duplicat de procedura VFP foloseste fisierul cautat PRIMUL (verificat empiric).
|
||||
Astfel umbreste `amessagebox` si in apelurile din metodele de clasa.
|
||||
h. **Cursorii ceruti de Init**: pre-populeaza-i INAINTE de instantiere (Init-ul recreeaza din
|
||||
Oracle cursoarele goale, iar recrearea lasa grid-ul fara coloane, eroare 1925). Pune 1 rand
|
||||
placeholder inainte, apoi **`ZAP`** dupa `Show()` — NU `DELETE ALL`, care lasa un rand fizic
|
||||
sters si pointerul la EOF (cod care face `GO <recno>` da eroare 5, vezi
|
||||
`conventie_go_recno.md`).
|
||||
i. **Cursorii trebuie sa aiba EXACT indecsii creati de fluxul real** inainte de instantiere
|
||||
(fluxul real ii creeaza in metoda apelanta, ex. un `INDEX ON ... TAG ord_doc` +
|
||||
`SET ORDER TO` inainte de `CREATEOBJECT`). Fara ei cursorul ramane pe ordinea FIZICA, iar
|
||||
logica de tip `GO TOP` + `LOCATE FOR ...` alege alt rand decat in productie - rezultat fals,
|
||||
uneori in cascada la apeluri repetate. Verifica in codul apelant ce indecsi se creeaza si
|
||||
replica-i identic.
|
||||
j. **Mock-urile de date trebuie sa reproduca structura si semantica REALA a sursei** (view
|
||||
Oracle: toate coloanele; relatii intre randuri: toate randurile implicate, nu unul singur cu
|
||||
valori "compuse" manual in test). Un mock incomplet lasa campuri mereu NULL sau ascunde
|
||||
pasul care se testeaza. Cand un flag global activeaza cod suplimentar (ex. `gl406`), acel
|
||||
cod cere mock-uri in plus - verifica ce apeleaza si adauga metodele lipsa din dummy.
|
||||
k. **`LOCATE FOR camp == 'literal'` pe camp `C(n)` padded nu gaseste nimic** (`==` e exact) -
|
||||
foloseste `ALLTRIM(camp) == 'literal'`.
|
||||
l. **`CREATEOBJECT`/`ADDPROPERTY` se comporta gresit apelate DINTR-O PROCEDURA in acest runtime
|
||||
headless** (obiect returnat ca string de 30 spatii; `ADDPROPERTY` da eroare 11). Din
|
||||
programul PRINCIPAL merg. Construieste obiectele-parametru INLINE in main, nu intr-un helper.
|
||||
(Cauza neclara; reproductibil.)
|
||||
m. **Fara `SELECT-SQL` pe cursorul legat de grid**: `SELECT ... FROM <cursor> INTO CURSOR` cat
|
||||
timp cursorul e RecordSource-ul unui grid viu poate omori procesul vfp9 silentios (fara
|
||||
eroare catchabila, fara semafor). Foloseste xBase nativ (`COUNT FOR ... TO`, `CALCULATE`,
|
||||
`LOCATE`), care nu comuta zona de lucru a grid-ului.
|
||||
|
||||
Reference in New Issue
Block a user