sync SVN
This commit is contained in:
@@ -32,6 +32,7 @@ atingi zona X, citeste fisierul Y"). Restul se deschid doar la declansator.
|
||||
| `testare-ui-vfp.md` | test cu formular real + screenshots (harness UI) |
|
||||
| **Oracle** | |
|
||||
| `conventie_mediu_oracle.md` | te conectezi la dev/test, alegi schema, rulezi un script de pachet |
|
||||
| `conexiuni-tunel-ssh-odbc.md` | ajungi la un Oracle de PRODUCTIE prin tunel SSH (Bitvise) + alias TNS + DSN ODBC |
|
||||
| `scripturi-migrare-db.md` | modifici schema (tabele/view-uri/pachete); compatibilitate 10.2 |
|
||||
| `publicare-scripturi-db.md` | publici scripturile catre clienti |
|
||||
| `oracle_export.md` | reexporti sursa unui pachet PL/SQL |
|
||||
|
||||
174
docs/conexiuni-tunel-ssh-odbc.md
Normal file
174
docs/conexiuni-tunel-ssh-odbc.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# Conexiuni la bazele de productie: tunel SSH (Bitvise) + ODBC/TNS
|
||||
|
||||
Cum se ajunge, de pe statia de lucru, la un Oracle care nu e expus direct in internet. Valabil
|
||||
pentru toate proiectele ROA. Scopul documentului: sa nu se mai piarda o sesiune intreaga
|
||||
redescoperind lantul.
|
||||
|
||||
> **Fara credentiale in git.** Acest fisier NU contine parole, si nici un fisier din repo nu
|
||||
> trebuie sa contina. Parolele stau in profilul Bitvise (criptate cu DPAPI, legate de contul
|
||||
> Windows) si in capul celui care lucreaza. Daca ai nevoie de o parola pe care nu o ai, cere-o -
|
||||
> nu o cauta prin fisiere si **nu o ghici**: Oracle poate avea `FAILED_LOGIN_ATTEMPTS` in profil,
|
||||
> iar cateva incercari gresite blocheaza contul de schema in PRODUCTIE.
|
||||
|
||||
## Lantul, pe scurt
|
||||
|
||||
```
|
||||
statie -> [tunel SSH Bitvise] -> 127.0.0.1:1521 -> Oracle de pe serverul de productie
|
||||
^
|
||||
aliasul TNS din instantclient\tnsnames.ora arata AICI
|
||||
^
|
||||
DSN-ul ODBC cu acelasi nume e ce primeste goConn.Connect()
|
||||
```
|
||||
|
||||
Trei nume trebuie sa se potriveasca, si e usor de ratat: **profilul Bitvise** (ce port local
|
||||
deschide), **aliasul TNS** (ce host:port foloseste) si **DSN-ul ODBC** (ce alias TNS foloseste).
|
||||
|
||||
## 1. Tunelul SSH
|
||||
|
||||
Profilele Bitvise (`.tlp`) stau in `D:\GoogleDrive\` (ex. `vending.tlp`). Un profil contine
|
||||
gazda, portul, utilizatorul, parola criptata, cheile de gazda deja acceptate si **regulile de
|
||||
port forwarding** - de obicei `127.0.0.1:1521 -> localhost:1521`.
|
||||
|
||||
Ridicare din linie de comanda, fara interfata grafica:
|
||||
|
||||
```powershell
|
||||
$exe = 'C:\Program Files (x86)\Bitvise SSH Client\stnlc.exe'
|
||||
Start-Process $exe -ArgumentList '-profile=D:\GoogleDrive\vending.tlp' -WindowStyle Hidden `
|
||||
-RedirectStandardOutput "$env:TEMP\stnlc_out.txt" -RedirectStandardError "$env:TEMP\stnlc_err.txt"
|
||||
```
|
||||
|
||||
`stnlc` foloseste singur parola stocata in profil - **nu o trece pe linia de comanda**, ar ajunge
|
||||
in istoricul shell-ului. Ruleaza sub acelasi cont Windows sub care a fost salvat profilul,altfel
|
||||
DPAPI nu poate decripta parola.
|
||||
|
||||
Verificare ca tunelul chiar e sus (nu te baza pe faptul ca procesul traieste):
|
||||
|
||||
```powershell
|
||||
(Test-NetConnection 127.0.0.1 -Port 1521 -WarningAction SilentlyContinue).TcpTestSucceeded
|
||||
```
|
||||
|
||||
In `stnlc_out.txt` linia care conteaza e `Added client-to-server forwarding rule on
|
||||
127.0.0.1:1521 to localhost:1521.` Inchidere: opreste procesul `stnlc`.
|
||||
|
||||
**Capcana**: un singur port local 1521 pentru mai multe destinatii. Daca ai deja un tunel ridicat
|
||||
catre alt client, al doilea nu mai poate lega portul si vei interoga **alta baza** fara sa-ti dai
|
||||
seama. Verifica intotdeauna cu ce te-ai conectat (vezi pasul 4).
|
||||
|
||||
## 2. Aliasul TNS
|
||||
|
||||
`D:\ROA\instantclient_19_18\tnsnames.ora` (si `instantclient_11_2_0_2` pentru driverul ODBC pe
|
||||
32 de biti). Aliasurile care merg prin tunel arata spre `127.0.0.1`:
|
||||
|
||||
```
|
||||
VENDING =
|
||||
(DESCRIPTION =
|
||||
(ADDRESS_LIST = (ADDRESS = (PROTOCOL = tcp)(HOST = 127.0.0.1)(PORT = 1521)))
|
||||
(CONNECT_DATA = (SERVICE_NAME = XEPDB1))
|
||||
)
|
||||
```
|
||||
|
||||
Pentru `sqlplus` trebuie `TNS_ADMIN` setat pe folderul care contine `tnsnames.ora`:
|
||||
|
||||
```powershell
|
||||
$env:TNS_ADMIN = 'D:\ROA\instantclient_19_18'
|
||||
```
|
||||
|
||||
## 3. DSN-ul ODBC
|
||||
|
||||
Aplicatia nu foloseste TNS direct: `oConn.Connect(tcHost, tcUser, tcPassword)`
|
||||
(`oproceduri_comune.prg`) construieste `dsn=<tcHost>;Uid=...;Pwd=...` - deci **`tcHost` e un nume
|
||||
de DSN ODBC**, nu un host si nu un alias TNS. DSN-urile sunt pe 32 de biti (VFP e pe 32 de biti),
|
||||
deci in `HKLM\SOFTWARE\WOW6432Node\ODBC\ODBC.INI`, cu driverul din `instantclient_11_2_0_2`.
|
||||
|
||||
Listare rapida:
|
||||
|
||||
```powershell
|
||||
Get-ChildItem 'HKLM:\SOFTWARE\WOW6432Node\ODBC\ODBC.INI' |
|
||||
Select-Object -ExpandProperty PSChildName
|
||||
```
|
||||
|
||||
De obicei DSN-ul are acelasi nume cu aliasul TNS (`VENDING`, `CENTRAL`, `ROA_CENTRAL`...).
|
||||
Administrare vizuala: `C:\Windows\SysWOW64\odbcad32.exe` (**nu** cel din `System32`, acela e pe
|
||||
64 de biti si nu vede DSN-urile VFP).
|
||||
|
||||
## 4. Interogare si verificare
|
||||
|
||||
```powershell
|
||||
$env:TNS_ADMIN = 'D:\ROA\instantclient_19_18'
|
||||
& 'D:\ROA\instantclient_19_18\sqlplus.exe' -S -L '<schema>/<parola>@VENDING' '@script.sql'
|
||||
```
|
||||
|
||||
`-L` = o singura incercare de login (nu reincearca la parola gresita). **Foloseste-l intotdeauna
|
||||
pe productie.**
|
||||
|
||||
Prima interogare, mereu, ca sa stii unde ai nimerit:
|
||||
|
||||
```sql
|
||||
select user || ' @ ' || sys_context('USERENV','DB_NAME') from dual;
|
||||
```
|
||||
|
||||
Pentru extragere in fisier, `spool` scrie **local**, nu pe server:
|
||||
|
||||
```sql
|
||||
set pagesize 0 feedback off heading off linesize 100 trimspool on termout off
|
||||
spool D:\cale\locala\iesire.txt
|
||||
select ...;
|
||||
spool off
|
||||
```
|
||||
|
||||
**Capcana PowerShell**: intr-un here-string cu ghilimele duble (`@"..."@`) PowerShell expandeaza
|
||||
`$`. Un regex Oracle care se termina cu `$` (ancora de sfarsit) ajunge stricat in fisierul `.sql`
|
||||
si interogarea intoarce 0 randuri **fara nicio eroare**. Foloseste here-string cu apostrof
|
||||
(`@'...'@`) si inlocuieste caile printr-un token.
|
||||
|
||||
**Capcana `termout off`**: ascunde si mesajele de eroare. Daca fisierul spool iese gol, ruleaza
|
||||
din nou fara `termout off` inainte sa banuiesti datele.
|
||||
|
||||
## Schemele cunoscute (fara parole)
|
||||
|
||||
| Tunel / profil | Alias TNS + DSN | Schema Oracle |
|
||||
|---|---|---|
|
||||
| `vending.tlp` | `VENDING` | `vending` |
|
||||
| conpress | `ROA_CONPRESS` | `xenoti` |
|
||||
|
||||
Restul aliasurilor din `tnsnames.ora` (`ROA_CENTRAL`, `CENTRAL105`, `ROA_TEST`, ...) sunt retea
|
||||
interna si nu au nevoie de tunel.
|
||||
|
||||
## Reguli pe productie
|
||||
|
||||
1. **Doar `SELECT`.** Fara `INSERT`/`UPDATE`/`DELETE`/DDL, fara `ALTER SESSION` care schimba
|
||||
comportament persistent. Daca ai nevoie de date de lucru, extrage-le si lucreaza local.
|
||||
2. **Fara ghicit parole** - vezi avertismentul de la inceput.
|
||||
3. **Interogari marginite**: `fetch first N rows only`, si evita scanarile complete pe tabele
|
||||
mari in orele de lucru.
|
||||
4. **Inchide tunelul** cand ai terminat; nu-l lasa deschis peste noapte.
|
||||
5. **Datele extrase**: codurile fiscale sunt publice si pot sta in repo (vezi
|
||||
`utile\Teste\cache_anaf\codes_*_reale.txt`). Denumirile de parteneri, adresele, soldurile
|
||||
**nu** - nu le comite.
|
||||
|
||||
## De unde vin loturile de coduri fiscale pentru teste
|
||||
|
||||
`COMUN\utile\Teste\cache_anaf\codes_1000_reale.txt` a fost extras din productia `vending`, tabela
|
||||
`nom_parteneri` (23932 randuri, 6382 coduri romanesti distincte):
|
||||
|
||||
```sql
|
||||
select cif from (
|
||||
select distinct regexp_replace(upper(trim(cod_fiscal)), '[^0-9]', '') as cif
|
||||
from nom_parteneri
|
||||
where cod_fiscal is not null
|
||||
and regexp_like(upper(trim(cod_fiscal)), '^(RO)?[[:space:]]*[0-9]{2,10}$')
|
||||
) where length(cif) between 2 and 10
|
||||
order by ora_hash(cif)
|
||||
fetch first 1000 rows only;
|
||||
```
|
||||
|
||||
`order by ora_hash(cif)` da o dispersie **deterministica** (aceeasi de fiecare data), spre
|
||||
deosebire de `dbms_random`; ordonarea crescuta dupa cod ar fi inclinat lotul spre firme vechi,
|
||||
deci spre Bucuresti. Suprapunere cu lotul mai vechi de 200: 8 coduri.
|
||||
|
||||
Folosire in proba end-to-end ANAF:
|
||||
|
||||
```powershell
|
||||
$env:E2E_CODURI = 'D:\ROA\ROADEF\COMUN\utile\Teste\cache_anaf\codes_1000_reale.txt'
|
||||
$env:E2E_LIMITA = '1000'
|
||||
```
|
||||
@@ -169,10 +169,19 @@ diferit de metoda `goExport.export_xls`, doar nume identic.
|
||||
|---|---|---|
|
||||
| VerificareCod (clasa) | validare completa cu webservice: metode VERIFICARE_CIF/VERIFICARE_CNP/ANAF/MFIN/VIES/validare/GetError | 169 |
|
||||
| VerificareANAF (clasa) | sincronizare lista CIF-uri cu ANAF (VerificaListaCIF, ANAF_SincronWebService_PlatitorTva) | 26 |
|
||||
| ParseAdresaAnaf / FaraPrefixLoc (VerificareANAF) | sparge adresa ANAF in judet/localitate/strada/nr/bloc/scara/etaj/apart; FaraPrefixLoc taie prefixul administrativ (JUD./MUN./ORS./COM./SAT, plus formele intregi MUNICIPIUL/JUDETUL - pe Bucuresti ANAF trimite "MUNICIPIUL BUCURESTI") pastrand denumirea intreaga. ORASUL/COMUNA/SATUL NU se taie: exista localitati care incep asa (ORASUL NOU). | - |
|
||||
| test_cif / VALIDARE_CIF / test_cnp / VALIDARE_CNP | validari format + cifra control, offline | 27/19/9/15 |
|
||||
| VERIFICARE_PLATITORTVA / VIES_VAT / ONRC | platitor TVA / verificare VIES / interogare ONRC | 5/5/9 |
|
||||
| IntroducereCod (clasa Form) | formular introducere + validare cod | 25 |
|
||||
|
||||
### onomenclatoare.prg - nomenclatoare judete/localitati
|
||||
| Functie/clasa | Ce face | Uz |
|
||||
|---|---|---|
|
||||
| AnafNomenclator (clasa) | potriveste denumirea de judet/localitate de la ANAF peste cursoarele nomenclatorului (crsJudete/crsLocalitati ale clasei `ct_localitate`) si le pozitioneaza pe randul gasit, de unde se ia id-ul. Trepte: exact -> fara sufix administrativ -> departajare dupa comuna -> cheie fuzzy cu A si I unificate (nomenclatorul tine si TIRGU MURES, si TARGU MURES). Treptele tolerante accepta doar candidat unic; altfel se completeaza doar judetul si utilizatorul alege manual. | - |
|
||||
| AnafNomenclator.nume_curat / poz_marcaj | reduc o denumire la numele propriu, taind sufixul administrativ in ambele forme in care apare: cu paranteze, cum il tine nomenclatorul (`AGIGEA (COM. AGIGEA)`), si fara, cum il trimite ANAF (`AGIGEA COM. AGIGEA`). Marcajele au punct (` COM.`/` ORS.`/` MUN.`/` SAT.`/` JUD.`), deci nu pot manca din numele propriu-zis. | - |
|
||||
| AnafNomenclator.cheie_sufix / fara_marcaj | scot comuna/orasul de care apartine localitatea (`GRECI (COM. SCHITU)` -> `SCHITU`), din oricare din cele doua forme, si fara cuvantul administrativ - sursele il scriu diferit (nomenclator `(ORAS X)`, ANAF `ORS. X`). Folosite la departajarea omonimelor din acelasi judet. | - |
|
||||
| AnafNomenclator.cheie_fuzzy / cheie_bruta | cheie de comparare: fara separatoare, cu A si I unificate. `cheie_fuzzy` taie intai sufixul, `cheie_bruta` nu. | - |
|
||||
|
||||
### ocautare.prg - dialoguri standard de cautare/alegere (de refolosit in loc de SELECT+grid manual)
|
||||
| Functie | Ce alege | Uz (prg/forme) |
|
||||
|---|---|---|
|
||||
|
||||
@@ -10,6 +10,14 @@ Fisiere in `COMUN\utile\Teste\`: `vfp_ui_harness.ps1` (orchestrator generic, par
|
||||
`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`.
|
||||
|
||||
**Initializarea de mediu se alege dupa PROIECT, nu dupa clasa testata.** `test_init_env_auto.prg`
|
||||
are `gcAppPath` hardcodat pe `D:\ROA\ROACONT\`; exista variante `_roagest`, `_roafacturare`,
|
||||
`_acnpro`, `_roadef`. `D:\ROA\ROACONT\COMUN` si `D:\ROA\ROADEF\COMUN` sunt **working copy-uri
|
||||
SVN separate, nu junctions**: un test pornit cu init-ul gresit incarca alt COMUN si nu vede
|
||||
modificarile necomise — trece sau pica pe cod vechi, fara niciun semn. Pune in test o verificare
|
||||
explicita ca noul cod chiar e incarcat (ex. `CREATEOBJECT` pe clasa noua, intr-un `TRY`) inainte
|
||||
de orice asertiune.
|
||||
|
||||
**REGULA (Marius): testele pe aplicatii frontend exerseaza fluxul REAL al
|
||||
utilizatorului** — formularul/butonul/metoda pe care o apeleaza aplicatia, NU introducerea
|
||||
directa a randurilor in cursoare/tabele. Popularea directa sare peste validari/filtre/efecte
|
||||
@@ -117,6 +125,14 @@ g. **Mock de messagebox**: `FUNCTION amessagebox` care returneaza direct valoare
|
||||
(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.
|
||||
**Nu acopera apelurile INTRA-FISIER**: cand functia care deschide dialogul sta in ACELASI
|
||||
`.prg` cu `amessagebox`, mock-ul nu o prinde si dialogul ramane modal. Caz real:
|
||||
`verifica_partener_show_info` si `amessagebox` sunt amandoua in `oproceduri_comune.prg` —
|
||||
rularea headless s-a blocat pe primul CIF pana s-a facut un mock pentru wrapper-ul insusi
|
||||
(`partener_anaf\mock_verifica_partener_show_info.prg`). Regula practica: mock-ul se pune pe
|
||||
functia pe care o apeleaza codul TESTAT, nu pe cea de la capatul lantului. Si pune in test o
|
||||
proba care confirma ca mock-ul a castigat, INAINTE de orice actiune care poate deschide un
|
||||
dialog — altfel esecul se manifesta ca test agatat, nu ca eroare.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user