docs: sterge documente de lucru neactuale (handoff, patch, rapoarte, qa_factura)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EvTWjHyBjoBXT43RFSZN4b
This commit is contained in:
101
docs/PORNIRE.md
101
docs/PORNIRE.md
@@ -1,101 +0,0 @@
|
|||||||
# Pornire pe VM 304 - QA factura/aviz
|
|
||||||
|
|
||||||
Pachet pregatit 17.09.2026 de sesiunea de pe masina principala. Contine tot ce ii trebuie unei
|
|
||||||
sesiuni Claude Code noi ca sa continue de la S1, fara sa refaca analiza.
|
|
||||||
|
|
||||||
## Pasul 1 - copiaza fisierele
|
|
||||||
|
|
||||||
Din acest pachet, in copia de lucru de pe VM (verifica intai care e calea reala - documentatia
|
|
||||||
spune `D:\roa\<produs>`, planul presupune `D:\ROA\ROAFACTURARE`; daca difera, calea reala castiga):
|
|
||||||
|
|
||||||
| Din pachet | Unde pe VM |
|
|
||||||
|---|---|
|
|
||||||
| `docs\*.md` (8 fisiere) | `<radacina ROAFACTURARE>\docs\` |
|
|
||||||
| `utile\context_watch.ps1` | peste `<radacina ROAGEST>\COMUN\utile\context_watch.ps1` |
|
|
||||||
|
|
||||||
Daca pe VM nu exista checkout ROAGEST, pune `context_watch.ps1` oriunde si ajusteaza calea din
|
|
||||||
settings.json la pasul 2.
|
|
||||||
|
|
||||||
## Pasul 2 - hook-urile (optional, dar recomandat)
|
|
||||||
|
|
||||||
In `%USERPROFILE%\.claude\settings.json` de pe VM, adauga:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"env": {
|
|
||||||
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "70"
|
|
||||||
},
|
|
||||||
"hooks": {
|
|
||||||
"SubagentStop": [
|
|
||||||
{ "hooks": [ { "type": "command",
|
|
||||||
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File <CALEA CATRE>\\context_watch.ps1 -Subagent -Json" } ] }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Daca `settings.json` are deja `env` sau `hooks`, se completeaza, nu se inlocuieste.
|
|
||||||
Verifica dupa editare ca fisierul e JSON valid.
|
|
||||||
|
|
||||||
Ce face: la fiecare subagent care se termina, masoara contextul ACELUI subagent (citind
|
|
||||||
`agent_transcript_path` din inputul hook-ului) si avertizeaza orchestratorul la 150k / 200k.
|
|
||||||
Tace sub prag si tace la `stop_hook_active=true`, ca sa nu intre in bucla.
|
|
||||||
|
|
||||||
## Pasul 3 - verifica mediul inainte de orice
|
|
||||||
|
|
||||||
Rulare rapida, inainte sa incepi lucrul:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
Test-Path 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
|
|
||||||
Test-Path '<radacina>\COMUN\utile\Teste\vfp_ui_harness.ps1'
|
|
||||||
Test-Path '<radacina>\COMUN\utile\Teste\test_init_env_auto_roafacturare.prg'
|
|
||||||
Test-Path 'D:\ROA\UTIL\foxbin2prg\vfp_symbols.ps1'
|
|
||||||
```
|
|
||||||
|
|
||||||
Daca vreunul lipseste, **spune-i lui Marius inainte sa incepi** - planul presupune ca exista.
|
|
||||||
Conexiunea Oracle se probeaza cu:
|
|
||||||
`DO test_init_env_auto_roafacturare WITH 'CENTRAL','MARIUSM_AUTO','ROMFASTSOFT'`
|
|
||||||
|
|
||||||
## Pasul 4 - promptul de pornire
|
|
||||||
|
|
||||||
Da-i sesiunii noi exact textul asta:
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Continua planul din `docs\plan_qa_factura_aviz.md`. Starea curenta e in
|
|
||||||
`docs\progres_qa_factura.md` - citeste-le pe amandoua inainte de orice.
|
|
||||||
|
|
||||||
S0 e terminat (prototipul de context/handoff). Urmeaza **S1: baseline QA**, si e primul story
|
|
||||||
care se face pe masina asta, cu UI vizibil.
|
|
||||||
|
|
||||||
Inainte sa incepi, ruleaza verificarile de mediu din `PORNIRE.md` pasul 3 si spune-mi daca ceva
|
|
||||||
lipseste.
|
|
||||||
|
|
||||||
Reguli care se aplica de la primul pas, nu dupa ce acumulezi context:
|
|
||||||
- Orice investigatie, editare, rulare de teste se deleaga unui subagent. Tu orchestrezi.
|
|
||||||
- Nu atinge `combosql` din `COMUN\clase\_cb_base.vc2` - e clasa de baza a tuturor produselor ROA.
|
|
||||||
Comportamentul nou de cautare se suprascrie in `combosql_cautare` din `ofacturare.vc2`.
|
|
||||||
- `.vc2` se editeaza cu script Python binar, niciodata `sed -i` (sterge CRLF-urile) si niciodata
|
|
||||||
Edit pe linii cu octeti >0x7F (diacriticele sunt cp1250, nu cp1252).
|
|
||||||
- Write-back doar `txt2vcx.ps1 -ProjectRoot <radacina ROAFACTURARE>`. Fara `-ProjectRoot` scrie
|
|
||||||
in alt produs.
|
|
||||||
- Dupa fiecare write-back: `MODIFY CLASS` + captura, si inchide designerul inainte de urmatorul.
|
|
||||||
- Commit dupa fiecare story, pe branch de lucru, fara push. Mandatul e dat: nu cere aprobare per
|
|
||||||
story. Exceptie: mockup-ul de la S4 se arata lui Marius inainte de implementare.
|
|
||||||
- Actualizeaza `docs\progres_qa_factura.md` dupa FIECARE story, nu la final.
|
|
||||||
- La ~250k context: opreste lucrul, scrie handoff pe disc, preda. „Mai am putin" nu e motiv de
|
|
||||||
amanare.
|
|
||||||
|
|
||||||
S1 nu modifica niciun cod. Produce doar dovada vizuala si scenariile, in
|
|
||||||
`docs\qa_factura_baseline.md` + capturi.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Ce sa NU refaca sesiunea noua
|
|
||||||
|
|
||||||
Analiza e platita deja, e in `docs\qa_factura_harta.md` (harta de cod, cu fisier:linie),
|
|
||||||
`docs\qa_factura_unelte.md` (harness UI, headless, Oracle, hook-uri) si
|
|
||||||
`docs\qa_factura_hooks_ref.md` (referinta de hook-uri). Se citesc, nu se refac.
|
|
||||||
|
|
||||||
Doua lucruri stabilite si neredeschise:
|
|
||||||
- Pragul de 3 caractere la cautare = proprietatea `ncharcountbegin=2`, nu logica.
|
|
||||||
- Duplicatele de articol vin din Oracle (`pack_facturare.cursor_preturi`), nu din VFP. S3 e
|
|
||||||
blocat pana se citeste corpul pachetului din `ALL_SOURCE` pe MARIUSM_AUTO.
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
# Diff - conturi 667/709 + analitice pe articol (COMUN)
|
|
||||||
|
|
||||||
Data: 2026-09-17. Fisiere SVN de comis: `programe/ofacturare_comun.prg`, `clase/onom_articole.vcx` + `.vct` (regenerate din `.vc2` prin txt2vcx; write-back verificat byte-identic CRLF-normalizat).
|
|
||||||
|
|
||||||
```diff
|
|
||||||
diff --git a/clase/onom_articole.vc2 b/clase/onom_articole.vc2
|
|
||||||
index 613f562..a263fd3 100644
|
|
||||||
--- a/clase/onom_articole.vc2
|
|
||||||
+++ b/clase/onom_articole.vc2
|
|
||||||
@@ -712,7 +712,9 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
|
|
||||||
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Cb_tx_proc_tvav" UniqueID="" Timestamp="" />
|
|
||||||
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Cb_tx_venit" UniqueID="" Timestamp="" />
|
|
||||||
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scd" UniqueID="" Timestamp="" />
|
|
||||||
+ *< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scd.Text1" UniqueID="" Timestamp="" />
|
|
||||||
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scc" UniqueID="" Timestamp="" />
|
|
||||||
+ *< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scc.Text1" UniqueID="" Timestamp="" />
|
|
||||||
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_cont_dedus" UniqueID="" Timestamp="" />
|
|
||||||
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Lb_info_conturi" UniqueID="" Timestamp="" />
|
|
||||||
*< OBJECTDATA: ObjPath="Cus_odata_parteneri_articole_coduri" UniqueID="" Timestamp="" />
|
|
||||||
@@ -1586,6 +1588,18 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
|
|
||||||
Lb_simplu1.Name = "Lb_simplu1"
|
|
||||||
*< END OBJECT: ClassLib="lb_tx.vcx" BaseClass="container" />
|
|
||||||
|
|
||||||
+ ADD OBJECT '_pageframe1.Page3.Clb_tx_scc.Text1' AS textbox WITH ;
|
|
||||||
+ Format = "!k", ;
|
|
||||||
+ Height = 23, ;
|
|
||||||
+ Left = 302, ;
|
|
||||||
+ MaxLength = 4, ;
|
|
||||||
+ Name = "Text1", ;
|
|
||||||
+ TabIndex = 2, ;
|
|
||||||
+ ToolTipText = "Cont analitic (optional)", ;
|
|
||||||
+ Top = 3, ;
|
|
||||||
+ Width = 74
|
|
||||||
+ *< END OBJECT: BaseClass="textbox" />
|
|
||||||
+
|
|
||||||
ADD OBJECT '_pageframe1.Page3.Clb_tx_scd' AS clb_tx_simplu WITH ;
|
|
||||||
Height = 29, ;
|
|
||||||
Left = 9, ;
|
|
||||||
@@ -1604,6 +1618,18 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
|
|
||||||
Lb_simplu1.Name = "Lb_simplu1"
|
|
||||||
*< END OBJECT: ClassLib="lb_tx.vcx" BaseClass="container" />
|
|
||||||
|
|
||||||
+ ADD OBJECT '_pageframe1.Page3.Clb_tx_scd.Text1' AS textbox WITH ;
|
|
||||||
+ Format = "!k", ;
|
|
||||||
+ Height = 23, ;
|
|
||||||
+ Left = 302, ;
|
|
||||||
+ MaxLength = 4, ;
|
|
||||||
+ Name = "Text1", ;
|
|
||||||
+ TabIndex = 2, ;
|
|
||||||
+ ToolTipText = "Cont analitic (optional)", ;
|
|
||||||
+ Top = 3, ;
|
|
||||||
+ Width = 74
|
|
||||||
+ *< END OBJECT: BaseClass="textbox" />
|
|
||||||
+
|
|
||||||
ADD OBJECT '_pageframe1.Page3.Lb_info_conturi' AS _label WITH ;
|
|
||||||
Caption = "Necompletate: debit 4111, credit contul de venit al articolului", ;
|
|
||||||
Left = 9, ;
|
|
||||||
@@ -1934,6 +1960,8 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
|
|
||||||
This._pageframe1.Page3.Clb_tx_pret_ctva.Text_simplu1.ControlSource = "Thisform.oPretNom.nPretCtva"
|
|
||||||
This._pageframe1.Page3.Clb_tx_scd.Text_simplu1.ControlSource = "Thisform.oPretNom.cScd"
|
|
||||||
This._pageframe1.Page3.Clb_tx_scc.Text_simplu1.ControlSource = "Thisform.oPretNom.cScc"
|
|
||||||
+ This._pageframe1.Page3.Clb_tx_scd.Text1.ControlSource = "Thisform.oPretNom.cAscd"
|
|
||||||
+ This._pageframe1.Page3.Clb_tx_scc.Text1.ControlSource = "Thisform.oPretNom.cAscc"
|
|
||||||
This._pageframe1.Page3.Clb_tx_cont_dedus.Text_simplu1.ControlSource = "Thisform.oPretNom.cContDedus"
|
|
||||||
If Used('crsvenchelt')
|
|
||||||
Use In crsvenchelt
|
|
||||||
diff --git a/programe/ofacturare_comun.prg b/programe/ofacturare_comun.prg
|
|
||||||
index 7b6b46b..efd2002 100644
|
|
||||||
--- a/programe/ofacturare_comun.prg
|
|
||||||
+++ b/programe/ofacturare_comun.prg
|
|
||||||
@@ -74,6 +74,10 @@
|
|
||||||
*!* marius.mutu
|
|
||||||
*!* + verifica_cont_venit_linie - verificare prietenoasa a contului de venit rezolvat pentru o linie
|
|
||||||
*!* de factura, la adaugarea articolului
|
|
||||||
+*!* 17.09.2026
|
|
||||||
+*!* agent
|
|
||||||
+*!* cus_pret_nomenclator: conturi analitice (cAscd/cAscc) in nota de vanzare a articolului;
|
|
||||||
+*!* apelul RPC devine pack_preturi.gaseste_creeaza_nota_vanzare (overload 6 arg cu analitice)
|
|
||||||
|
|
||||||
***************************************************************************************************************
|
|
||||||
**** Clase:
|
|
||||||
@@ -2375,6 +2379,8 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
nIdNota = .NULL.
|
|
||||||
cScd = []
|
|
||||||
cScc = []
|
|
||||||
+ cAscd = []
|
|
||||||
+ cAscc = []
|
|
||||||
cContDedus = []
|
|
||||||
cEroare = []
|
|
||||||
|
|
||||||
@@ -2410,6 +2416,8 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
This.nIdNota = .NULL.
|
|
||||||
This.cScd = []
|
|
||||||
This.cScc = []
|
|
||||||
+ This.cAscd = []
|
|
||||||
+ This.cAscc = []
|
|
||||||
This.cContDedus = []
|
|
||||||
If This.asigura_politica() <= 0
|
|
||||||
Return .F.
|
|
||||||
@@ -2450,7 +2458,7 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
Use In crspolart
|
|
||||||
Endif
|
|
||||||
If !Isnull(This.nIdNota)
|
|
||||||
- lcSql = [select n.scd, n.scc from crm_note_vanzari v, note_contabile n ] + ;
|
|
||||||
+ lcSql = [select n.scd, n.ascd, n.scc, n.ascc from crm_note_vanzari v, note_contabile n ] + ;
|
|
||||||
[where v.id_nota = ] + Alltrim(Str(This.nIdNota)) + [ and n.id_set = v.id_set]
|
|
||||||
If Used('crsnotapol')
|
|
||||||
Use In crsnotapol
|
|
||||||
@@ -2459,6 +2467,8 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
If lnSucces >= 0 And Reccount('crsnotapol') > 0
|
|
||||||
This.cScd = Alltrim(Nvl(crsnotapol.scd,[]))
|
|
||||||
This.cScc = Alltrim(Nvl(crsnotapol.scc,[]))
|
|
||||||
+ This.cAscd = Alltrim(Nvl(crsnotapol.ascd,[]))
|
|
||||||
+ This.cAscc = Alltrim(Nvl(crsnotapol.ascc,[]))
|
|
||||||
Endif
|
|
||||||
If Used('crsnotapol')
|
|
||||||
Use In crsnotapol
|
|
||||||
@@ -2482,11 +2492,20 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
* valideaza conturile si pretul curente; mesaj + .F. la refuz
|
|
||||||
Procedure valideaza
|
|
||||||
This.cEroare = []
|
|
||||||
- If (!Empty(This.cScd) And Empty(This.cScc)) Or (Empty(This.cScd) And !Empty(This.cScc))
|
|
||||||
- This.cEroare = [Trebuie completate ambele conturi (debit si credit) sau niciunul.]
|
|
||||||
+ If !Empty(This.cAscd) And Empty(This.cScd)
|
|
||||||
+ This.cEroare = [Contul analitic debitor nu poate fi completat fara contul sintetic debitor.]
|
|
||||||
Else
|
|
||||||
- If This.nPretFtva < 0 Or This.nPretCtva < 0
|
|
||||||
- This.cEroare = [Pretul nu poate fi negativ.]
|
|
||||||
+ If !Empty(This.cAscc) And Empty(This.cScc)
|
|
||||||
+ This.cEroare = [Contul analitic creditor nu poate fi completat fara contul sintetic creditor.]
|
|
||||||
+ Endif
|
|
||||||
+ Endif
|
|
||||||
+ If Empty(This.cEroare)
|
|
||||||
+ If (!Empty(This.cScd) And Empty(This.cScc)) Or (Empty(This.cScd) And !Empty(This.cScc))
|
|
||||||
+ This.cEroare = [Trebuie completate ambele conturi (debit si credit) sau niciunul.]
|
|
||||||
+ Else
|
|
||||||
+ If This.nPretFtva < 0 Or This.nPretCtva < 0
|
|
||||||
+ This.cEroare = [Pretul nu poate fi negativ.]
|
|
||||||
+ Endif
|
|
||||||
Endif
|
|
||||||
Endif
|
|
||||||
If !Empty(This.cEroare)
|
|
||||||
@@ -2505,14 +2524,16 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
Return .F.
|
|
||||||
Endif
|
|
||||||
If !Empty(This.cScd) And !Empty(This.cScc)
|
|
||||||
- Private pcScd, pcScc, pnIdUtil
|
|
||||||
+ Private pcScd, pcScc, pcAscd, pcAscc, pnIdUtil
|
|
||||||
pcScd = This.cScd
|
|
||||||
pcScc = This.cScc
|
|
||||||
+ pcAscd = This.cAscd
|
|
||||||
+ pcAscc = This.cAscc
|
|
||||||
pnIdUtil = gnIdUtil
|
|
||||||
pnIdNota = 0
|
|
||||||
- lcSql = [begin pack_preturi.gaseste_sau_creeaza_nota_vanzare(?pcScd,?pcScc,?pnIdUtil,?@pnIdNota); end;]
|
|
||||||
+ lcSql = [begin pack_preturi.gaseste_creeaza_nota_vanzare(?pcScd,?pcAscd,?pcScc,?pcAscc,?pnIdUtil,?@pnIdNota); end;]
|
|
||||||
lnSucces = goExecutor.oExecute(lcSql)
|
|
||||||
- Release pcScd, pcScc, pnIdUtil
|
|
||||||
+ Release pcScd, pcScc, pcAscd, pcAscc, pnIdUtil
|
|
||||||
If lnSucces < 0
|
|
||||||
AMESSAGEBOX(goExecutor.oPrelucrareEroare(),16,"Eroare")
|
|
||||||
Release pnIdArticol, pnPretFtva, pnPretCtva, pnProcTvav, pnIdVenchelt, pnIdNota, pnScrieNota
|
|
||||||
@@ -2555,7 +2576,9 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
Local lcSql, lnSucces
|
|
||||||
This.cScd = []
|
|
||||||
This.cScc = []
|
|
||||||
- lcSql = [select n.scd, n.scc from crm_politici_preturi p, crm_note_vanzari v, note_contabile n ] + ;
|
|
||||||
+ This.cAscd = []
|
|
||||||
+ This.cAscc = []
|
|
||||||
+ lcSql = [select n.scd, n.ascd, n.scc, n.ascc from crm_politici_preturi p, crm_note_vanzari v, note_contabile n ] + ;
|
|
||||||
[where p.id_pol = ] + Alltrim(Str(tnIdPol)) + [ and v.id_nota = p.id_nota and n.id_set = v.id_set]
|
|
||||||
If Used('crsnotapolstoc')
|
|
||||||
Use In crsnotapolstoc
|
|
||||||
@@ -2564,6 +2587,8 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
If lnSucces >= 0 And Reccount('crsnotapolstoc') > 0
|
|
||||||
This.cScd = Alltrim(Nvl(crsnotapolstoc.scd,[]))
|
|
||||||
This.cScc = Alltrim(Nvl(crsnotapolstoc.scc,[]))
|
|
||||||
+ This.cAscd = Alltrim(Nvl(crsnotapolstoc.ascd,[]))
|
|
||||||
+ This.cAscc = Alltrim(Nvl(crsnotapolstoc.ascc,[]))
|
|
||||||
Endif
|
|
||||||
If Used('crsnotapolstoc')
|
|
||||||
Use In crsnotapolstoc
|
|
||||||
@@ -2573,8 +2598,10 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
|
|
||||||
* valideaza (ambele conturi sau niciunul) si scrie nota implicita a politicii date
|
|
||||||
Procedure salveaza_nota_politica
|
|
||||||
- Lparameters tnIdPol, tcScd, tcScc
|
|
||||||
+ Lparameters tnIdPol, tcScd, tcScc, tcAscd, tcAscc
|
|
||||||
Local lcSql, lnSucces
|
|
||||||
+ tcAscd = Iif(Type('tcAscd')='C', tcAscd, [])
|
|
||||||
+ tcAscc = Iif(Type('tcAscc')='C', tcAscc, [])
|
|
||||||
If (!Empty(tcScd) And Empty(tcScc)) Or (Empty(tcScd) And !Empty(tcScc))
|
|
||||||
This.cEroare = [Trebuie completate ambele conturi (debit si credit) sau niciunul.]
|
|
||||||
AMESSAGEBOX(This.cEroare,0+48,"Atentie")
|
|
||||||
@@ -2583,16 +2610,18 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
If Empty(tcScd) And Empty(tcScc)
|
|
||||||
Return .T.
|
|
||||||
Endif
|
|
||||||
- Private pcScd, pcScc, pnIdUtil, pnIdNota, pnIdPol
|
|
||||||
+ Private pcScd, pcScc, pcAscd, pcAscc, pnIdUtil, pnIdNota, pnIdPol
|
|
||||||
pcScd = tcScd
|
|
||||||
pcScc = tcScc
|
|
||||||
+ pcAscd = tcAscd
|
|
||||||
+ pcAscc = tcAscc
|
|
||||||
pnIdUtil = gnIdUtil
|
|
||||||
pnIdNota = 0
|
|
||||||
- lcSql = [begin pack_preturi.gaseste_sau_creeaza_nota_vanzare(?pcScd,?pcScc,?pnIdUtil,?@pnIdNota); end;]
|
|
||||||
+ lcSql = [begin pack_preturi.gaseste_creeaza_nota_vanzare(?pcScd,?pcAscd,?pcScc,?pcAscc,?pnIdUtil,?@pnIdNota); end;]
|
|
||||||
lnSucces = goExecutor.oExecute(lcSql)
|
|
||||||
If lnSucces < 0
|
|
||||||
AMESSAGEBOX(goExecutor.oPrelucrareEroare(),16,"Eroare")
|
|
||||||
- Release pcScd, pcScc, pnIdUtil, pnIdNota, pnIdPol
|
|
||||||
+ Release pcScd, pcScc, pcAscd, pcAscc, pnIdUtil, pnIdNota, pnIdPol
|
|
||||||
Return .F.
|
|
||||||
Endif
|
|
||||||
pnIdPol = tnIdPol
|
|
||||||
@@ -2601,7 +2630,7 @@ Define Class cus_pret_nomenclator As Custom
|
|
||||||
If lnSucces < 0
|
|
||||||
AMESSAGEBOX(goExecutor.oPrelucrareEroare(),16,"Eroare")
|
|
||||||
Endif
|
|
||||||
- Release pcScd, pcScc, pnIdUtil, pnIdNota, pnIdPol
|
|
||||||
+ Release pcScd, pcScc, pcAscd, pcAscc, pnIdUtil, pnIdNota, pnIdPol
|
|
||||||
Return (lnSucces >= 0)
|
|
||||||
Endproc && salveaza_nota_politica
|
|
||||||
Enddefine
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Erori deschise — formular facturare unificat
|
|
||||||
|
|
||||||
Erori observate la rulare, neinvestigate inca. Se sterge intrarea cand e reparata.
|
|
||||||
|
|
||||||
## 1. `Alias 'CRSGESTIUNE' is not found.` la parasirea combo-ului de gestiune
|
|
||||||
|
|
||||||
- **Unde:** `FRM_FACTURARE_ARTICOLE2.GRD_FACTURA.CGESTIUNE.CCBOGESTIUNE.LOSTFOCUS`, linia 5
|
|
||||||
- **Linia:** `REPLACE nume_gestiune WITH crsGestiune.nume_gestiune, id_gestiune WITH crsGestiune.id_gestiune IN crsFactura`
|
|
||||||
- **Mediu:** MARIUSM_AUTO, 09.09.2026
|
|
||||||
- **De verificat:** cine creeaza `crsGestiune` si pe ce cale de cod ajunge `LostFocus` sa ruleze
|
|
||||||
inainte (sau dupa inchiderea) cursorului — probabil combo-ul isi pierde focusul intr-un context
|
|
||||||
in care cursorul nu a fost creat sau a fost deja inchis.
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
# Verificare "function hooks" — Claude Code 2.1.274 (nativ, win32-x64)
|
|
||||||
|
|
||||||
Sursa de verificat: https://claudefa.st/blog/tools/hooks/function-hooks (blog tert, neoficial).
|
|
||||||
Metoda: (1) incercare de rulare reala `/plugin-types`; (2) cautare de siruri si context in
|
|
||||||
binarul nativ instalat, `C:\Users\mmari\.local\bin\claude.exe` (233691808 octeti, Bun-compiled,
|
|
||||||
comitul `1efcc1361e64`). Fiecare constatare de mai jos e marcata cu sursa ei.
|
|
||||||
|
|
||||||
## 1. Rularea comenzii cerute
|
|
||||||
|
|
||||||
```
|
|
||||||
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude -p "/plugin-types ./types"
|
|
||||||
```
|
|
||||||
|
|
||||||
Rezultat: **nu s-a generat niciun fisier**. Raspunsul modelului a fost text conversational
|
|
||||||
("Ready — no task given yet." / la reincercare cu `--output-format json`: `"I'm ready. What
|
|
||||||
would you like me to work on?"`). Niciun folder `types/` sau `.claude/types/` nu a aparut in
|
|
||||||
scratchpad dupa rulare (verificat cu `ls`).
|
|
||||||
|
|
||||||
**Cauza identificata, nu specifica function hooks**: am testat control cu `claude -p "/help"`
|
|
||||||
(comanda locala cea mai de baza din tot CLI-ul, intotdeauna inregistrata, fara nicio poarta de
|
|
||||||
feature-flag). A esuat identic — modelul a primit "/help" ca text mangled si a raspuns
|
|
||||||
conversational, in loc sa afiseze ajutorul. **Concluzie: invocarea `claude -p "<text>"` din acest
|
|
||||||
mediu nu ruteaza deloc prin dispecerul de comenzi locale ("slash commands")** — cererea merge
|
|
||||||
direct la model ca prompt text. Din `--debug-file`, header-ul de atribuire arata
|
|
||||||
`cc_entrypoint=sdk-cli`, adica sesiunea porneste prin calea SDK, nu prin REPL-ul interactiv complet;
|
|
||||||
acolo comenzile locale par sa nu fie interceptate. Deci esecul lui `/plugin-types` **nu e dovada
|
|
||||||
ca function hooks sunt dezactivate** — e o limitare a modului de invocare folosit aici, valabila
|
|
||||||
si pentru comenzi complet neutre ca `/help`. N-am putut testa varianta interactiva (fara TTY in
|
|
||||||
acest mediu).
|
|
||||||
|
|
||||||
`claude --help` si `claude plugin --help` (rulate separat, capturate integral) **nu listeaza**
|
|
||||||
nicio comanda `plugin-types` la nivel de CLI top-level — motivul e ca `/plugin-types` e o comanda
|
|
||||||
*in-sesiune* ("slash command"), nu o subcomanda a executabilului `claude`, deci absenta ei din
|
|
||||||
`--help` e normala si nu spune nimic despre activare.
|
|
||||||
|
|
||||||
## 2. Ce exista REAL in binar (cautare de siruri, cu offset de octet verificabil)
|
|
||||||
|
|
||||||
Nu s-au generat `.d.ts`, deci n-am putut citi declaratii TypeScript complete ca "dovada
|
|
||||||
canonica" ceruta in sarcina. In schimb, am gasit in binar cod sursa minificat (nu doar siruri
|
|
||||||
izolate) care confirma mecanismul de facto. Tot ce urmeaza e citat verbatim din binar, cu offset.
|
|
||||||
|
|
||||||
### 2a. Flag-ul si poarta lui reala
|
|
||||||
|
|
||||||
La offset ~103270720 (grep -a -b), blocul de cod al flag-ului:
|
|
||||||
|
|
||||||
```
|
|
||||||
var kYe="tengu_plugin_hooks_modules";
|
|
||||||
var Azt=()=>!1;
|
|
||||||
var AFe=()=>a.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS??P(kYe,Azt());
|
|
||||||
var h8=()=>AFe()&&!qb()&&!Br("hooks")&&!zm();
|
|
||||||
```
|
|
||||||
|
|
||||||
Interpretare directa din cod:
|
|
||||||
- flag-ul GrowthBook real se numeste **`tengu_plugin_hooks_modules`**, default **oprit** (`Azt=()=>!1`).
|
|
||||||
- `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` e o suprascriere locala peste acel flag (`??`) — confirma
|
|
||||||
exact ce zice blogul despre numele variabilei de mediu.
|
|
||||||
- **dar** poarta finala folosita in productie, `h8()`, cere si `!qb() && !Br("hooks") && !zm()`
|
|
||||||
— trei conditii suplimentare. Numele acestor functii sunt minificate si reciclate in zeci de
|
|
||||||
module din bundle (acelasi nume `qb`/`Br`/`zm` apare cu corpuri complet diferite in alte
|
|
||||||
scope-uri), deci **nu pot atribui cu certitudine ce verifica exact aici** fara deminificare
|
|
||||||
completa — onest: aceasta e limita cercetarii, nu o concluzie.
|
|
||||||
- Alaturi, la acelasi offset, un tabel de atribuire a sursei flag-ului pentru afisare in UI:
|
|
||||||
`{override:"from a local override", payload:"from GrowthBook (this session's payload)",
|
|
||||||
disk:"from GrowthBook (the disk cache of an earlier session)", disabled:"from the default
|
|
||||||
(GrowthBook is off ...)"}` — confirma ca e un flag GrowthBook cu override local, exact
|
|
||||||
mecanismul descris de blog.
|
|
||||||
|
|
||||||
### 2b. Comanda `/plugin-types` — confirmata REAL, dar NEgatata de flag
|
|
||||||
|
|
||||||
La offset 203438732, definitia completa a comenzii (citat verbatim):
|
|
||||||
|
|
||||||
```
|
|
||||||
var mer=Object.freeze({type:"local",name:"plugin-types",
|
|
||||||
description:"Write claude-code.d.ts, claude-code-plugins.d.ts and claude-code-mcp.d.ts:
|
|
||||||
the plugin API's TypeScript declarations, the enabled plugins' type contracts and the
|
|
||||||
inputs of the connected MCP tools, for typing a hooks module against this session",
|
|
||||||
argumentHint:"[dir]", supportsNonInteractive:!0,
|
|
||||||
load:()=>import("B:/~BUN/root/chunk-0rpsm23r.js")});
|
|
||||||
```
|
|
||||||
|
|
||||||
Important: **acest obiect n-are nicio conditie `isEnabled`/gate legata de `h8()` sau de
|
|
||||||
flag** — e inregistrata neconditionat. Descrierea confirma explicit sintagma "hooks module"
|
|
||||||
din blog: comanda exista ca sa tipizeze "a hooks module against this session". Deci comanda in
|
|
||||||
sine e reala si documentata intern, indiferent de starea flag-ului; problema empirica de mai
|
|
||||||
sus (sectiunea 1) e doar despre modul de invocare `-p` din acest mediu.
|
|
||||||
|
|
||||||
### 2c. Lista REALA de evenimente si API-uri ("scan manifest"), gasita ca date, nu ca documentatie
|
|
||||||
|
|
||||||
La offset 223160560 exista un manifest static de tip "scan" folosit (aparent) pentru analiza
|
|
||||||
statica / sandboxing a unui modul de plugin, cu lista completa si explicita:
|
|
||||||
|
|
||||||
```js
|
|
||||||
scan: {
|
|
||||||
hooks: ["session.start","ui.render","command.run","ui.close","ui.focus","ui.scroll",
|
|
||||||
"tool.call","prompt.submit"],
|
|
||||||
calls: ["clock.after","clock.every","clock.now","command.register","fs.list","fs.read",
|
|
||||||
"fs.stat","process.run","session.id","session.messages","store.get","store.set",
|
|
||||||
"telemetry.log","telemetry.mark","ui.close","ui.invalidate","ui.log","ui.open",
|
|
||||||
"ui.resolve","ui.status"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Aceasta e cea mai tare dovada gasita — e o **lista de date**, nu un string de proza, deci
|
|
||||||
foarte probabil chiar suprafata reala (sau foarte apropiata) a API-ului de hooks module:
|
|
||||||
|
|
||||||
- **evenimente de hook confirmate**: `session.start`, `ui.render`, `command.run`, `ui.close`,
|
|
||||||
`ui.focus`, `ui.scroll`, `tool.call`, `prompt.submit` (8 la numar).
|
|
||||||
- **`turn.complete` si `turn.start` NU sunt in lista de evenimente de hook**, desi cele doua
|
|
||||||
siruri exista in alta parte a binarului (24, respectiv 68 aparitii) — apartin mecanismului
|
|
||||||
intern de motor ("engine turn 1 start/end", vazut si in log-ul de debug la rularea reala),
|
|
||||||
nu suprafetei de hooks expuse pluginurilor.
|
|
||||||
- **`prompt.submit` e confirmat**, si separat, la offset 206959908, exista codul intern real
|
|
||||||
care implementeaza acest hook point: un pipeline `core`/`managed` cu posibilitate de a
|
|
||||||
rescrie textul promptului (`"prompt.submit: text rewritten by a hook (...)"`) sau de a-l
|
|
||||||
bloca (`"Prompt dropped by a hook: ..."`). Semnatura exacta gasita e apelul
|
|
||||||
`.prompt.submit({submission, origin, turnId, shouldWait})` — **nu am gasit litera `$` ca
|
|
||||||
alias/receiver exact** in siruri; forma `$.prompt.submit({text})` din blog e plauzibila ca
|
|
||||||
sugar-syntax peste acest mecanism, dar nu e confirmata verbatim.
|
|
||||||
- **API-uri de tip "calls" confirmate ca namespace-uri cu metode**: `fs.read/list/stat`
|
|
||||||
(deci `$.fs` probabil e un namespace, nu o valoare simpla), `store.get/set`,
|
|
||||||
`clock.after/every/now`, `session.id/messages` — coincide cu ce cerea sarcina sa verific
|
|
||||||
(`$.session.messages()`, `$.fs`, `$.store`, `$.clock.every`), dar iar, prefixul `$.` in sine
|
|
||||||
nu apare ca sir literal langa aceste nume — e o inferenta rezonabila, nu o citare directa.
|
|
||||||
|
|
||||||
### 2d. Tokeni / context / usage / compactare — CAUTATE EXPLICIT, NU GASITE
|
|
||||||
|
|
||||||
Am cautat explicit `token`, `usage`, `context`, `compact` in vecinatatea manifestului de mai
|
|
||||||
sus si in restul zonei de "function hooks". **Niciunul dintre cele 20 de `calls` sau 8 `hooks`
|
|
||||||
de mai sus nu are legatura cu tokeni, context window, usage sau compactare.** Sirurile
|
|
||||||
`contextWindow` (8 aparitii) si `compact` (405 aparitii) exista din abundenta in binar, dar in
|
|
||||||
alte module (afisarea usage-ului in `--output-format json`, autocompact intern al motorului
|
|
||||||
— vazut si in logul de debug: `autocompact: tokens=[REDACTED] level=ok effectiveWindow=980000`),
|
|
||||||
**nu ca hook sau call disponibil unui modul de function hooks**. Nu exista dovada in acest
|
|
||||||
binar ca un modul de hooks ar putea citi tokenii ramasi sau starea de compactare.
|
|
||||||
|
|
||||||
### 2e. Forma `register` si `hooks/hooks.json`
|
|
||||||
|
|
||||||
Doua descoperiri, ambele reale, dar **doua lucruri diferite**:
|
|
||||||
|
|
||||||
1. **`hooks.json` din binar (27 aparitii) e manifestul VECHI/existent de plugin-hooks**
|
|
||||||
(PreToolUse, SessionStart etc. — cel folosit deja de pluginul `ponytail` din acest proiect,
|
|
||||||
vazut in logul de debug: `Read manifest hooks for plugin ponytail (enabled=true):
|
|
||||||
./hooks/claude-codex-hooks.json`). Contextul din binar confirma: sirul e `"plugin
|
|
||||||
hooks.json"`, langa mesaje despre `SKILL.md` si actualizarea regulilor de auto-mode/permisiuni
|
|
||||||
— sistemul clasic de hooks pe evenimente de tool-use, nesuprapus cu "function hooks".
|
|
||||||
|
|
||||||
2. **Forma `register` pentru "function hooks" (modulul nou)**, gasita in doua exemple interne
|
|
||||||
reale de pluginuri deja construite cu acest mecanism:
|
|
||||||
- `tengu_quiet_dolphin` — "panoul de diff ca plugin: /diff", cu
|
|
||||||
`isAvailable:()=>At` unde `At=()=>h8()&&lu(Rr(),bt)` — **confirma ca h8() (poarta de
|
|
||||||
function hooks descrisa la 2a) chiar gateaza un plugin real deja construit**, nu doar un
|
|
||||||
flag mort.
|
|
||||||
- `tengu_tips_mod` — "spinner tips as a plugin", cu forma exacta:
|
|
||||||
```js
|
|
||||||
var P=(e)=>({register:(o)=>{e.registerHooks(o,x())}});
|
|
||||||
```
|
|
||||||
adica modulul exporta un `register(engine)` care apeleaza
|
|
||||||
`engine.registerHooks(implementareHooks, apiSuplimentar)`. E cea mai apropiata dovada
|
|
||||||
gasita de "forma exacta a functiei register", dar e exemplul unui plugin intern concret,
|
|
||||||
nu semnatura generica documentata explicit undeva ca atare.
|
|
||||||
|
|
||||||
## VERDICT
|
|
||||||
|
|
||||||
Se poate construi o bucla "prag context -> scrie handoff -> /clear -> reia din handoff" complet
|
|
||||||
automat, FARA interventia utilizatorului, folosind acest surface?
|
|
||||||
|
|
||||||
**Nu, nu cu ce am vazut efectiv in acest binar.** Motive, punct cu punct:
|
|
||||||
|
|
||||||
1. Suprafata de hooks confirmata (`session.start`, `ui.*`, `command.run`, `tool.call`,
|
|
||||||
`prompt.submit`) **nu contine niciun eveniment sau apel legat de prag de context, tokeni,
|
|
||||||
usage sau compactare** (sectiunea 2d) — deci un modul de hooks n-ar avea de unde sa afle
|
|
||||||
"am trecut de 200k tokeni" din interior. Ar trebui sa deduca asta indirect (ex. numarand
|
|
||||||
caractere din `session.messages()`), fara nicio garantie ca se potriveste cu tokenizarea
|
|
||||||
reala sau cu `autocompact` intern.
|
|
||||||
2. Nu exista niciun hook de tipul "inainte de compactare" / "sesiune se inchide" in lista —
|
|
||||||
deci nu exista un punct de agatare curat pentru "scrie handoff chiar inainte sa se piarda
|
|
||||||
contextul".
|
|
||||||
3. `/clear` (sau relansarea sesiunii) nu apare in `calls`-urile confirmate (`command.register`
|
|
||||||
exista, dar a inregistra o comanda noua nu e totuna cu a declansa `/clear` programatic din
|
|
||||||
interiorul unui hook).
|
|
||||||
4. Flag-ul e in continuare oprit implicit (`tengu_plugin_hooks_modules` default `!1`),
|
|
||||||
marcat "early access" chiar in textul din comanda `/plugin-types` insasi
|
|
||||||
("module 'claude-code', early access: it may change between releases") — deci si daca
|
|
||||||
API-ul de mai sus ar acoperi cazul, Anthropic il trateaza explicit ca nestabil.
|
|
||||||
5. **Nu am reusit sa validam empiric nimic din surface** (n-am putut genera `.d.ts` din cauza
|
|
||||||
limitarii de mediu descrise la sectiunea 1) — tot ce e mai sus e din citirea codului
|
|
||||||
minificat, nu din declaratii TypeScript generate si citite ca atare, cum cerea procedura.
|
|
||||||
|
|
||||||
Ce ar lipsi/ar trebui reincercat ca sa se stie sigur: rulare **interactiva** reala (TTY, nu
|
|
||||||
`-p`/SDK) cu `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1`, ca sa se vada daca `/plugin-types` chiar
|
|
||||||
scrie fisierele si daca `h8()` se evalueaza `true` in acel context — asta ar da acces la
|
|
||||||
declaratiile TypeScript reale si ar inchide toate necunoscutele de mai sus (in special conditiile
|
|
||||||
`qb()`/`Br("hooks")`/`zm()` neatribuite cu certitudine).
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
# Handoff — conturi 667/709 + analitice pe articol (catalog articole)
|
|
||||||
|
|
||||||
Data: 2026-09-17. Sesiune: analiza + reproducere + proiectare. **Zero cod de productie modificat.**
|
|
||||||
|
|
||||||
## 1. Cerinta
|
|
||||||
|
|
||||||
In facturarea din lista de preturi „catalog articole" (politica de stoc), un articol cu
|
|
||||||
`NOM_ARTICOLE.CONT = 667` (sau `709`) da eroarea „articolul nu are cont de venit".
|
|
||||||
Se cere:
|
|
||||||
- `667`/`709` considerate conturi valide (nu blocate);
|
|
||||||
- nota generata sa fie `667/709 = 4111` **cu suma negativa** (nu `4111 = <cont de venit>` standard);
|
|
||||||
- pe articol sa se poata completa si **conturile analitice** (`ASCD`/`ASCC`), nu doar debit/credit,
|
|
||||||
ca nota sa fie completa.
|
|
||||||
|
|
||||||
## 2. Decizii luate (NU se redeschid)
|
|
||||||
|
|
||||||
- **Fix = nota per-articol creata automat** (nu nota la nivel de politica, care s-ar aplica tuturor
|
|
||||||
articolelor din lista). Legatura: `CRM_POLITICI_PRET_ART.ID_NOTA`.
|
|
||||||
- **`SCC` (contul de client) vine din optiunea `RF_CONT_ART_FARA_POL`**, fallback `4111`.
|
|
||||||
- **Analiticele se salveaza in nota** (`NOTE_CONTABILE.ASCD/ASCC`), nu pe articol/politica.
|
|
||||||
- Semnul negativ **nu are nevoie de cod nou**: exista deja in `scrie_nota` (vezi §4).
|
|
||||||
- Se **adauga un overload** la `gaseste_sau_creeaza_nota_vanzare` (6 arg), ca sa nu rupa apelantii
|
|
||||||
vechi de 4 arg si sa nu fie nevoie de deploy sincronizat EXE/DB.
|
|
||||||
|
|
||||||
## 3. Cauza (dovada pe cod)
|
|
||||||
|
|
||||||
- Pre-check VFP la adaugarea liniei: `verifica_cont_venit_linie`
|
|
||||||
(`D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_comun.prg:842`), apelat din
|
|
||||||
`COMUN\clase\ofacturare.vc2:18737` si `:18972`. Rezolva
|
|
||||||
`nvl(case when a.id_pol=stoc and a.id_nota is null then cont_venit_articol_stoc(art) end, d.scc)`;
|
|
||||||
mesaj cand e NULL.
|
|
||||||
- Derivare fara politica: `deriva_cont_venit_fara_pol` (`ofacturare_comun.prg:815`) — pentru
|
|
||||||
`cont` 6xx intoarce `667` (dar e folosit ca SCC, directia e inversa).
|
|
||||||
- Deducere Oracle: `PACK_FACTURARE.cont_venit_articol_stoc`
|
|
||||||
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_16_04_COMUN_PACK_FACTURARE.sql:17794`)
|
|
||||||
trateaza doar `7xx` (il intoarce) si `2xx/3xx` (prin `CORESP_CONT_VENCHELT`); **`6xx` → NULL**.
|
|
||||||
- Garda Oracle: FACT-033 cand `V_SCC` e NULL (`ff_2026_09_16_04_...:7896-7901`).
|
|
||||||
- Nota articolului e deja folosita la facturare: `cursor_articol` ia `D.SCD/D.SCC/D.ASCD/D.ASCC`
|
|
||||||
(`ff_2026_09_16_04_...:7545-7546, 7864-7865, 7890-7893`), iar `scrie_nota` primeste
|
|
||||||
`V_ASCD/V_ASCC`.
|
|
||||||
|
|
||||||
## 4. Mecanism deja existent (nu se rescrie)
|
|
||||||
|
|
||||||
- `scrie_nota`, `ff_2026_09_16_04_...:12838` si `:12879-12887`: daca
|
|
||||||
`V_SCD IN ('667','267','2678','709')` pe factura → `V_SEMN := -1` (suma negativa).
|
|
||||||
- `scrie_tva`, `ff_2026_09_16_04_...:13064-13078`: comuta pe `4111/4427` pentru aceleasi conturi.
|
|
||||||
- Componenta noua in `PACK_PRETURI` (`ff_2026_09_16_05_COMUN_PACK_PRETURI.sql`):
|
|
||||||
`gaseste_sau_creeaza_nota_vanzare` (`:1305`), `salveaza_pret_nomenclator` (`:1261`),
|
|
||||||
`asigura_politica_stoc` (`:1391`), `seteaza_nota_politica_stoc` (`:1440`).
|
|
||||||
Azi `gaseste_sau_creeaza_nota_vanzare` scrie fortat `ascd/ascc` NULL (`:1339-1340, 1387`).
|
|
||||||
|
|
||||||
## 5. Plan de implementare (confirmat, NEINCEPUT)
|
|
||||||
|
|
||||||
### Pas 1 — Oracle: script nou `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_01_COMUN_PACK_PRETURI.sql`
|
|
||||||
- overload `gaseste_creeaza_nota_vanzare(tcScd, tcAscd, tcScc, tcAscc, tnIdUtil, tnIdNota OUT)`;
|
|
||||||
cautare pe toate 4 conturile; la creare scrie `ascd/ascc` reale.
|
|
||||||
- pastreaza si signatura veche de 4 arg `(tcScd, tcScc, tnIdUtil, tnIdNota OUT)`, sub acelasi nume
|
|
||||||
scurtat (numele original de 32 caractere nu compila pe Oracle 10/11).
|
|
||||||
- procedura noua `asigura_nota_articol_disc(V_ID_ARTICOL, V_ID_UTIL)`:
|
|
||||||
`cont ∈ (667,267,2678,709)` → `gaseste_sau_creeaza_nota_vanzare(cont, ascd, client, ascc, ...)`,
|
|
||||||
`client = nvl(pack_sesiune.getoptiunefirma('RF_CONT_ART_FARA_POL'),'4111')`;
|
|
||||||
`merge into crm_politici_pret_art set id_nota=... where id_pol=pack_facturare.nid_politica_stoc
|
|
||||||
and id_articol=... and id_nota is null` (nu suprascrie nota pusa de utilizator).
|
|
||||||
- apel in `salveaza_pret_nomenclator` cand `V_ID_NOTA IS NULL AND V_SCRIE_NOTA = 1`.
|
|
||||||
- backfill idempotent pentru randurile existente din politica de stoc.
|
|
||||||
- `exec pack_migrare.UpdateVersiune('ff_2026_09_17_01_COMUN_PACK_PRETURI'); commit;`
|
|
||||||
- compatibil Oracle 10.2. Vezi skill `roa-oracle-migration`.
|
|
||||||
|
|
||||||
### Pas 2 — VFP clasa `cus_pret_nomenclator` (`COMUN\programe\ofacturare_comun.prg`)
|
|
||||||
- clasa incepe la `:2366`; proprietati: `asigura_politica :2382`, `incarca :2400`,
|
|
||||||
`valideaza :2483`, `salveaza :2500`, `incarca_nota_politica :2553`, `salveaza_nota_politica :2575`.
|
|
||||||
- proprietati noi `cAscd`/`cAscc`; `incarca` si `incarca_nota_politica` le citesc din nota;
|
|
||||||
`valideaza` — analitic optional, dar cer sintetic daca exista analitic; `salveaza`/`salveaza_nota_politica`
|
|
||||||
le trimit in RPC.
|
|
||||||
- apeluri RPC de actualizat: `:2513` si `:2591` (`gaseste_creeaza_nota_vanzare`).
|
|
||||||
- NU se atinge `do_scrie_articole` (argumentul `cont_venit` ramane cum e, `:14279`/`:20246`).
|
|
||||||
|
|
||||||
### Pas 3 — VFP formular `COMUN\clase\onom_articole.vc2`, pagina Vanzare (Page3)
|
|
||||||
- `Clb_tx_scd` (`:1589`, label „Cont debitor") → `ADD ...Text1` legat la `Thisform.oPretNom.cAscd`.
|
|
||||||
- `Clb_tx_scc` (`:1571`, label „Cont creditor") → `Text1` legat la `cAscc`.
|
|
||||||
- tipar de copiat: `Clb_tx_cont.Text1` (`:1104`, ControlSource `porec.acont`).
|
|
||||||
- init `oPretNom` + ControlSource-uri: `:1931-1944`.
|
|
||||||
- write-back `.vc2 -> .vcx` obligatoriu (`txt2vcx`), cp1252. Vezi skill `roa-vfp-text-edit`.
|
|
||||||
|
|
||||||
### Pas 4 — Test headless pe `MARIUSM_AUTO`
|
|
||||||
- salveaza articol cont=667 fara acont (+ analitice), verifica `verifica_cont_venit_linie` = `[]`
|
|
||||||
si rezolvarea tip `cursor_articol` = `SCD=667/SCC=4111/ASCD/ASCC`.
|
|
||||||
- **tranzactie manuala obligatorie** (`SQLSETPROP(gnHandle,"Transactions",2)`), altfel scrierile
|
|
||||||
se commmit (vezi §7).
|
|
||||||
|
|
||||||
## 6. Fisiere create in aceasta sesiune (probe, NU productie)
|
|
||||||
|
|
||||||
In `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\`:
|
|
||||||
- `probe_cont_667.prg/.fxp` + `out\probe_cont_667.log`
|
|
||||||
- `probe_cont_667b.prg/.fxp` + `out\probe_cont_667b.log`
|
|
||||||
- `probe_cont_667c.prg/.fxp` + `out\probe_cont_667c.log`
|
|
||||||
- `probe_cont_667d.prg/.fxp` + `out\probe_cont_667d.log`
|
|
||||||
- `restore_pol41.prg/.fxp` + `out\restore_pol41.log`
|
|
||||||
|
|
||||||
Niciun fisier de productie (`.prg`/`.vc2`/`.vcx` din COMUN) nu a fost atins; niciun commit.
|
|
||||||
|
|
||||||
## 7. Capcane de mediu platite
|
|
||||||
|
|
||||||
- **`goExecutor`/ODBC e pe auto-commit implicit** — `UPDATE` fara
|
|
||||||
`SQLSETPROP(gnHandle,"Transactions",2)` se **commiteaza**. Am patit-o: `probe_cont_667b` a golit
|
|
||||||
`id_nota` al politicii 41; **restaurat** la `6` (`restore_pol41.log`). Orice scriere de proba:
|
|
||||||
`Transactions=2` + `ROLLBACK` la final.
|
|
||||||
- `vfp9.exe` = `C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe`.
|
|
||||||
- Precompilare izolata obligatorie inainte de rulare:
|
|
||||||
`powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1" -Prg "<cale.prg>"`
|
|
||||||
apoi `vfp9.exe -A "<cale.fxp>"` cu `WaitForExit`. `-A` porneste cu SAFETY ON → prima linie `SET SAFETY OFF`.
|
|
||||||
- Init mediu: `DO "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto_roafacturare.prg" WITH 'CENTRAL','MARIUSM_AUTO','ROMFASTSOFT'`, apoi `gnIdUtil = 8`.
|
|
||||||
- Log-uri: `...\facturare_unificat\out\*.log`.
|
|
||||||
|
|
||||||
## 8. Date utile din baza vie (MARIUSM_AUTO, citite 2026-09-17)
|
|
||||||
|
|
||||||
- `OPTIUNI.ROAFACTURARE.ID_POL_PRET_STOC = 41` („STOC PRODUSE"), `id_nota=6` (nota 6 = `4111/7015`).
|
|
||||||
Exista si `id_pol=43` „STOC PRODUSE", tot `id_nota=6`.
|
|
||||||
- Nota `2` = `DISCOUNT`, `SCD=667`, `SCC=4111` (politica 7 „DISCOUNT").
|
|
||||||
- 16 articole cu `cont=667` in politica 41, **fara nota per-articol** (nume: DISCOUNT,
|
|
||||||
VOUCHER DISCOUNT, DISCOUNT 19%, STORNARE DISCOUNT..., etc.).
|
|
||||||
- Singura nota per-articol existenta: `art=4294507172` („A1", `cont=371`) → `id_nota=7` (`4111/704`).
|
|
||||||
- `PLCONT` 2026 contine `667`, `709`, `267`, `2678`, `4111`, `704`, `707`.
|
|
||||||
- Politici fara nota: `32`, `33`, `41` (dupa restaur, 41 are din nou nota 6).
|
|
||||||
|
|
||||||
## 9. Interzis / atentie
|
|
||||||
|
|
||||||
- Nu `git pull` in COMUN; nu `roa_sync` pe tree murdar; commit **doar** tintit si **doar** cu
|
|
||||||
aprobarea lui Marius (skill `roa-git-svn-commit`).
|
|
||||||
- Nu edita `.vcx` direct; doar `.vc2` + `txt2vcx`.
|
|
||||||
- Nu lasa scrieri necommit-uite/auto-commit pe schema partajata `MARIUSM_AUTO`.
|
|
||||||
- Deploy: DB inainte de EXE (overload-ul vechi face ordinea toleranta, dar EXE nou + DB vechi ar
|
|
||||||
esua la apelul cu parametri noi).
|
|
||||||
|
|
||||||
## 10. Comanda de test rapida (dupa implementare)
|
|
||||||
|
|
||||||
```
|
|
||||||
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1" -Prg "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\<proba>.prg"
|
|
||||||
# apoi: Start-Process vfp9.exe -ArgumentList '-A', '"<cale.fxp>"' -Wait (NU '-A -T ...', nu porneste)
|
|
||||||
# citeste out\<proba>.log
|
|
||||||
```
|
|
||||||
|
|
||||||
## 11. STARE EXECUTIE (2026-09-17, orchestrator + subagenti)
|
|
||||||
|
|
||||||
Pas 1-4 EXECUTATE. Zero commit, zero publicare in UPD_DATABASE, zero modificari pe schema in afara
|
|
||||||
de scriptul de migrare aplicat pe MARIUSM_AUTO.
|
|
||||||
|
|
||||||
- **Pas 1** `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_01_COMUN_PACK_PRETURI.sql` (nou,
|
|
||||||
2441 linii): overload 6 arg `gaseste_creeaza_nota_vanzare` (cauta pe toate 4 conturile,
|
|
||||||
`nvl(trim(x),'~')` pentru 10.2), procedura `asigura_nota_articol_disc` (667/267/2678/709 ->
|
|
||||||
nota `<cont>=<RF_CONT_ART_FARA_POL|4111>`, doar pe rand fara id_nota), apel in
|
|
||||||
`salveaza_pret_nomenclator` cand `V_ID_NOTA IS NULL AND V_SCRIE_NOTA=1`, backfill idempotent in
|
|
||||||
politica de stoc, `UpdateVersiune('ff_2026_09_17_01_COMUN_PACK_PRETURI')`. **Aplicat pe
|
|
||||||
MARIUSM_AUTO** (sqlplus, `@`): `PACK_PRETURI` VALID, 0 erori; backfill a legat toate cele 16
|
|
||||||
articole 667 la nota 2 (DISCOUNT 667=4111); nota 2 are in continuare un singur rand.
|
|
||||||
|
|
||||||
**Compatibilitate Oracle 10.2 (corectie 17.09):** limita de 30 de caractere pe identificator.
|
|
||||||
In `ff_2026_09_16_05` singurul nume peste limita era `gaseste_sau_creeaza_nota_vanzare` = 32
|
|
||||||
caractere (pica pe 10g/11g cu PLS-00114; MARIUSM_AUTO e pe o versiune care permite 128, de-aia
|
|
||||||
compilase). Redenumit in `gaseste_creeaza_nota_vanzare` = 28. `asigura_nota_articol_discount`
|
|
||||||
era 29 (legal), dar a fost scurtat preventiv la `asigura_nota_articol_disc` = 25. Verificat:
|
|
||||||
0 identificatori >30 in tot pachetul; fara constructii 11g+ (listagg/regexp/pivot/continue/with
|
|
||||||
etc.). **Atentie la deploy:** numele vechi dispare din pachet, deci un EXE vechi care chema
|
|
||||||
`gaseste_sau_creeaza_nota_vanzare` se rupe - componenta e noua (16.09), dar DB-ul nou trebuie
|
|
||||||
livrat impreuna cu EXE-ul nou (nu mai exista toleranta la ordinea DB-inainte-de-EXE pentru acest
|
|
||||||
apel). Audit luna 2026-09: acelasi nume de 32 caractere exista si in `ff_2026_09_15_01`
|
|
||||||
(introducerea procedurii) si `ff_2026_09_16_05` - ambele corectate identic in surse; restul
|
|
||||||
scripturilor lunii sunt curate (singurele tokenuri >30 sunt numele de script din stringul
|
|
||||||
`UpdateVersiune` si un blob base64). **Atentie:** arhiva `database_20260901_20260930.zip`
|
|
||||||
exista deja (generata 2026-09-17 00:07) si contine versiunile vechi - corectia ajunge la clienti
|
|
||||||
doar dupa republicare.
|
|
||||||
- **Pas 2** `COMUN\programe\ofacturare_comun.prg` (clasa `cus_pret_nomenclator`, ~2366-2630):
|
|
||||||
proprietati `cAscd`/`cAscc`; `incarca`/`incarca_nota_politica` le citesc din nota; `valideaza`
|
|
||||||
refuza analitic fara sintetic; `salveaza` trimite RPC-ul cu 6 argumente; `salveaza_nota_politica`
|
|
||||||
primeste `tcAscd/tcAscc` optionali (apelantul din `ofacturare.vc2:24524` ramane pe 3 arg).
|
|
||||||
- **Pas 3** `COMUN\clase\onom_articole.vc2` (+ write-back `.vcx`/`.vct` cu txt2vcx): pe Page3,
|
|
||||||
`Clb_tx_scd.Text1` si `Clb_tx_scc.Text1` (MaxLength 4, Format `!k`), ControlSource
|
|
||||||
`Thisform.oPretNom.cAscd` / `cAscc` setat in Init. Round-trip `.vcx -> .vc2` identic, +2 obiecte.
|
|
||||||
- **Pas 4** trei probe E2E (toate verzi):
|
|
||||||
- `utile\Teste\facturare_unificat\probe_e2e_cont_667.prg` - salvare reala prin clasa + rezolvare
|
|
||||||
`cursor_articol` + fallback, in tranzactie manuala cu ROLLBACK: **24 PASS / 0 FAIL**.
|
|
||||||
- `probe_e2e_cont_667_ui.prg` - smoke UI pe `frm_catalog_articole_nou` Page3 (cele doua Text1
|
|
||||||
exista si sunt legate): **10 PASS / 0 FAIL**.
|
|
||||||
- `probe_e2e_cont_667_semn.sql` - `pack_facturare.scrie_nota` cu SCD=667/ASCD=11/SCC=4111/ASCC=12
|
|
||||||
-> `ACT_TEMP.SUMA=-84.03` (semn negativ), apoi ROLLBACK. Reziduuri verificate: 0.
|
|
||||||
- Loguri: `out\probe_e2e_cont_667.log`, `out\probe_e2e_cont_667_ui.log`.
|
|
||||||
|
|
||||||
### Raman de facut (deliberat, NU in aceasta executie)
|
|
||||||
- Commit tintit pe `COMUN` (`ofacturare_comun.prg`, `onom_articole.vc2`) + commit script SVN -
|
|
||||||
**doar cu aprobarea lui Marius** (skill `roa-git-svn-commit`).
|
|
||||||
- Import/publicare in `UPD_DATABASE` + regenerare arhiva lunara (NU s-a rulat; aprobat de Marius
|
|
||||||
doar "corecteaza sursele"). Comanda (scrie 3 scripturi + arhiva septembrie, face backup):
|
|
||||||
```
|
|
||||||
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\publicare_scripturi.ps1" -Luna 2026-09
|
|
||||||
```
|
|
||||||
`-DryRun` a confirmat: `FF_2026_09_15_01` MODIFICAT, `FF_2026_09_16_05` MODIFICAT,
|
|
||||||
`FF_2026_09_17_01` NOU, 26 scripturi in arhiva. DB inainte de EXE.
|
|
||||||
|
|
||||||
### Capcane noi platite
|
|
||||||
- `vfp9.exe -A -T "<fxp>"` NU porneste testul (proces mut, log gol). Forma corecta:
|
|
||||||
`Start-Process vfp9.exe -ArgumentList '-A','"<fxp>"'` (ca in `_precompile.ps1`).
|
|
||||||
- `scrie_nota` scrie in `ACT_TEMP` (toate coloanele NOT NULL au DEFAULT 0), deci e testabil direct
|
|
||||||
din sqlplus in tranzactie, fara sa treci prin formular; nu are FK-uri.
|
|
||||||
|
|
||||||
## 12. PREDARE catre sesiunea urmatoare (stare la 2026-09-17, ~10:30)
|
|
||||||
|
|
||||||
### Livrabile si stare pe disc
|
|
||||||
| Ce | Cale | Stare |
|
|
||||||
|---|---|---|
|
|
||||||
| Migrare noua | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_01_COMUN_PACK_PRETURI.sql` | scris + **aplicat pe MARIUSM_AUTO**; `PACK_PRETURI` VALID, 0 erori, 0 identificatori >30 |
|
|
||||||
| Migrari corectate | `...\ff_2026_09_15_01_COMUN_PACK_PRETURI.sql`, `...\ff_2026_09_16_05_COMUN_PACK_PRETURI.sql` | rename aplicat in sursa; **NU aplicate** (17_01 le inlocuieste), **NU publicate** |
|
|
||||||
| Clasa VFP | `COMUN\programe\ofacturare_comun.prg` (clasa `cus_pret_nomenclator`, ~2366-2630) | `cAscd/cAscc`, RPC 6 arg la `:2530` si `:2616`; nemodificat de altcineva |
|
|
||||||
| Formular VFP | `COMUN\clase\onom_articole.vc2` (+ `.vcx`/`.vct` regenerate cu txt2vcx) | `Clb_tx_scd.Text1` si `Clb_tx_scc.Text1` (Page3), ControlSource in Init `:1963-1964` |
|
|
||||||
| Probe E2E | `COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667.prg`, `probe_e2e_cont_667_ui.prg`, `probe_e2e_cont_667_semn.sql` | rulate: **24/24**, **10/10**, `SUMA=-84.03` |
|
|
||||||
| Backup formular | `COMUN\clase\onom_articole.vc2.pre_runda3_cont_analitic.bak` | creat de subagent, git-ignorat |
|
|
||||||
|
|
||||||
Nume vehiculate dupa corectia Oracle 10: `pack_preturi.gaseste_creeaza_nota_vanzare` (28) si
|
|
||||||
`pack_preturi.asigura_nota_articol_disc` (25). Numele vechi (`gaseste_sau_creeaza_nota_vanzare`
|
|
||||||
= 32, `asigura_nota_articol_discount` = 29) **nu mai exista** nicaieri in cod.
|
|
||||||
|
|
||||||
### Stare date MARIUSM_AUTO (commisa, nu de test)
|
|
||||||
- `OPTIUNI.ROAFACTURARE.ID_POL_PRET_STOC = 41`; politica 41 are `id_nota=6`; nota `2` = DISCOUNT
|
|
||||||
`667=4111`.
|
|
||||||
- 16 articole cu `cont 667/709` in politica 41 → toate au acum `id_nota=2` (backfill-ul din 17_01,
|
|
||||||
**commis** de script). Zero articole fara nota.
|
|
||||||
- Testele E2E nu au lasat reziduuri (verificat: 0 randuri `ACT_TEMP`/note noi).
|
|
||||||
- Conexiune: `sqlplus MARIUSM_AUTO/ROMFASTSOFT@ROA_CENTRAL`, `TNS_ADMIN=D:\ROA\instantclient_19_18`.
|
|
||||||
|
|
||||||
### Ce NU s-a facut (blocat pe aprobare / decizie)
|
|
||||||
1. **Niciun commit** (nici `ROAFACTURARE`, nici `COMUN`). Tree murdar: `COMUN` are `M` pe
|
|
||||||
`clase/onom_articole.vc2` si `programe/ofacturare_comun.prg`, plus probe E2E untracked; binarul
|
|
||||||
`.vcx`/`.vct` e git-ignorat (by design). `ROAFACTURARE` are untracked doar handoff-ul.
|
|
||||||
2. **Nicio publicare**: `UPD_DATABASE` + `database_20260901_20260930.zip` (generat 2026-09-17
|
|
||||||
00:07) au inca versiunile vechi (nume de 32 caractere) → pe Oracle 10/11 update-ul lunii pica.
|
|
||||||
Comanda de publicare (utilizatorul a ales sa NU o rulez acum):
|
|
||||||
`powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\publicare_scripturi.ps1" -Luna 2026-09`
|
|
||||||
`-DryRun` dovedit: 15_01 MODIFICAT, 16_05 MODIFICAT, 17_01 NOU, 26 scripturi in arhiva.
|
|
||||||
3. **Celelalte produse nu au fost atinse.** Numele vechi traieste inca in working copy-urile COMUN
|
|
||||||
ale produselor: `ROAACNPRO, ROAAUTO, ROACONT, ROACONTRACTE, ROADEF, ROAGEST, ROAIMOB,
|
|
||||||
ROAPRETURI, ROAREGISTRATURA` (`<PRODUS>\COMUN\programe\ofacturare_comun.prg:2513` si `:2591`).
|
|
||||||
Fiecare `<PRODUS>\COMUN` e working copy SVN separat (`.svn` + `.git`). Propagarea se face prin
|
|
||||||
commit SVN pe COMUN + `roa_sync` in fiecare produs + **rebuild EXE**.
|
|
||||||
4. **Decizie deschisa**: wrapper cu numele vechi de 32 caractere creat conditionat doar pe
|
|
||||||
Oracle ≥12.2 (`$IF DBMS_DB_VERSION.VERSION >= 12 $THEN` + `EXECUTE IMMEDIATE`), pentru zero
|
|
||||||
breakage la clientii ≥12.2 cu EXE vechi. Neconfirmat; daca nu se doreste, ramane doar rename.
|
|
||||||
|
|
||||||
### Ordinea recomandata pentru sesiunea urmatoare
|
|
||||||
1. Marius decide: wrapper conditionat da/nu.
|
|
||||||
2. Commit tintit pe `COMUN` (`ofacturare_comun.prg`, `onom_articole.vc2`) + commit SVN COMUN
|
|
||||||
(skill `roa-git-svn-commit`; NU `git pull` in COMUN, NU `roa_sync` pe tree murdar).
|
|
||||||
3. Publicare septembrie (`publicare_scripturi.ps1 -Luna 2026-09`) dupa aprobare.
|
|
||||||
4. Sync COMUN in celelalte produse + rebuild EXE; livrare DB + EXE **impreuna** (numele vechi a
|
|
||||||
disparut, deci EXE vechi = rupere la salvare nomenclator).
|
|
||||||
|
|
||||||
### Comenzi de re-rulare a testelor
|
|
||||||
```powershell
|
|
||||||
# 1) precompilare izolata
|
|
||||||
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1" -Prg "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667.prg"
|
|
||||||
# 2) rulare (ATENTIE: NU `-A -T`, porneste mut) + asteapta terminarea + citeste logul
|
|
||||||
$vfp='C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
|
|
||||||
$p=Start-Process -FilePath $vfp -ArgumentList @('-A','"D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667.fxp"') -PassThru
|
|
||||||
$p.WaitForExit(240000)
|
|
||||||
Get-Content "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\out\probe_e2e_cont_667.log"
|
|
||||||
# idem pentru probe_e2e_cont_667_ui.prg (log out\probe_e2e_cont_667_ui.log)
|
|
||||||
# 3) dovada semnului (Oracle, cu ROLLBACK)
|
|
||||||
sqlplus MARIUSM_AUTO/ROMFASTSOFT@ROA_CENTRAL "@D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667_semn.sql"
|
|
||||||
```
|
|
||||||
Clasele/procedurile se incarca prin `test_init_env_auto_roafacturare.prg WITH 'CENTRAL',
|
|
||||||
'MARIUSM_AUTO','ROMFASTSOFT'` apoi `gnIdUtil=8`. Tranzactie manuala:
|
|
||||||
`SQLSETPROP(gnHandle,"Transactions",2)` + `SQLROLLBACK` la final (altfel se commiteaza).
|
|
||||||
|
|
||||||
### Interzis
|
|
||||||
- `git pull` in COMUN; `roa_sync` pe tree murdar; commit fara aprobarea lui Marius.
|
|
||||||
- editare `.vcx` direct (doar `.vc2` + `txt2vcx`, cp1252).
|
|
||||||
- scrieri necommise sau fara rollback pe `MARIUSM_AUTO`.
|
|
||||||
- identificatori >30 caractere in orice obiect Oracle nou (10.2 = limita).
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# Handoff — id_set unic + skilluri rutare (2026-09-17, ~13:30)
|
|
||||||
|
|
||||||
Stare pentru sesiunea urmatoare. Zero lucrari ramase in stare periculoasa pe disc (doar fisiere necomise, listate mai jos).
|
|
||||||
|
|
||||||
## 1. Livrabile terminate si comise
|
|
||||||
|
|
||||||
| Ce | Unde | Stare |
|
|
||||||
|---|---|---|
|
|
||||||
| Migrare id_set unic | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_03_COMUN_ID_SET_UNIC.sql` | SVN r18155, publicat (UPD_DATABASE id 17609 + arhiva lunara) |
|
|
||||||
| Doc SSH | `COMUN\docs\conexiuni-tunel-ssh-odbc.md` | comis de Marius (r18154) |
|
|
||||||
| Skill `roa-domeniu-rutare` | `COMUN\skills\roa-domeniu-rutare\SKILL.md` | SVN r18156 + symlink + backstop |
|
|
||||||
| Skill `roa-actualizare-doc` | `COMUN\skills\roa-actualizare-doc\SKILL.md` | SVN r18156 + symlink + backstop |
|
|
||||||
| Backstop | `COMUN\utile\backstop_skills.txt` + `ROAFACTURARE\AGENTS.md` | r18156 / r18157 |
|
|
||||||
|
|
||||||
## 2. NEFINALIZAT / necomis
|
|
||||||
|
|
||||||
- `COMUN\utile\roa-sql.ps1` — **NOU, necomis** (helper determinist sqlplus; parola citita din `COMUN\docs\local\oracle.md`, NU hardcodata). Trebuie `svn add` + commit + roa_sync.
|
|
||||||
- **Îmbunătățirea `roa-domeniu-rutare`** cu sectiunea "Comenzi directe" — NEÎNCEPUT. Feedback Marius: skillurile trebuie sa aiba **comenzi directe / apelari deterministe de scripturi**, nu doar pointeri.
|
|
||||||
|
|
||||||
## 3. Feedback de urmat (de la Marius, sesiunea aceasta)
|
|
||||||
|
|
||||||
1. **Skilluri cu comenzi directe**: adauga in `roa-domeniu-rutare` comenzile exacte (sqlplus connect dev+productie, interogari de dictionar `all_source`/`all_triggers`/`all_sequences`/`all_tab_columns`, grep in `SCRIPTURI_CLAR`), nu doar harta descriptiva.
|
|
||||||
2. **Uita-te la pluginul gstack** din Claude Code (`C:\Users\mmari\.claude\skills\gstack\`) ca sa vezi hook-ul de context: injecteaza dimensiunea contextului si se opreste/salveaza la ~250k. De implementat echivalent (eu am ajuns la 341k fara sa opresc — nerespectand regula 250k din `reguli_lucru.md` pct. 0/9).
|
|
||||||
3. **Subagenti DeepSeek Flash v4.1** pentru sarcini de executie (mai ieftini), ca in instructiunile din docs — nu am urmat instructiunea, am facut interogarile in sesiunea principala.
|
|
||||||
4. **Nu hardcoda parole** in scripturi — foloseste `COMUN\docs\local\oracle.md` (NEVERSIONAT in git).
|
|
||||||
|
|
||||||
## 4. Comenzi de reluat (deterministe)
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# SQL pe dev (parola din local/oracle.md, automat)
|
|
||||||
powershell -File COMUN\utile\roa-sql.ps1 -Schema MARIUSM_AUTO -Sql "select user from dual"
|
|
||||||
# SQL pe productie (alias TNS)
|
|
||||||
powershell -File COMUN\utile\roa-sql.ps1 -Con "ROMCONSTRUCT/<parola>@ROA_ROMCONSTRUCT" -Sql "..."
|
|
||||||
|
|
||||||
# SSH ROMCONSTRUCT (cheie publica, port 22122, NU 22)
|
|
||||||
ssh -N -o BatchMode=yes -L 1521:127.0.0.1:1521 romfast@82.76.217.177 -p 22122
|
|
||||||
# ROMFAST e intern, fara tunel: 10.0.20.36:1521
|
|
||||||
|
|
||||||
# commit roa-sql.ps1 (la aprobarea lui Marius)
|
|
||||||
svn add COMUN\utile\roa-sql.ps1 ; svn commit ... ; roa_sync.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. Stiut deja (nu se reia)
|
|
||||||
|
|
||||||
- **Spatiul ID_SET**: doua lucruri distincte — set de contare `XSETS.ID_SET = ACT.ID_SET` (25000+tip, in `tipuri_documente_facturare.md`) vs nota contabila `NOTE_CONTABILE.ID_SET = CRM_NOTE_VANZARI.ID_SET`. Generatoare: `SEQ_CRM_NOTE_ID_SET` + `SEQ_XSETS` (>=1M). Fix: un singur generator SEQ_XSETS, coloane NUMBER(6)->NUMBER(10). Vezi `roa-domeniu-rutare` + `ff_2026_09_17_03`.
|
|
||||||
- **Lantul notei vanzare**: `CRM_POLITICI_PRET_ART.ID_NOTA -> CRM_POLITICI_PRETURI.ID_NOTA -> CRM_NOTE_VANZARI.ID_NOTA`; `CRM_NOTE_VANZARI.ID_SET -> NOTE_CONTABILE.ID_SET`.
|
|
||||||
- **Coloane ID_SET**: majoritatea schemelor erau NUMBER(6) (max 999999), de-aia bump-ul 2007 a mers pe +200000.
|
|
||||||
- **Capcana**: `sterge_nota_vanzari` face `delete from note_contabile where id_set`; index NONUNIQUE -> coliziune = stergere silentioasa.
|
|
||||||
|
|
||||||
## 6. Tuneluri/SSH — NU lasa deschise
|
|
||||||
|
|
||||||
Toate inchise la finalul sesiunii. Re-deschiderea: comanda din pct. 4.
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
# Handoff — idempotenta script 03 + ColumnNeedModify (2026-09-17, ~15:00)
|
|
||||||
|
|
||||||
Stare pentru sesiunea urmatoare. Nimic in stare periculoasa pe disc. Fisierele necomise sunt listate la pct. 4.
|
|
||||||
|
|
||||||
## 1. Livrabile terminate
|
|
||||||
|
|
||||||
| Ce | Unde | Stare |
|
|
||||||
|---|---|---|
|
|
||||||
| Script 03 idempotent | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_03_COMUN_ID_SET_UNIC.sql` | editat in loc, PUBLICAT (UPD_DATABASE id 17609 reactualizat) |
|
|
||||||
| Script 04 nou (PACK_MIGRARE + functia ColumnNeedModify) | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_04_COMUN_PACK_MIGRARE.sql` | nou, PUBLICAT (id 17610) |
|
|
||||||
| Arhiva lunara | `Y:\ROAUPDATE\_UPDATE\` | regenerata (30 scripturi), verificata octet cu octet |
|
|
||||||
|
|
||||||
Ambele scripturi: ASCII (0 octeti > 127), CRLF, fara BOM, se termina cu `UpdateVersiune` + `commit;`.
|
|
||||||
|
|
||||||
## 2. Ce face scriptul 03 (acum idempotent)
|
|
||||||
|
|
||||||
- `alter table ... modify (id_set number(10))` era direct; acum e in `declare` cu guard:
|
|
||||||
`pack_migrare.ColumnExist(...) = 1` AND `data_type='NUMBER' AND data_precision < 10`.
|
|
||||||
Pe dev coloanele sunt deja NUMBER(10) (data_precision=10), deci guard-ul sare corect.
|
|
||||||
- Trigger-ul e `create or replace` (idempotent); renumerotarea `where id_set < 1000000` e no-op la a doua rulare.
|
|
||||||
|
|
||||||
## 3. Ce face scriptul 04 (noua functie)
|
|
||||||
|
|
||||||
`PACK_MIGRARE.ColumnNeedModify(tcTableName, tcColumnName, tcDataType, tnPrecision, tnScale, tcNullable)`:
|
|
||||||
intoarce 1 daca coloana NU are definitia ceruta (trebuie ALTER MODIFY), 0 altfel. Orice parametru NULL = aspectul nu se verifica.
|
|
||||||
|
|
||||||
Pachetul complet a fost reprodus din DB-ul live (are ramura `when 'RF' THEN` din UpdateVersiune, pe care `.pck`-ul stale o pierduse) + functia noua. Baza: scriptul 2014 `ff_2014_08_18_01_COMUN_PACK_MIGRARE.sql` == DB live (verificat cu diff).
|
|
||||||
|
|
||||||
## 4. APROBAT dar NEEXECUTAT (Marius a aprobat in sesiune)
|
|
||||||
|
|
||||||
1. **A**: in `COMUN\skills\roa-oracle-migration\SKILL.md` — inlocuieste criteriul CRLF buguit
|
|
||||||
`($b -join ',').Contains('13,10')` (fals pozitiv: byte 113='q' + 10 => "113,10" contine "13,10")
|
|
||||||
cu regex-ul `(?<!\r)\n` (acelasi pe care-l foloseste `publicare_scripturi.ps1` linia 543).
|
|
||||||
2. **B**: capcana noua in `roa-oracle-migration\SKILL.md` + `scripturi-migrare-db.md`: fisierele `.sql`
|
|
||||||
noi ies cu LF din `write`/`edit`; converteste CRLF inainte de publicare. One-liner:
|
|
||||||
`$t = [IO.File]::ReadAllText($p); $t = $t -replace "`r`n","`n" -replace "`n","`r`n"; [IO.File]::WriteAllText($p, $t, (New-Object System.Text.ASCIIEncoding))`
|
|
||||||
3. **C**: in `COMUN\docs\oracle_export.md` — un rand: `.pck` poate fi stale (ex. PACK_MIGRARE fara ramura
|
|
||||||
RF); verifica echivalenta fata de DB live inainte de a-l folosi.
|
|
||||||
4. **Sterge `.pck`-urile din `COMUN\docs\`**: `PACK_MIGRARE.pck`, `PACK_UPDATE.pck`, `PACK_DIAG_SPATIU.pck`.
|
|
||||||
Marius: foloseste doar ultimele versiuni din SCRIPTURI_CLAR, nu exporturi `.pck` din docs.
|
|
||||||
ATENTIE: `oracle_export.md` si `roa-domeniu-rutare\SKILL.md` trimit la `*.pck` — actualizeaza-le.
|
|
||||||
|
|
||||||
## 5. NEFINALIZAT / necomis (din sesiunile trecute)
|
|
||||||
|
|
||||||
- `COMUN\utile\roa-sql.ps1` — NOU, necomis (svn add + commit + roa_sync).
|
|
||||||
- AGENTS.md: sectiunea "Stil de interactiune" adaugata — necomisa.
|
|
||||||
- `COMUN\docs\reguli_lucru.md`: regula 14 (intreaba cu optiuni in loc de investigatie) adaugata — necomisa.
|
|
||||||
- `roa-domeniu-rutare\SKILL.md`: sectiunea "Comenzi directe" (comenzi sqlplus deterministe) — NEINCEPUTA.
|
|
||||||
- Scripturile 03 (M) si 04 (A) din `D:\ROA\DATABASE` — de comis in SVN (`svn add` pt 04).
|
|
||||||
|
|
||||||
## 6. Comenzi de reluat
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# publicare (deja facuta, doar daca re-rulezi):
|
|
||||||
powershell -File D:\ROA\ROAFACTURARE\COMUN\utile\publicare_scripturi.ps1 -DryRun
|
|
||||||
|
|
||||||
# SQL pe dev:
|
|
||||||
powershell -File COMUN\utile\roa-sql.ps1 -Schema MARIUSM_AUTO -Sql "select user from dual"
|
|
||||||
|
|
||||||
# commit SVN in D:\ROA\DATABASE (doar 03 + 04, tintit):
|
|
||||||
# svn commit 2026\09\ff_2026_09_17_03_COMUN_ID_SET_UNIC.sql 2026\09\ff_2026_09_17_04_COMUN_PACK_MIGRARE.sql -F <mesaj>
|
|
||||||
# commit COMUN: vezi skill roa-git-svn-commit (doar fisierele aprobate, apoi roa_sync.bat)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 7. Capcane platite (NU se repeta)
|
|
||||||
|
|
||||||
- Verificarea CRLF cu `($b -join ',').Contains('13,10')` e FALS-POZITIVA. Foloseste `(?<!\r)\n`.
|
|
||||||
- Tool-ul `write` scrie `.sql` cu LF, nu CRLF — converteste.
|
|
||||||
- `.pck`-urile din `COMUN\docs` pot fi stale fata de DB live (PACK_MIGRARE pierduse ramura RF).
|
|
||||||
- `roa-actualizare-doc` / `roa-domeniu-rutare` NU sunt in lista mea de skill-uri; citeste-le cu `read`, nu invoca `skill`.
|
|
||||||
- Greseli de disciplina: n-am folosit subagenti (regula 6) si am depasit pragul de 250k (REGULA ZERO).
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
# handoff_inject.ps1 - reinjectare handoff la SessionStart
|
|
||||||
|
|
||||||
Fisier: `D:\ROA\ROAGEST\COMUN\utile\handoff_inject.ps1`
|
|
||||||
Branch: `qa-factura-s0` (repo `D:\ROA\ROAGEST\COMUN`)
|
|
||||||
Commit: `56d1764ed8c9509acef8d3683583aab03b04ae97` — "adauga handoff_inject.ps1 - reinjectare handoff pe SessionStart"
|
|
||||||
|
|
||||||
## Ce face
|
|
||||||
|
|
||||||
Hook `SessionStart`. Citeste JSON-ul primit pe stdin (`source`, `cwd`). Daca `source` e in lista
|
|
||||||
acceptata (implicit `compact`, `clear`, `resume`) si `<cwd>\docs\handoff_activ.md` (sau calea data
|
|
||||||
prin `-CaleHandoff`) exista, emite pe stdout:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"HANDOFF RELUAT automat din <cale> (pornire: <source>). ...\n\n<continut>"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Tace (exit 0, fara iesire) daca `source` nu e in lista, sau daca fisierul nu exista — nu inventeaza,
|
|
||||||
nu creeaza nimic. Peste ~9000 de caractere, nu trunchiaza tacut: taie continutul, adauga un rand
|
|
||||||
final `[TRUNCHIAT - ... Citeste fisierul complet cu Read: <cale>]` si il include in output.
|
|
||||||
|
|
||||||
Parametri: `-CaleHandoff <cale>` (optional), `-Surse <lista>` (implicit `compact,clear,resume`).
|
|
||||||
|
|
||||||
## Rezultatul probelor
|
|
||||||
|
|
||||||
1. `source=startup`, fisier existent -> `exit=0`, `out=[]` (nicio iesire). Confirmat.
|
|
||||||
2. `source=compact`, fisier inexistent -> `exit=0`, `out=[]`. Confirmat.
|
|
||||||
3. `source=compact`, continut cu ghilimele duble, apostrof, diacritice (`șăâîț`) -> JSON valid,
|
|
||||||
`hookEventName=SessionStart`, iar `additionalContext` dupa `ConvertFrom-Json` contine exact
|
|
||||||
continutul original (`CONTINE ORIGINAL: True`, verificat prin `.Contains()`, nu din ochi).
|
|
||||||
4. `source=clear`, fisier de 10350 caractere -> JSON valid, `additionalContext` taiat la 9403
|
|
||||||
caractere, se termina cu randul `[TRUNCHIAT - handoff-ul are 10350 caractere, peste limita de
|
|
||||||
9000. Citeste fisierul complet cu Read: <cale>]`, calea completa prezenta in text.
|
|
||||||
|
|
||||||
Probele au rulat in `$env:TEMP\hoi_probe\p1..p4`, sterse dupa rulare.
|
|
||||||
|
|
||||||
## Configurare settings.json (de aplicat de orchestrator)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hooks": {
|
|
||||||
"SessionStart": [
|
|
||||||
{
|
|
||||||
"matcher": "clear,compact,resume",
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "powershell -ExecutionPolicy Bypass -File D:\\ROA\\ROAGEST\\COMUN\\utile\\handoff_inject.ps1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Nu am atins `C:\Users\mmari\.claude\settings.json` — cablarea ramane la orchestrator, conform
|
|
||||||
interdictiei primite.
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
# Handoff: actualizare blocata pe ROMFAST 10.0.20.36 (16.09.2026, seara)
|
|
||||||
|
|
||||||
Stare, nu concluzie finala. Nimic modificat pe productie: exclusiv `SELECT`-uri.
|
|
||||||
|
|
||||||
## Ce e DOVEDIT
|
|
||||||
|
|
||||||
1. **Nu e problema de certificat.** `ORA-29024` vine de pe ramura a doua a lui
|
|
||||||
`CONTAFIN_ORACLE.PACK_UTILS.URL2Clob`. Cod instalat, citit din `all_source`:
|
|
||||||
- linia 285: `IF lcPowerShellDownload = '1' THEN` -> ramura 1 (curl extern);
|
|
||||||
- linia 291: `llDownloaded := DownloadFileOS(tcURL, lcDir, lcFileName);` cu `lcDir := 'DMPDIR'`;
|
|
||||||
- linia 293-301: daca a mers, citeste si `RETURN`;
|
|
||||||
- linia 305: `l_clob := HTTPURITYPE.createuri(tcURL).getclob();` <- ramura 2, cade aici;
|
|
||||||
- linia 307-313: `WHEN OTHERS` -> cleanup -> `RAISE`.
|
|
||||||
Stiva reala (`PACK_UTILS` 313 si 305) se potriveste exact. Deci ORA-29024 = ramura 1 nu a produs fisierul.
|
|
||||||
|
|
||||||
2. **Ramura 1 E activata pe ROMFAST.** `CONTAFIN_ORACLE.SERVER_INFO`:
|
|
||||||
`POWERSHELLDOWNLOAD = 1`, `POWERSHELLTIMEOUT = 30`,
|
|
||||||
`POWERSHELLPATH = C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`,
|
|
||||||
`ROAUPDATEPATH = D:\ROAUPDATE`. **Nu exista rand `CURLPATH`**, dar codul are fallback pe
|
|
||||||
`C:\Windows\System32\curl.exe` (`PACK_UTILS` body 690-692), deci absenta lui nu e fatala.
|
|
||||||
|
|
||||||
3. **Esecul dureaza exact cat timeout-ul.** `all_scheduler_job_run_details`, `UPDATEROA_JOB`:
|
|
||||||
- 21:17:51 SUCCEEDED (error# 0)
|
|
||||||
- 22:10:36 FAILED, error# 29273 — pornire 22:10:05 => **31 secunde**
|
|
||||||
- 22:27:42 FAILED, error# 29273 — pornire 22:27:12 => **30 secunde**
|
|
||||||
`POWERSHELLTIMEOUT = 30`. Bucla de polling (`PACK_UTILS` body 709-713) a mers pana la capat si
|
|
||||||
`PACK_UTILS_FILE.FileExists` a intors fals. Fisierul **nu a aparut niciodata**, nu a aparut tarziu.
|
|
||||||
|
|
||||||
4. **Corelatia temporala cu patch-ul de azi.** `all_objects`, owner `CONTAFIN_ORACLE`:
|
|
||||||
`PACK_UTILS` (spec+body), `PACK_UTILS_FILE` (spec+body) `last_ddl_time = 16.09 21:17:51`;
|
|
||||||
`PACK_UPDATE` (spec+body) `21:17:52`. Toate VALID.
|
|
||||||
Rularea de la 21:17:30-21:20:14 e cea care a **instalat** noul cod; descarcarea din ea s-a facut
|
|
||||||
inainte de recompilare, deci **cu codul vechi**. Toate rularile de DUPA recompilare (22:10, 22:27)
|
|
||||||
au esuat. => **noul `PACK_UTILS` nu a descarcat niciodata nimic cu succes pe ROMFAST.**
|
|
||||||
|
|
||||||
5. **Serverul de update raspunde.** Masurat de pe statia de lucru, 16.09.2026:
|
|
||||||
`https://roa.romfast.ro/contafinupdate/roaupdate/` -> HTTP 200 in 0.127s.
|
|
||||||
`http://10.0.20.122/` -> 404 in 0.0016s de la `Microsoft-HTTPAPI/2.0` (normal, situl real e pe
|
|
||||||
portul 81 cu ruta `/contafinupdate/...`; 404 de la `http.sys` NU inseamna server picat).
|
|
||||||
|
|
||||||
6. **Versiuni** (`SERVER_INFO.VERSIUNE_*`): majoritatea schemelor la `202609150001` (15.09);
|
|
||||||
`CONTAFIN_ORACLE` la `202609160003`, `ROMFAST` la `202609160005`. Actualizarea de azi a inceput si
|
|
||||||
s-a oprit pe drum.
|
|
||||||
|
|
||||||
7. **`UPD_LOG` se goleste la fiecare rulare** (`last_ddl_time` 22:27:02 pe tabela si indecsi).
|
|
||||||
Singurul rand ramas: `16.09 22:34:38 ACTUALIZARE INCHEIATA MANUAL`. Nu e sursa de istoric.
|
|
||||||
|
|
||||||
8. **`sys.ExecuteScriptOS`** (`all_source`, owner SYS, `last_ddl_time` 05.02.2026, deci **neatins azi**)
|
|
||||||
creeaza un job `exec_ps_<8hex>` de tip `executable` cu argumentele
|
|
||||||
`-ExecutionPolicy Bypass -File <script>` si il `ENABLE`-aza. Joburile sunt SYS-owned, deci
|
|
||||||
`CONTAFIN_ORACLE` **nu le vede** in `all_scheduler_job_run_details` (interogat: zero randuri
|
|
||||||
`EXEC_PS%`). Aici e gaura de vizibilitate.
|
|
||||||
|
|
||||||
## Ce NU e dovedit (ipoteze deschise, in ordinea probabilitatii)
|
|
||||||
|
|
||||||
- **A. Patch-ul de azi a rupt ramura 1.** Cel mai probabil, pe baza punctului 4. Ce s-a schimbat azi
|
|
||||||
(antet `PACK_UTILS` body linia 4): „DownloadFileOS descarca in .part si redenumeste la final, nume
|
|
||||||
unic de script ps". Scriptul generat (body 694-699):
|
|
||||||
`& "<curl>" -k -o "<dest>.part" "<url>"` + `if ($LASTEXITCODE -eq 0) { Move-Item ... } else { Remove-Item ... }`.
|
|
||||||
Scris cu `pack_utils_file.clob2fileX` (701), citit cu `File2ClobX` (295) — ambele functii noi.
|
|
||||||
**Nedeterminat care linie anume pica.** Nu am comparat inca versiunea instalata cu cea anterioara.
|
|
||||||
- **B. PowerShell/curl nu ruleaza deloc pe server** (drepturi pe jobul extern, `C:\DMPDIR` nescriibil,
|
|
||||||
curl absent). Ar da acelasi simptom si ar fi independent de patch — dar atunci ar fi picat si
|
|
||||||
inainte de 21:17, ceea ce contrazice punctul 3. Slaba, dar nu exclusa.
|
|
||||||
- **C. Legatura cu incidentul conpress de azi.** Capatul departat al db link-ului `DBL_ROMFAST2` este
|
|
||||||
chiar 10.0.20.36; la conpress s-a omorat sesiunea de pe partea apropiata (ROA_CENTRAL) fara sa se
|
|
||||||
priveasca vreodata partea departata. **Neverificata.**
|
|
||||||
|
|
||||||
## Urmatorul pas care departajeaza (nerulat)
|
|
||||||
|
|
||||||
Pe serverul 10.0.20.36, in afara Oracle: ruleaza manual scriptul pe care il genereaza codul si vezi
|
|
||||||
codul de iesire al lui `curl.exe` — el e singura informatie pe care lantul o pierde complet
|
|
||||||
(`$LASTEXITCODE` decide doar redenumirea, valoarea nu se logheaza nicaieri). Si verifica ce ramane in
|
|
||||||
`C:\DMPDIR` dupa o rulare esuata: daca ramane un `.part`, curl a mers si a picat `Move-Item`; daca nu
|
|
||||||
ramane nimic, curl a esuat sau PowerShell n-a pornit.
|
|
||||||
Alternativ, din Oracle ca SYS: `dba_scheduler_job_run_details` pentru joburile `EXEC_PS%` — acolo
|
|
||||||
sunt esecurile jobului extern, invizibile din `CONTAFIN_ORACLE`.
|
|
||||||
|
|
||||||
## Stare periculoasa / de curatat
|
|
||||||
|
|
||||||
- **Tunelul Bitvise e RIDICAT** — proces `stnlc`, profil `D:\vm303-profile\romfast.tlp`,
|
|
||||||
forwardari active pe `127.0.0.1:1521` (ROMFAST, `SID=ROA`), 1522, 1523 si altele.
|
|
||||||
**De inchis** cand nu mai e nevoie:
|
|
||||||
`Stop-Process -Id (Get-NetTCPConnection -LocalPort 1521 -State Listen).OwningProcess`.
|
|
||||||
- Nimic modificat pe productie. Doar `SELECT`-uri. Niciun script relansat, niciun job atins.
|
|
||||||
- `SERVER_INFO` contine parole (`PASSWORD_SYS`, `PASSWORD_CONTAFIN_ORACLE` base64+gzip,
|
|
||||||
`EMAIL_PASSWORD` in clar). **Nu le pune in niciun raport, doc sau commit.**
|
|
||||||
- Modificare necomisa in alt repo: `COMUN/docs/depanare-server-update-roa.md`, +60 linii
|
|
||||||
(doua sectiuni noi, doar insertii). Neverificata linie cu linie de Marius.
|
|
||||||
- Lane opencode `src` inca rula la scrierea acestui fisier; livrabilul lui ar fi
|
|
||||||
`docs/raport_src.md` (diff conceptual intre versiunea de azi a lui PACK_UTILS si cea anterioara).
|
|
||||||
|
|
||||||
## Cum se reia conexiunea
|
|
||||||
|
|
||||||
```
|
|
||||||
"C:\Program Files (x86)\Bitvise SSH Client\stnlc.exe" "-profile=D:/vm303-profile/romfast.tlp"
|
|
||||||
D:\ROA\instantclient_19_18\sqlplus.exe -S -L "CONTAFIN_ORACLE/<parola>@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SID=ROA)))" "@script.sql"
|
|
||||||
```
|
|
||||||
Din Git Bash, calea profilului se da cu **bare oblice** — backslash-urile sunt inghitite si dau
|
|
||||||
„CreateFile(): Windows error 2". Parola se cere de la Marius, nu se ghiceste
|
|
||||||
(`FAILED_LOGIN_ATTEMPTS` poate bloca schema in productie).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## COMPLETARE (dupa lane-ul `src` + `dba_scheduler_job_run_details`)
|
|
||||||
|
|
||||||
`CONTAFIN_ORACLE` **are** drept pe vederile DBA. Joburile externe PowerShell sunt vizibile si
|
|
||||||
schimba diagnosticul:
|
|
||||||
|
|
||||||
```
|
|
||||||
EXEC_PS_40900C5A SUCCEEDED 16.09 22:27:13 error# 0
|
|
||||||
UPDATEROA_JOB FAILED 16.09 22:27:42 error# 29273
|
|
||||||
EXEC_PS_804FF579 SUCCEEDED 16.09 22:10:06 error# 0
|
|
||||||
UPDATEROA_JOB FAILED 16.09 22:10:36 error# 29273
|
|
||||||
EXEC_PS_B5715BCA / BF32458E / A31FD48B / 6FC1191C / 8E28DF1C / 15128026 SUCCEEDED 16.09 21:17:31-38
|
|
||||||
UPDATEROA_JOB SUCCEEDED 16.09 21:17:51
|
|
||||||
EXEC_PS_* x7 SUCCEEDED 16.09 04:00:01-08 (rularea automata, cod vechi)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Ce dovedeste asta:**
|
|
||||||
|
|
||||||
1. **PowerShell PORNESTE si iese cu cod 0.** Ipoteza B din handoff (job extern neconfigurat, lipsa
|
|
||||||
drepturi, `curl` absent) e **INFIRMATA**. `sys.ExecuteScriptOS` functioneaza pe ROMFAST.
|
|
||||||
2. **Esecul e INAUNTRUL scriptului `.ps1`.** Jobul e `job_type=>'executable'` pe `powershell.exe`;
|
|
||||||
`SUCCEEDED` inseamna doar ca `powershell.exe` a iesit cu 0. Scriptul iese cu 0 **si pe ramura de
|
|
||||||
esec**, fiindca `else { Remove-Item ... }` (PACK_UTILS body 698-699) nu propaga nimic.
|
|
||||||
Deci: ori `curl` a esuat si `.part` a fost sters, ori `curl` a mers si `Move-Item` a picat.
|
|
||||||
**Ambele sunt inghitite tacit.**
|
|
||||||
3. **Sub codul vechi mergea, sub cel nou nu.** La 04:00 (automat) au rulat 7 joburi PS, toate
|
|
||||||
reusite, si actualizarea a mers; la 21:17 sase joburi PS reusite, `UPDATEROA_JOB` SUCCEEDED —
|
|
||||||
toate **inainte** de recompilarea de la 21:17:51. Dupa recompilare: la 22:10 si 22:27 ruleaza
|
|
||||||
**exact un singur** job PS, apoi esec la 30 de secunde. Adica **prima descarcare** sub noul cod
|
|
||||||
pica, si lantul se opreste acolo.
|
|
||||||
|
|
||||||
**Diferentele reale introduse azi** (lane `src`, comparatie `co_2026_09_16_02` vs
|
|
||||||
`co_2026_01_14_01_PACK_UTILS.sql`):
|
|
||||||
- numele scriptului `.ps1`: fix `download_file.ps1` -> unic `download_<timestamp>.ps1`;
|
|
||||||
- se sterge fisierul destinatie INAINTE de descarcare (body 685) — nou;
|
|
||||||
- se descarca in `<dest>.part` si se muta cu `Move-Item -Force` — nou;
|
|
||||||
- `.ps1` se sterge la final (body 719) — nou;
|
|
||||||
- in `PACK_UTILS_FILE`, `FileDelete` a devenit best-effort (`when others then null`), deci nu mai
|
|
||||||
ridica ORA-29283/29291 care inainte semnalau problema.
|
|
||||||
- **Gardele si preconditiile sunt NESCHIMBATE** — deci nu s-a pierdut nicio configurare.
|
|
||||||
|
|
||||||
**Suspectul principal ramas:** scriptul `.ps1` nou. Doua variante, nedepartajate:
|
|
||||||
(a) `curl.exe -k -o "<dest>.part"` esueaza (dar mergea la 21:17 cu aceeasi retea si acelasi URL);
|
|
||||||
(b) `Move-Item -Force` din `.part` in destinatie pica — si e exact pasul care NU exista inainte.
|
|
||||||
Varianta (b) explica de ce simptomul a aparut fix la prima rulare de dupa recompilare.
|
|
||||||
A treia varianta, de exclus: `clob2fileX` (functie noua) scrie `.ps1`-ul incomplet/gol — powershell
|
|
||||||
ar rula un fisier gol si ar iesi tot cu 0, exact ce se vede.
|
|
||||||
|
|
||||||
**Cum se departajeaza, pe OS, pe 10.0.20.36, in `C:\DMPDIR`:**
|
|
||||||
- raman fisiere `url2clob_*.tmp.part` -> `curl` a mers, `Move-Item` a picat -> varianta (b);
|
|
||||||
- nu ramane nimic -> `curl` a esuat -> varianta (a);
|
|
||||||
- raman `download_*.ps1` -> scriptul nu a fost sters, deci bucla nu a ajuns la capat;
|
|
||||||
- deschide un `download_*.ps1` ramas si vezi daca are continut -> departajeaza `clob2fileX`.
|
|
||||||
Rularea manuala a aceluiasi `.ps1` din consola, pe server, arata direct codul de iesire al lui `curl`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## COMPLETARE 2: dovada de pe disc (`\10.0.20.36\c\DMPDIR`)
|
|
||||||
|
|
||||||
`roadigi.romfast.ro` ESTE 10.0.20.36; share-ul `\10.0.20.36\c\` e accesibil direct de pe statie.
|
|
||||||
|
|
||||||
Continut relevant:
|
|
||||||
```
|
|
||||||
url2clob_20260916222712422000000.tmp.part 10863 octeti
|
|
||||||
CreationTime = LastWriteTime = 16.09 22:27:12.990
|
|
||||||
```
|
|
||||||
- Numele contine timestamp-ul generat de PL/SQL: **22:27:12.422**.
|
|
||||||
- `EXEC_PS_40900C5A` (jobul PowerShell) s-a incheiat SUCCEEDED la **22:27:13**.
|
|
||||||
- `UPDATEROA_JOB` a esuat la 22:27:42 (30s = `POWERSHELLTIMEOUT`).
|
|
||||||
- Continutul `.part` este `roa_app.xml` **complet si valid** (`<?xml ... Windows-1252?><VFPData>` ...
|
|
||||||
`</VFPData>`), lista de programe de la ROAACNPRO la ROAVIN.
|
|
||||||
- **Niciun `download_*.ps1` ramas** in DMPDIR (stergerea de la body 719 a functionat).
|
|
||||||
|
|
||||||
**Ce e DOVEDIT acum:**
|
|
||||||
1. `curl.exe` a mers perfect: a descarcat tot fisierul, in ~0.5 secunde, in locatia corecta.
|
|
||||||
Ipoteza (a) — „curl esueaza" — e **INFIRMATA**.
|
|
||||||
2. Redenumirea `.part` -> destinatie **nu s-a facut**. Nici `Remove-Item` de pe ramura `else` nu
|
|
||||||
s-a facut, altfel fisierul ar fi disparut.
|
|
||||||
3. PowerShell a iesit imediat dupa `curl` (22:27:12.990 -> job incheiat 22:27:13), cu cod 0.
|
|
||||||
4. NTFS pe `C:\DMPDIR`: `Authenticated Users:(M)` — **Modify, deci si drept de stergere**.
|
|
||||||
Ipoteza „lipsa drept de delete/rename" e **INFIRMATA**.
|
|
||||||
|
|
||||||
**Intrebarea ramasa, singura, nedepartajata:** de ce nu s-a executat linia a doua a scriptului.
|
|
||||||
Doua variante, ambele compatibile cu toate dovezile de mai sus:
|
|
||||||
- **(b) `Move-Item` a rulat si a esuat** ca eroare ne-terminanta -> scrisa pe stderr, ignorata,
|
|
||||||
PowerShell iese 0. Motivul esecului ramane nestiut (jobul extern nu captureaza stderr).
|
|
||||||
- **(c) fisierul `.ps1` a fost scris trunchiat la primul CRLF**, deci PowerShell a executat doar
|
|
||||||
linia cu `curl` si a iesit. Scrierea se face cu `PACK_UTILS_FILE.Clob2FileX`
|
|
||||||
(`dbms_xslprocessor.clob2file(clob, dir, file, csid => 0)`), iar scriptul de azi este **primul
|
|
||||||
script cu mai mult de o linie** — inainte era o singura comanda `curl` direct in destinatie, deci
|
|
||||||
o trunchiere la primul CRLF ar fi fost invizibila. Asta explica de ce simptomul apare exact la
|
|
||||||
prima rulare de dupa patch.
|
|
||||||
|
|
||||||
**Testul minim care departajeaza (cere o scriere, NU s-a facut):** pe o instanta de test, apeleaza
|
|
||||||
`PACK_UTILS_FILE.Clob2FileX` cu un clob de doua linii separate prin `chr(13)||chr(10)` si uita-te la
|
|
||||||
fisierul rezultat: daca are o singura linie -> varianta (c), si reparatia e in felul in care se scrie
|
|
||||||
`.ps1`-ul, nu in `curl`. Daca are doua linii -> varianta (b), si atunci trebuie capturat stderr-ul
|
|
||||||
scriptului ca sa se vada de ce pica `Move-Item`.
|
|
||||||
|
|
||||||
**Fisierul `.part` de la 22:27 e singura proba fizica ramasa — nu-l stergeti si nu-l redenumiti
|
|
||||||
pana nu se decide testul.** Cel de la rularea 22:10 nu mai exista (nu se stie cine l-a sters).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## CAUZA RADACINA — DEMONSTRATA prin reproducere pe ROMFAST (16.09.2026, 23:07)
|
|
||||||
|
|
||||||
Marius a aprobat rularea directa pe productie. Probele au fost rulate prin
|
|
||||||
`PACK_UTILS_FILE.Clob2FileX` + `sys.ExecuteScriptOS`, adica prin exact acelasi mecanism si sub
|
|
||||||
exact acelasi cont ca lantul real. Toate fisierele de proba au fost sterse dupa (vezi „Curatenie").
|
|
||||||
|
|
||||||
### Proba 1 — `Clob2FileX` scrie corect (varianta (c) INFIRMATA)
|
|
||||||
Clob de doua linii separate prin `chr(13)||chr(10)` -> fisier de 248 octeti, **ambele linii intacte**,
|
|
||||||
`\r \n` pastrat, fara BOM, fara trunchiere (`od -c`). Scrierea `.ps1`-ului nu e problema.
|
|
||||||
|
|
||||||
### Proba 2 — `Move-Item` functioneaza sub contul jobului (varianta (b) INFIRMATA)
|
|
||||||
Script rulat prin `ExecuteScriptOS`: creeaza un fisier in `C:\DMPDIR`, il muta, il sterge.
|
|
||||||
Rezultat in log: `MOVE OK`, `DEL OK`. Si `cwd=C:\`. Deci nici drepturile, nici `Move-Item` nu pica.
|
|
||||||
(NTFS pe `C:\DMPDIR`: `Authenticated Users:(M)`.)
|
|
||||||
|
|
||||||
### Proba 3 — reproducerea scriptului real
|
|
||||||
Script identic cu cel generat de `DownloadFileOS`, pe URL-ul real
|
|
||||||
`https://roa.romfast.ro/contafinupdate/default.aspx/updroa/download/1/roa_app.xml`
|
|
||||||
(`optiuni.UPD_URL_APP` + `sys.auth_detalii.detalii = 1`):
|
|
||||||
```
|
|
||||||
lastexit=
|
|
||||||
part_ramas=False dest_creat=False
|
|
||||||
erori=1
|
|
||||||
Cannot find path 'C:\DMPDIR\claude_repro.tmp.part' because it does not exist.
|
|
||||||
```
|
|
||||||
`$LASTEXITCODE` **gol**; `Remove-Item` a picat pentru ca fisierul inca nu exista. Fisierul `.part`
|
|
||||||
a aparut DUPA ce scriptul se terminase.
|
|
||||||
|
|
||||||
### Proba 4 — mecanismul, cu ceas
|
|
||||||
```
|
|
||||||
t0=23:07:08.671
|
|
||||||
t1=23:07:08.707 ec=[] exists=False <- la 36 de milisecunde dupa lansarea curl
|
|
||||||
t2=23:07:13.735 exists=True <- dupa Start-Sleep 5
|
|
||||||
proc_curl=0
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verdict
|
|
||||||
**Sub jobul extern DBMS_SCHEDULER (proces fara consola), operatorul `&` din PowerShell nu asteapta
|
|
||||||
procesul nativ.** Scriptul trece la linia urmatoare in ~36 ms, `$LASTEXITCODE` nu e niciodata setat
|
|
||||||
(deci `$null -eq 0` = False), se executa ramura `else` pe un fisier inca inexistent, iar cand `curl`
|
|
||||||
termina in fundal lasa `<destinatie>.part` pe disc **pentru totdeauna**. Fisierul destinatie nu apare
|
|
||||||
niciodata -> bucla de polling din `DownloadFileOS` expira dupa `POWERSHELLTIMEOUT` (30s) ->
|
|
||||||
`llDownloaded = FALSE` -> `URL2Clob` cade pe `HTTPURITYPE` (body 305) -> pe `https` fara wallet ->
|
|
||||||
**ORA-29273 / ORA-29024**.
|
|
||||||
|
|
||||||
**De ce mergea inainte:** codul vechi scria cu `curl -o` DIRECT in fisierul destinatie. Nu exista
|
|
||||||
pas de redenumire, deci nu depindea de terminarea lui `curl`: bucla de polling din Oracle astepta
|
|
||||||
oricum aparitia fisierului, iar asincronia era inofensiva. Patch-ul de azi a introdus un pas
|
|
||||||
(`Move-Item`) care presupune ca `curl` s-a terminat — presupunere falsa in acest mediu.
|
|
||||||
Asta explica exact de ce simptomul apare la prima rulare de dupa recompilarea de la 21:17:51.
|
|
||||||
|
|
||||||
### Directia de reparatie (NEAPLICATA, nediscutata inca)
|
|
||||||
Scriptul trebuie sa astepte explicit procesul, nu sa se bazeze pe `&` si pe `$LASTEXITCODE`. Varianta
|
|
||||||
minima, in `PACK_UTILS.DownloadFileOS` body ~694-699, inlocuind linia cu `&`:
|
|
||||||
```powershell
|
|
||||||
$p = Start-Process -FilePath "<curl>" -ArgumentList '-k','-o','<dest>.part','<url>' -Wait -PassThru -NoNewWindow
|
|
||||||
if ($p.ExitCode -eq 0) { Move-Item ... } else { Remove-Item ... }
|
|
||||||
```
|
|
||||||
Alternativ, `& "<curl>" ... | Out-Null` forteaza consumarea iesirii, deci asteptarea — dar `Start-Process
|
|
||||||
-Wait -PassThru` e explicit si nu depinde de un efect secundar al pipeline-ului.
|
|
||||||
**Ambele variante trebuie probate prin acelasi mecanism (`ExecuteScriptOS`) inainte de a fi propuse
|
|
||||||
pentru commit** — mediul jobului extern e exact ce a invalidat presupunerea initiala.
|
|
||||||
|
|
||||||
### Curatenie
|
|
||||||
Toate fisierele de proba sterse din `C:\DMPDIR` (`test_clob2file_claude.txt`, `diag_claude.*`,
|
|
||||||
`repro_claude.*`, `claude_repro.*`, `async_claude.*`). A ramas **doar** proba originala
|
|
||||||
`url2clob_20260916222712422000000.tmp.part` (10863 octeti, 22:27) — nu o stergeti, e dovada fizica
|
|
||||||
a incidentului.
|
|
||||||
Nu s-a modificat niciun pachet, niciun job, nicio optiune. Singurele scrieri pe productie au fost
|
|
||||||
fisiere temporare in `C:\DMPDIR`, toate sterse.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## INCHEIERE BLOC (16.09.2026, ~23:40)
|
|
||||||
|
|
||||||
### Livrat si comis
|
|
||||||
| ce | unde | revizie |
|
|
||||||
|---|---|---|
|
|
||||||
| `co_2026_09_16_06_COMUN_PACK_UTILS.sql` | `^/DATABASE/Branches/RB-1.00` | r18146 (comis de Marius), r18148 (scoaterea lui `&`) |
|
|
||||||
| `docs/depanare-server-update-roa.md` | `^/COMUN/Trunk` | r18147 |
|
|
||||||
|
|
||||||
### Aplicat pe servere
|
|
||||||
- **ROMFAST 10.0.20.36**: aplicat 23:19:32, `PACK_UTILS` VALID, `VERSIUNE_CONTAFIN_ORACLE =
|
|
||||||
202609160006`. `PACK_UPDATE.UpdateROA` rulat de Marius 23:21:50-23:24:19, `UPDATEROA_JOB`
|
|
||||||
SUCCEEDED, sase joburi `EXEC_PS_*` reusite, `UPD_LOG` fara randuri `ERR`, 65 de scheme pe
|
|
||||||
`20260916`. `URL2Clob` intoarce documentul complet in ~2s.
|
|
||||||
- **ROA_CENTRAL 10.0.20.122**: aplicat de Marius. A afisat `SP2-0317: expected symbol name is
|
|
||||||
missing` — cauza: un `&` la sfarsit de rand intr-un COMENTARIU (linia 756), citit de sqlplus ca
|
|
||||||
variabila de substitutie fara nume. Efect strict cosmetic: pachetul s-a compilat
|
|
||||||
(„Package body created"), codul e intact. Reparat in r18148 prin reformularea comentariilor.
|
|
||||||
**NU e BOM** — fisierul are 0 octeti non-ASCII, verificat la scriere si la commit.
|
|
||||||
**Nu s-a verificat independent** starea de pe ROA_CENTRAL (nu s-a deschis tunel acolo).
|
|
||||||
|
|
||||||
### Capcana de retinut
|
|
||||||
La aplicarea unui script prin sqlplus, **nu filtra iesirea** — asa am ratat acelasi `SP2-0317` pe
|
|
||||||
ROMFAST. Un `&` la sfarsit de rand, chiar si in comentariu, produce eroarea; `&` urmat de virgula
|
|
||||||
sau spatiu e tolerat.
|
|
||||||
|
|
||||||
### Ramas deschis
|
|
||||||
- **Tunelul Bitvise catre ROMFAST e inca RIDICAT** (`stnlc`, profil `D:\vm303-profile\romfast.tlp`,
|
|
||||||
porturi 1521/1522/1523). De inchis:
|
|
||||||
`Stop-Process -Id (Get-NetTCPConnection -LocalPort 1521 -State Listen).OwningProcess`.
|
|
||||||
- `C:\DMPDIR\url2clob_20260916222712422000000.tmp.part` — proba fizica a incidentului, pastrata
|
|
||||||
intentionat. Acum ca e documentata, poate fi stearsa.
|
|
||||||
- **Necomise, intentionat** (documente de lucru): `docs/handoff_update_romfast.md`,
|
|
||||||
`docs/raport_src.md`, `docs/raport_doc.md`.
|
|
||||||
- **Intrebare deschisa, neinrudita cu reparatia asta**: capatul departat al db link-ului
|
|
||||||
`DBL_ROMFAST2` este chiar 10.0.20.36; la incidentul conpress s-a omorat sesiunea de pe partea
|
|
||||||
apropiata (ROA_CENTRAL) fara sa se priveasca partea departata. Cele doua incidente pot avea
|
|
||||||
radacini diferite — reparatia de azi NU il explica pe cel de la conpress.
|
|
||||||
- Restul serverelor de client: scriptul ajunge la ele prin fluxul normal de actualizare.
|
|
||||||
|
|
||||||
### Stare periculoasa
|
|
||||||
Niciuna. Pe productie nu a ramas nimic in lucru: niciun job atins, nicio tranzactie deschisa, toate
|
|
||||||
fisierele de proba sterse din `C:\DMPDIR`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ACTUALIZARE FINALA (23:45)
|
|
||||||
|
|
||||||
**Tunelul Bitvise catre ROMFAST este INCHIS.** Anuleaza randul „Tunelul ... e inca RIDICAT" din
|
|
||||||
sectiunea „Ramas deschis" de mai sus: procesul `stnlc` a fost oprit, iar porturile 1521/1522/1523
|
|
||||||
nu mai asculta (verificat cu `Get-NetTCPConnection`). Nu mai exista acces deschis la productie din
|
|
||||||
aceasta sesiune.
|
|
||||||
|
|
||||||
Reluarea conexiunii, cand va fi nevoie:
|
|
||||||
```
|
|
||||||
"C:\Program Files (x86)\Bitvise SSH Client\stnlc.exe" "-profile=D:/vm303-profile/romfast.tlp"
|
|
||||||
```
|
|
||||||
Din Git Bash calea profilului se da cu bare oblice — backslash-urile sunt inghitite si dau
|
|
||||||
„CreateFile(): Windows error 2".
|
|
||||||
|
|
||||||
Restul sectiunii „Ramas deschis" ramane valabil: proba `.part` din `C:\DMPDIR`, documentele de lucru
|
|
||||||
necomise, si intrebarea deschisa despre db link-ul `DBL_ROMFAST2`.
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
# Hooks ca functii + acces la context consumat — verificare documentatie oficiala
|
|
||||||
|
|
||||||
Data verificare: 2026-09-17. Metoda: WebFetch pe paginile oficiale (rezumate de un model
|
|
||||||
intermediar, nu HTML brut — unde continutul a fost trunchiat, marcat explicit mai jos) +
|
|
||||||
verificare empirica directa pe un `.jsonl` de pe disc.
|
|
||||||
|
|
||||||
## 1. Exista hook-uri definite ca FUNCTII (nu shell command)?
|
|
||||||
|
|
||||||
### In Claude Code (`settings.json` / plugin `hooks/hooks.json`) — NU
|
|
||||||
|
|
||||||
Pagina `https://code.claude.com/docs/en/hooks` defineste explicit tipurile de handler pentru
|
|
||||||
hook-uri:
|
|
||||||
|
|
||||||
> "Hooks are user-defined shell commands, HTTP endpoints, MCP tool calls, LLM prompts, or
|
|
||||||
> subagents that execute automatically at specific points in Claude Code's lifecycle."
|
|
||||||
|
|
||||||
Cinci tipuri de `type`, toate procese externe sau apeluri la distanta, niciunul „functie in-proces":
|
|
||||||
|
|
||||||
1. `"command"` — shell command (Bash/PowerShell)
|
|
||||||
2. `"http"` — POST catre un endpoint HTTP
|
|
||||||
3. `"mcp_tool"` — apel catre un tool MCP
|
|
||||||
4. `"prompt"` — evaluare printr-un prompt LLM single-turn
|
|
||||||
5. `"agent"` — subagent (experimental)
|
|
||||||
|
|
||||||
Confirmat separat pe `https://code.claude.com/docs/en/plugins-reference`, care listeaza acelasi
|
|
||||||
set de cinci tipuri pentru schema hook-urilor din plugin-uri si spune explicit:
|
|
||||||
|
|
||||||
> "There is no `function` type mentioned anywhere in the documentation."
|
|
||||||
|
|
||||||
Exemplu de schema (din `plugins-reference`):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hooks": {
|
|
||||||
"PostToolUse": [
|
|
||||||
{
|
|
||||||
"matcher": "Write|Edit",
|
|
||||||
"hooks": [
|
|
||||||
{ "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/format-code.sh" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Concluzie punct 1a**: in Claude Code (inclusiv plugin-uri), hook-urile NU pot fi functii
|
|
||||||
JS/TS/Python in-proces — doar comenzi shell, HTTP, MCP tool, prompt LLM sau subagent.
|
|
||||||
|
|
||||||
### In Claude Agent SDK (TypeScript) — DA, dar cu rezerva NEDOCUMENTAT pe detalii
|
|
||||||
|
|
||||||
Pagina `https://code.claude.com/docs/en/agent-sdk/typescript` (redirect de la
|
|
||||||
`docs.claude.com/.../agent-sdk/typescript`) arata ca optiunea `hooks` a SDK-ului accepta
|
|
||||||
callback-uri, nu comenzi shell:
|
|
||||||
|
|
||||||
> `hooks` | `Partial<Record<`HookEvent`, `HookCallbackMatcher`[]>>` | `{}` | Hook callbacks for events
|
|
||||||
|
|
||||||
Asta e un mecanism DIFERIT de `settings.json` al Claude Code: aici hook-ul e literal o functie
|
|
||||||
TypeScript data la `query({ ..., hooks: {...} })`, ruland in acelasi proces Node ca aplicatia SDK.
|
|
||||||
|
|
||||||
**NEDOCUMENTAT - nu am putut extrage**: definitiile exacte de tip pentru `HookEvent`,
|
|
||||||
`HookCallback`, `HookCallbackMatcher`, `HookJSONOutput`, sau tipurile de input per eveniment
|
|
||||||
(`PreToolUseHookInput` etc.) — pagina e mare si WebFetch a trunchiat/rezumat continutul de doua
|
|
||||||
ori la rand, fara sa gaseasca sectiunea cu type body-urile (doar link-uri ancora `#hookevent`,
|
|
||||||
`#hookcallbackmatcher`, nerezolvate de rezumator). Nu pot afirma nici ca schema de input e identica
|
|
||||||
cu a hook-urilor `command`, nici ca difera — necesita citire directa a paginii (curl/browser),
|
|
||||||
nu WebFetch.
|
|
||||||
|
|
||||||
## 2. Primesc function hooks (SDK) un input mai bogat decat hook-urile `command`?
|
|
||||||
|
|
||||||
**NEDOCUMENTAT - nu am putut extrage.** Pagina SDK TS nu a livrat campurile exacte ale obiectului
|
|
||||||
de input trimis catre `HookCallback` (vezi punctul 1). Singurul camp relevant gasit pe acea pagina
|
|
||||||
a fost `maxThinkingTokens` (o optiune de configurare a sesiunii, nu un camp de input al hook-ului).
|
|
||||||
Nu exista nicio mentiune gasita de `usage`, `context_window` sau echivalent in continutul extras.
|
|
||||||
|
|
||||||
## 3. Ce primeste `statusLine` ca input, si acelasi obiect e disponibil vreunui hook?
|
|
||||||
|
|
||||||
Pagina `https://code.claude.com/docs/en/statusline` confirma ca `statusLine` e un mecanism separat
|
|
||||||
de hook-uri: un script shell propriu, care primeste JSON pe stdin cu date de sesiune, explicit
|
|
||||||
descris ca fiind pentru monitorizarea folosirii contextului:
|
|
||||||
|
|
||||||
> "The status line is a customizable bar at the bottom of Claude Code that runs any shell script
|
|
||||||
> you configure. It receives JSON session data on stdin and displays whatever your script prints,
|
|
||||||
> giving you a persistent, at-a-glance view of context usage, costs, git status..."
|
|
||||||
|
|
||||||
**NEDOCUMENTAT - nu am putut extrage** schema JSON exacta trimisa pe stdin (campurile
|
|
||||||
`context_window.used_percentage` etc.) — fetch-ul a livrat doar introducerea paginii, nu tabelul
|
|
||||||
de schema (posibil mai jos in pagina, trunchiat de rezumator).
|
|
||||||
|
|
||||||
Nu am gasit, in niciuna din paginile de hook-uri (`hooks`, `hooks-guide`, `plugins-reference`),
|
|
||||||
vreo mentiune ca acelasi obiect JSON dat lui `statusLine` ar fi disponibil si unui hook obisnuit.
|
|
||||||
Structural, `statusLine` e configurat separat de `hooks` in `settings.json` si documentat ca
|
|
||||||
mecanism de sine statator, nu ca un tip de hook din lista de 5 (`command`/`http`/`mcp_tool`/
|
|
||||||
`prompt`/`agent`).
|
|
||||||
|
|
||||||
## 4. Exista un eveniment de hook dedicat contextului (prag, PreCompact cu date de ocupare)?
|
|
||||||
|
|
||||||
Pagina `hooks` listeaza evenimentul `PreCompact` ("Before context compaction") si `PostCompact`,
|
|
||||||
dar continutul extras nu contine schema de input pentru `PreCompact`:
|
|
||||||
|
|
||||||
> `PreCompact` are matcher pe ce a declansat compactarea (`"manual"` sau `"auto"`), dar campurile
|
|
||||||
> JSON de input nu sunt specificate in continutul extras.
|
|
||||||
|
|
||||||
Nu exista, in continutul extras din niciuna dintre pagini, un eveniment de tip "context threshold"
|
|
||||||
separat de `PreCompact`/`PostCompact`. **NEDOCUMENTAT - nu am putut extrage** schema completa a
|
|
||||||
`PreCompact` (posibil contine deja procente de ocupare — nu s-a putut confirma nici infirma).
|
|
||||||
|
|
||||||
Campurile COMUNE confirmate pentru toate evenimentele de hook (din tabelul extras pe pagina
|
|
||||||
`hooks`):
|
|
||||||
|
|
||||||
```
|
|
||||||
session_id, prompt_id, transcript_path, cwd, scratchpad_dir, permission_mode,
|
|
||||||
effort.level, hook_event_name, agent_id (doar subagenti), agent_type (doar subagenti)
|
|
||||||
```
|
|
||||||
|
|
||||||
Niciun camp de tokeni/usage/context in aceasta lista. Coincide cu dovada empirica deja detinuta
|
|
||||||
(inputul real al `SubagentStop` capturat anterior nu are camp de tokeni).
|
|
||||||
|
|
||||||
## 5. Schema unei linii `assistant` din transcriptul `.jsonl` — are `usage`?
|
|
||||||
|
|
||||||
**Verificat direct pe disc, DA** — nu doar documentatie, dovada empirica reala:
|
|
||||||
|
|
||||||
```
|
|
||||||
grep -o '"usage":{[^}]*}' bdb0bf8c-a086-4d46-b08d-545a92e5c32c.jsonl | head -3
|
|
||||||
```
|
|
||||||
|
|
||||||
rezultat (identic pe primele linii verificate):
|
|
||||||
|
|
||||||
```json
|
|
||||||
"usage":{"input_tokens":2,"cache_creation_input_tokens":34191,"cache_read_input_tokens":31003,"output_tokens":1710,"output_tokens_details":{"thinking_tokens":198}
|
|
||||||
```
|
|
||||||
|
|
||||||
Deci fiecare linie `assistant` din `.jsonl` are un obiect `message.usage` cu:
|
|
||||||
`input_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens`, `output_tokens`,
|
|
||||||
`output_tokens_details.thinking_tokens`.
|
|
||||||
|
|
||||||
Asta e citibil de orice proces cu acces la fisier (inclusiv un hook `command`, daca i s-ar da
|
|
||||||
calea) — dar hook-ul primeste doar `transcript_path` ca referinta, nu campul de usage direct in
|
|
||||||
inputul lui JSON. Un hook `command` ar putea *citi singur* fisierul si insuma `usage` peste toate
|
|
||||||
liniile `assistant` ca sa aproximeze contextul consumat — asta nu necesita „function hooks",
|
|
||||||
functioneaza si cu un hook shell obisnuit care are `jq`/`python` la indemana si stie
|
|
||||||
`transcript_path`.
|
|
||||||
|
|
||||||
## Verdict
|
|
||||||
|
|
||||||
**Poate un hook sa afle contextul consumat, si pe ce cale — da, dar nu prin niciun camp direct din
|
|
||||||
inputul JSON al hook-ului**, indiferent daca hook-ul e `command` sau (in SDK, nu in Claude Code)
|
|
||||||
o functie in-proces:
|
|
||||||
|
|
||||||
- **Calea documentata si confirmata empiric**: orice hook `command` (sau function-hook din SDK,
|
|
||||||
daca primeste `transcript_path` in input — nedocumentat exact, dar plauzibil, campul e comun
|
|
||||||
tuturor evenimentelor conform tabelului din `hooks`) poate **citi singur** `transcript_path` de
|
|
||||||
pe disc si insuma `message.usage` din liniile `assistant` ca sa aproximeze tokenii consumati.
|
|
||||||
Asta confirma ce a spus deja Marius implicit: se poate afla, dar prin citire activa a
|
|
||||||
transcriptului, nu pentru ca hook-ul primeste un camp gata calculat.
|
|
||||||
- **Nu exista, in ce am putut extrage din documentatie, niciun camp `usage`/`context_window`/
|
|
||||||
`tokens` in inputul JSON dat direct hook-ului** (nici la `command`, nici — din cate am putut
|
|
||||||
verifica — mentionat pentru function hooks din SDK).
|
|
||||||
- **`statusLine` e mecanismul care primeste `context_window.used_percentage` gata calculat**, dar
|
|
||||||
e un canal separat de `hooks` in `settings.json`, nu un tip de hook; nu am gasit dovada ca acel
|
|
||||||
obiect ar fi expus si catre hook-uri.
|
|
||||||
- Afirmatia initiala („niciun hook Claude Code nu poate afla cat context s-a consumat") e
|
|
||||||
**partial gresita**: un hook nu primeste tokenii de-a gata, dar poate sa-i afle citind singur
|
|
||||||
`transcript_path` — cale disponibila oricarui hook `command`, nu doar unor ipotetice „function
|
|
||||||
hooks".
|
|
||||||
|
|
||||||
## Goluri ramase (NEDOCUMENTAT, de reverificat cu citire directa a paginii, nu WebFetch)
|
|
||||||
|
|
||||||
- Schema completa de tip TypeScript pentru `HookCallback`/`HookCallbackMatcher`/`HookEvent` din
|
|
||||||
SDK (`code.claude.com/docs/en/agent-sdk/typescript`) — pagina prea mare, WebFetch a trunchiat de
|
|
||||||
doua ori la rand.
|
|
||||||
- Schema JSON completa trimisa pe stdin catre `statusLine` (`code.claude.com/docs/en/statusline`).
|
|
||||||
- Schema completa de input pentru `PreCompact` (`code.claude.com/docs/en/hooks`).
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
diff --git a/COMUN/clase/onom_curs.vc2 b/COMUN/clase/onom_curs.vc2
|
|
||||||
index 4042942..7fb9fd6 100644
|
|
||||||
--- a/COMUN/clase/onom_curs.vc2
|
|
||||||
+++ b/COMUN/clase/onom_curs.vc2
|
|
||||||
@@ -15,6 +15,8 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
|
|
||||||
*m: scrie_curs_bnr
|
|
||||||
*p: ccursor
|
|
||||||
*p: clink
|
|
||||||
+ *p: clinkan
|
|
||||||
+ *p: clinkarhiva
|
|
||||||
*p: ctempfile
|
|
||||||
*p: ddata
|
|
||||||
*p: ncurs
|
|
||||||
@@ -26,6 +28,8 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
|
|
||||||
*<PropValue>
|
|
||||||
ccursor = ccrsbnr
|
|
||||||
clink = https://curs.bnr.ro/nbrfxrates.xml
|
|
||||||
+ clinkan = https://curs.bnr.ro/files/xml/years/nbrfxrates%.xml
|
|
||||||
+ clinkarhiva = https://curs.bnr.ro/nbrfxrates10days.xml
|
|
||||||
ctempfile = C:\temp\temp.xml
|
|
||||||
ddata = {}
|
|
||||||
Height = 17
|
|
||||||
@@ -37,6 +41,8 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
|
|
||||||
_memberdata = <VFPData>
|
|
||||||
<memberdata name="ctempfile" display="cTempFile"/>
|
|
||||||
<memberdata name="clink" display="cLink"/>
|
|
||||||
+ <memberdata name="clinkan" display="cLinkAn"/>
|
|
||||||
+ <memberdata name="clinkarhiva" display="cLinkArhiva"/>
|
|
||||||
<memberdata name="ccursor" display="cCursor"/>
|
|
||||||
<memberdata name="ddata" display="dData"/>
|
|
||||||
<memberdata name="ntip" display="nTip"/>
|
|
||||||
@@ -98,8 +104,13 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
|
|
||||||
ENDPROC
|
|
||||||
|
|
||||||
PROCEDURE citeste_curs_bnr
|
|
||||||
+ Lparameters tnSursa
|
|
||||||
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
|
|
||||||
- Local lcData, lcFisier, lcServer
|
|
||||||
+ Local lcData, lcFisier, lcServer, lcCubeCautat, ldCandidat, lnIncercari, lnAnCautat, llRetrasAnAnterior, lnStatus
|
|
||||||
+
|
|
||||||
+ If Vartype(tnSursa) <> "N"
|
|
||||||
+ tnSursa = 0
|
|
||||||
+ Endif
|
|
||||||
|
|
||||||
If Empty(This.cCursor)
|
|
||||||
This.cCursor = [ccrsbnr]
|
|
||||||
@@ -109,30 +120,90 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
|
|
||||||
Use In (This.cCursor)
|
|
||||||
Endif
|
|
||||||
|
|
||||||
- lcServer = This.cLink
|
|
||||||
+ Do Case
|
|
||||||
+ Case tnSursa = 1
|
|
||||||
+ lcServer = This.cLinkArhiva
|
|
||||||
+ Case tnSursa = 2
|
|
||||||
+ lnAnCautat = Year(This.dData - 1)
|
|
||||||
+ lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
|
|
||||||
+ Otherwise
|
|
||||||
+ lcServer = This.cLink
|
|
||||||
+ Endcase
|
|
||||||
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
|
|
||||||
loHTTP.Open('GET', lcServer, .F.)
|
|
||||||
+ loHTTP.SetTimeouts(5000,5000,5000,15000)
|
|
||||||
loHTTP.setRequestHeader("Content-Type", "application/xml;")
|
|
||||||
poLog.Log(m.lcServer)
|
|
||||||
- loHTTP.Send()
|
|
||||||
-
|
|
||||||
- If loHTTP.Status = 200
|
|
||||||
+ Try
|
|
||||||
+ loHTTP.Send()
|
|
||||||
+ lnStatus = loHTTP.Status
|
|
||||||
+ Catch
|
|
||||||
+ lnStatus = 0
|
|
||||||
+ Endtry
|
|
||||||
+
|
|
||||||
+ If lnStatus = 200
|
|
||||||
lcFisier = loHTTP.Responsebody
|
|
||||||
|
|
||||||
lcFisier = Strextract(lcFisier,[</OrigCurrency>],[</Body>])
|
|
||||||
- lcData = Strextract(lcFisier,[date="],["])
|
|
||||||
- lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
|
|
||||||
+ If tnSursa = 0
|
|
||||||
+ lcData = Strextract(lcFisier,[date="],["])
|
|
||||||
+ lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
|
|
||||||
+ Else
|
|
||||||
+ lcCubeCautat = []
|
|
||||||
+ ldCandidat = This.dData - 1
|
|
||||||
+ lnIncercari = 0
|
|
||||||
+ llRetrasAnAnterior = .F.
|
|
||||||
+ Do While Empty(lcCubeCautat) And lnIncercari < 10
|
|
||||||
+ If tnSursa = 2 And Year(ldCandidat) < lnAnCautat And !llRetrasAnAnterior
|
|
||||||
+ llRetrasAnAnterior = .T.
|
|
||||||
+ lnAnCautat = lnAnCautat - 1
|
|
||||||
+ lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
|
|
||||||
+ loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
|
|
||||||
+ loHTTP.Open('GET', lcServer, .F.)
|
|
||||||
+ loHTTP.SetTimeouts(5000,5000,5000,15000)
|
|
||||||
+ loHTTP.setRequestHeader("Content-Type", "application/xml;")
|
|
||||||
+ poLog.Log(m.lcServer)
|
|
||||||
+ Try
|
|
||||||
+ loHTTP.Send()
|
|
||||||
+ lnStatus = loHTTP.Status
|
|
||||||
+ Catch
|
|
||||||
+ lnStatus = 0
|
|
||||||
+ Endtry
|
|
||||||
+ If lnStatus <> 200
|
|
||||||
+ repune_backup_cursoare(This.cCursor)
|
|
||||||
+ Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
+ Return 1
|
|
||||||
+ Endif
|
|
||||||
+ lcFisier = Strextract(loHTTP.Responsebody,[</OrigCurrency>],[</Body>])
|
|
||||||
+ Endif
|
|
||||||
+ lcData = Str(Year(ldCandidat),4) + "-" + Padl(Alltrim(Str(Month(ldCandidat))),2,"0") + "-" + Padl(Alltrim(Str(Day(ldCandidat))),2,"0")
|
|
||||||
+ lcCubeCautat = Strextract(lcFisier,[<Cube date="]+lcData+[">],[</Cube>])
|
|
||||||
+ ldCandidat = ldCandidat - 1
|
|
||||||
+ lnIncercari = lnIncercari + 1
|
|
||||||
+ Enddo
|
|
||||||
+ If Empty(lcCubeCautat)
|
|
||||||
+ repune_backup_cursoare(This.cCursor)
|
|
||||||
+ Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
+ Return 2
|
|
||||||
+ Endif
|
|
||||||
+ lcFisier = [<Cube>] + lcCubeCautat + [</Cube>]
|
|
||||||
+ Endif
|
|
||||||
lcFisier= Strtran(Strtran(lcFisier,[">],[" curs="]),[</Rate>],[" />])
|
|
||||||
Xmltocursor(lcFisier,This.cCursor)
|
|
||||||
- This.dData = Ttod(Ctot(lcData+[T000000]))+1
|
|
||||||
+ If tnSursa = 0
|
|
||||||
+ This.dData = Ttod(Ctot(lcData+[T000000]))+1
|
|
||||||
+ Endif
|
|
||||||
*!* modificare 03.06.2013
|
|
||||||
sterge_backup_cursoare(This.cCursor)
|
|
||||||
Else
|
|
||||||
repune_backup_cursoare(This.cCursor)
|
|
||||||
+ Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
+ Return 1
|
|
||||||
Endif
|
|
||||||
*!* modificare 03.06.2013 ^
|
|
||||||
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
|
|
||||||
+ Return 0
|
|
||||||
ENDPROC
|
|
||||||
|
|
||||||
PROCEDURE Destroy
|
|
||||||
@@ -168,9 +239,20 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
|
|
||||||
Case (This.dData = Ttod(ltDataOra) And Hour(ltDataOra) <13) Or (This.dData = Ttod(ltDataOra) + 1 And Hour(ltDataOra) >= 13)
|
|
||||||
This.citeste_curs_bnr()
|
|
||||||
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
|
|
||||||
- If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
|
|
||||||
- goUrl("http://www.bnr.ro") && din wwutils.prg
|
|
||||||
+ lnStareBnr = This.citeste_curs_bnr(1)
|
|
||||||
+ If lnStareBnr = 2
|
|
||||||
+ lnStareBnr = This.citeste_curs_bnr(2)
|
|
||||||
Endif
|
|
||||||
+ Do Case
|
|
||||||
+ Case lnStareBnr = 0
|
|
||||||
+ && curs preluat din arhiva sau fisierul anual BNR
|
|
||||||
+ Case lnStareBnr = 2
|
|
||||||
+ amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
|
|
||||||
+ Otherwise
|
|
||||||
+ If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina cursbnr.ro?",4+32,"Confirmare") = 6
|
|
||||||
+ goUrl("https://www.cursbnr.ro/")
|
|
||||||
+ Endif
|
|
||||||
+ Endcase
|
|
||||||
Otherwise
|
|
||||||
amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
|
|
||||||
Endcase
|
|
||||||
@@ -1,368 +0,0 @@
|
|||||||
# Patch propus (v3) — arhiva 10 zile + fisier anual BNR, link alternativ cursbnr.ro
|
|
||||||
|
|
||||||
Tinta: `COMUN\clase\onom_curs.vc2`, clasa `actualizare_curs_bnr`. Investigatie sursa:
|
|
||||||
`docs\raport_curs_bnr_politici_preturi.md`. Diff unificat aplicabil: `docs\patch_curs_bnr_arhiva.diff`.
|
|
||||||
Inlocuieste integral v1 si v2 (nu e delta) — aplicat pe fisierul original, neschimbat intre timp.
|
|
||||||
|
|
||||||
**Scop restrans de Marius, valabil din v2**: se lucreaza DOAR pe ramura `nTip = 1`, cazul de dupa
|
|
||||||
ora 13. `nTip = 2` si `nTip = 3` raman neatinse.
|
|
||||||
|
|
||||||
Doua verificari live, facute de Marius, folosite ca atare (nereverificate aici):
|
|
||||||
- `https://curs.bnr.ro/nbrfxrates10days.xml` -> HTTP 200, un `<OrigCurrency>`, mai multe
|
|
||||||
`<Cube date="YYYY-MM-DD">` descrescator, pana la 10 zile lucratoare (v2).
|
|
||||||
- `https://curs.bnr.ro/files/xml/years/nbrfxrates2026.xml` -> HTTP 200, ~245 KB, structura
|
|
||||||
IDENTICA (un `<OrigCurrency>`, apoi `<Cube date="...">` de la 2026-01-05 pana azi, ordine
|
|
||||||
crescatoare). Acelasi cod de extragere a unui `<Cube>` merge neschimbat.
|
|
||||||
|
|
||||||
## 1. Proprietati noi: `cLinkArhiva` si `cLinkAn`
|
|
||||||
|
|
||||||
Bloc afectat: definitia clasei (lista `*p:`, `PropValue`, `_memberdata`), `onom_curs.vc2:16-42`.
|
|
||||||
|
|
||||||
**VECHI** (`onom_curs.vc2:16-18`, in `*<DefinedPropArrayMethod>`):
|
|
||||||
```
|
|
||||||
*p: ccursor
|
|
||||||
*p: clink
|
|
||||||
*p: ctempfile
|
|
||||||
```
|
|
||||||
**NOU:**
|
|
||||||
```
|
|
||||||
*p: ccursor
|
|
||||||
*p: clink
|
|
||||||
*p: clinkan
|
|
||||||
*p: clinkarhiva
|
|
||||||
*p: ctempfile
|
|
||||||
```
|
|
||||||
|
|
||||||
**VECHI** (`onom_curs.vc2:26-29`, in `*<PropValue>`):
|
|
||||||
```
|
|
||||||
ccursor = ccrsbnr
|
|
||||||
clink = https://curs.bnr.ro/nbrfxrates.xml
|
|
||||||
ctempfile = C:\temp\temp.xml
|
|
||||||
```
|
|
||||||
**NOU:**
|
|
||||||
```
|
|
||||||
ccursor = ccrsbnr
|
|
||||||
clink = https://curs.bnr.ro/nbrfxrates.xml
|
|
||||||
clinkan = https://curs.bnr.ro/files/xml/years/nbrfxrates%.xml
|
|
||||||
clinkarhiva = https://curs.bnr.ro/nbrfxrates10days.xml
|
|
||||||
ctempfile = C:\temp\temp.xml
|
|
||||||
```
|
|
||||||
`cLinkAn` tine sablonul cu `%` in locul anului; se compune la rulare cu
|
|
||||||
`Strtran(This.cLinkAn,"%",Alltrim(Str(lnAn)))`.
|
|
||||||
|
|
||||||
**VECHI** (`onom_curs.vc2:37-40`, in `_memberdata`):
|
|
||||||
```
|
|
||||||
<memberdata name="ctempfile" display="cTempFile"/>
|
|
||||||
<memberdata name="clink" display="cLink"/>
|
|
||||||
<memberdata name="ccursor" display="cCursor"/>
|
|
||||||
```
|
|
||||||
**NOU:**
|
|
||||||
```
|
|
||||||
<memberdata name="ctempfile" display="cTempFile"/>
|
|
||||||
<memberdata name="clink" display="cLink"/>
|
|
||||||
<memberdata name="clinkan" display="cLinkAn"/>
|
|
||||||
<memberdata name="clinkarhiva" display="cLinkArhiva"/>
|
|
||||||
<memberdata name="ccursor" display="cCursor"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. `citeste_curs_bnr` (`onom_curs.vc2:100-136`) — parametru `tnSursa` (0/1/2) + cautare inapoi + reincercare an anterior
|
|
||||||
|
|
||||||
Parametrul `tlArhiva` (logic, din v2) devine **`tnSursa`** (numeric): `0` = fisierul zilei (`cLink`,
|
|
||||||
comportamentul actual, apelul existent `This.citeste_curs_bnr()` fara argumente ramane identic —
|
|
||||||
parametrul netransmis e `.F.` logic, convertit explicit la `0` cu `Vartype`), `1` = arhiva 10 zile
|
|
||||||
(`cLinkArhiva`), `2` = fisierul anual (`cLinkAn`, cu anul `Year(This.dData - 1)`). Cod de retur
|
|
||||||
neschimbat fata de v2: `0` = succes, `1` = eroare HTTP/serviciu indisponibil, `2` = nicio zi din
|
|
||||||
fereastra cautata nu exista in raspuns.
|
|
||||||
|
|
||||||
Cautarea inapoi zi cu zi (max 10 incercari) din v2 e neschimbata pentru `tnSursa=1` si e refolosita
|
|
||||||
identic pentru `tnSursa=2`. Cazul special pentru `tnSursa=2`: daca in timpul cautarii data candidata
|
|
||||||
trece in anul precedent fata de anul fisierului deja descarcat (caz real: o factura din primele zile
|
|
||||||
ale lui ianuarie are nevoie de publicarea din 31 decembrie anul trecut), se face **o singura
|
|
||||||
reincercare** — un al doilea request HTTP pe fisierul anual anterior — si cautarea continua in noul
|
|
||||||
raspuns. Nu se reincearca a doua oara peste alt an (`llRetrasAnAnterior` blocheaza a doua trecere).
|
|
||||||
|
|
||||||
**VECHI** (`onom_curs.vc2:100-136`, integral):
|
|
||||||
```
|
|
||||||
PROCEDURE citeste_curs_bnr
|
|
||||||
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
|
|
||||||
Local lcData, lcFisier, lcServer
|
|
||||||
|
|
||||||
If Empty(This.cCursor)
|
|
||||||
This.cCursor = [ccrsbnr]
|
|
||||||
Endif
|
|
||||||
creeaza_backup_cursoare(This.cCursor) && modificare 03.06.2013
|
|
||||||
If Used(This.cCursor)
|
|
||||||
Use In (This.cCursor)
|
|
||||||
Endif
|
|
||||||
|
|
||||||
lcServer = This.cLink
|
|
||||||
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
|
|
||||||
loHTTP.Open('GET', lcServer, .F.)
|
|
||||||
loHTTP.setRequestHeader("Content-Type", "application/xml;")
|
|
||||||
poLog.Log(m.lcServer)
|
|
||||||
loHTTP.Send()
|
|
||||||
|
|
||||||
If loHTTP.Status = 200
|
|
||||||
lcFisier = loHTTP.Responsebody
|
|
||||||
|
|
||||||
lcFisier = Strextract(lcFisier,[</OrigCurrency>],[</Body>])
|
|
||||||
lcData = Strextract(lcFisier,[date="],["])
|
|
||||||
lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
|
|
||||||
lcFisier= Strtran(Strtran(lcFisier,[">],[" curs="]),[</Rate>],[" />])
|
|
||||||
Xmltocursor(lcFisier,This.cCursor)
|
|
||||||
This.dData = Ttod(Ctot(lcData+[T000000]))+1
|
|
||||||
*!* modificare 03.06.2013
|
|
||||||
sterge_backup_cursoare(This.cCursor)
|
|
||||||
Else
|
|
||||||
repune_backup_cursoare(This.cCursor)
|
|
||||||
Endif
|
|
||||||
*!* modificare 03.06.2013 ^
|
|
||||||
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
|
|
||||||
ENDPROC
|
|
||||||
```
|
|
||||||
|
|
||||||
**NOU (v4, cu reparatia `Try/Catch` de la sectiunea 7):**
|
|
||||||
```
|
|
||||||
PROCEDURE citeste_curs_bnr
|
|
||||||
Lparameters tnSursa
|
|
||||||
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
|
|
||||||
Local lcData, lcFisier, lcServer, lcCubeCautat, ldCandidat, lnIncercari, lnAnCautat, llRetrasAnAnterior, lnStatus
|
|
||||||
|
|
||||||
If Vartype(tnSursa) <> "N"
|
|
||||||
tnSursa = 0
|
|
||||||
Endif
|
|
||||||
|
|
||||||
If Empty(This.cCursor)
|
|
||||||
This.cCursor = [ccrsbnr]
|
|
||||||
Endif
|
|
||||||
creeaza_backup_cursoare(This.cCursor) && modificare 03.06.2013
|
|
||||||
If Used(This.cCursor)
|
|
||||||
Use In (This.cCursor)
|
|
||||||
Endif
|
|
||||||
|
|
||||||
Do Case
|
|
||||||
Case tnSursa = 1
|
|
||||||
lcServer = This.cLinkArhiva
|
|
||||||
Case tnSursa = 2
|
|
||||||
lnAnCautat = Year(This.dData - 1)
|
|
||||||
lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
|
|
||||||
Otherwise
|
|
||||||
lcServer = This.cLink
|
|
||||||
Endcase
|
|
||||||
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
|
|
||||||
loHTTP.Open('GET', lcServer, .F.)
|
|
||||||
loHTTP.SetTimeouts(5000,5000,5000,15000)
|
|
||||||
loHTTP.setRequestHeader("Content-Type", "application/xml;")
|
|
||||||
poLog.Log(m.lcServer)
|
|
||||||
Try
|
|
||||||
loHTTP.Send()
|
|
||||||
lnStatus = loHTTP.Status
|
|
||||||
Catch
|
|
||||||
lnStatus = 0
|
|
||||||
Endtry
|
|
||||||
|
|
||||||
If lnStatus = 200
|
|
||||||
lcFisier = loHTTP.Responsebody
|
|
||||||
|
|
||||||
lcFisier = Strextract(lcFisier,[</OrigCurrency>],[</Body>])
|
|
||||||
If tnSursa = 0
|
|
||||||
lcData = Strextract(lcFisier,[date="],["])
|
|
||||||
lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
|
|
||||||
Else
|
|
||||||
lcCubeCautat = []
|
|
||||||
ldCandidat = This.dData - 1
|
|
||||||
lnIncercari = 0
|
|
||||||
llRetrasAnAnterior = .F.
|
|
||||||
Do While Empty(lcCubeCautat) And lnIncercari < 10
|
|
||||||
If tnSursa = 2 And Year(ldCandidat) < lnAnCautat And !llRetrasAnAnterior
|
|
||||||
llRetrasAnAnterior = .T.
|
|
||||||
lnAnCautat = lnAnCautat - 1
|
|
||||||
lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
|
|
||||||
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
|
|
||||||
loHTTP.Open('GET', lcServer, .F.)
|
|
||||||
loHTTP.SetTimeouts(5000,5000,5000,15000)
|
|
||||||
loHTTP.setRequestHeader("Content-Type", "application/xml;")
|
|
||||||
poLog.Log(m.lcServer)
|
|
||||||
Try
|
|
||||||
loHTTP.Send()
|
|
||||||
lnStatus = loHTTP.Status
|
|
||||||
Catch
|
|
||||||
lnStatus = 0
|
|
||||||
Endtry
|
|
||||||
If lnStatus <> 200
|
|
||||||
repune_backup_cursoare(This.cCursor)
|
|
||||||
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
Return 1
|
|
||||||
Endif
|
|
||||||
lcFisier = Strextract(loHTTP.Responsebody,[</OrigCurrency>],[</Body>])
|
|
||||||
Endif
|
|
||||||
lcData = Str(Year(ldCandidat),4) + "-" + Padl(Alltrim(Str(Month(ldCandidat))),2,"0") + "-" + Padl(Alltrim(Str(Day(ldCandidat))),2,"0")
|
|
||||||
lcCubeCautat = Strextract(lcFisier,[<Cube date="]+lcData+[">],[</Cube>])
|
|
||||||
ldCandidat = ldCandidat - 1
|
|
||||||
lnIncercari = lnIncercari + 1
|
|
||||||
Enddo
|
|
||||||
If Empty(lcCubeCautat)
|
|
||||||
repune_backup_cursoare(This.cCursor)
|
|
||||||
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
Return 2
|
|
||||||
Endif
|
|
||||||
lcFisier = [<Cube>] + lcCubeCautat + [</Cube>]
|
|
||||||
Endif
|
|
||||||
lcFisier= Strtran(Strtran(lcFisier,[">],[" curs="]),[</Rate>],[" />])
|
|
||||||
Xmltocursor(lcFisier,This.cCursor)
|
|
||||||
If tnSursa = 0
|
|
||||||
This.dData = Ttod(Ctot(lcData+[T000000]))+1
|
|
||||||
Endif
|
|
||||||
*!* modificare 03.06.2013
|
|
||||||
sterge_backup_cursoare(This.cCursor)
|
|
||||||
Else
|
|
||||||
repune_backup_cursoare(This.cCursor)
|
|
||||||
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
Return 1
|
|
||||||
Endif
|
|
||||||
*!* modificare 03.06.2013 ^
|
|
||||||
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
|
|
||||||
|
|
||||||
Return 0
|
|
||||||
ENDPROC
|
|
||||||
```
|
|
||||||
|
|
||||||
Explicatie tehnica pe scurt (nu intra in cod, doar aici):
|
|
||||||
- `tnSursa=0` (apel normal, nemodificat functional) merge exact pe ramura veche, pastreaza
|
|
||||||
`This.dData = publishingDate+1`.
|
|
||||||
- `tnSursa=1` — identic v2: cauta cel mai recent `Cube` cu data `<= This.dData - 1`, mergand inapoi
|
|
||||||
zi cu zi, max 10 incercari.
|
|
||||||
- `tnSursa=2` — acelasi mers inapoi zi cu zi, dar pe fisierul anual al anului `Year(This.dData-1)`;
|
|
||||||
daca data candidata trece in anul precedent fara sa fi gasit inca un `Cube`, face un singur request
|
|
||||||
suplimentar pe fisierul anului precedent si continua cautarea acolo. Daca acel request esueaza
|
|
||||||
(`Status<>200`), intoarce direct `1` (indisponibilitate), nu `2`.
|
|
||||||
- `This.dData` NU se rescrie pentru `tnSursa=1` sau `2` — ramane ziua ceruta, exact ce asteapta
|
|
||||||
`scrie_curs_bnr`.
|
|
||||||
|
|
||||||
## 3. `initializeaza` (`onom_curs.vc2:152-202`) — ramura `nTip=1`, cazul "prea tarziu azi": cascada + link alternativ
|
|
||||||
|
|
||||||
Se atinge STRICT ramura de la `onom_curs.vc2:170-173`. `nTip=2` si `nTip=3` raman neatinse.
|
|
||||||
|
|
||||||
**VECHI** (`onom_curs.vc2:170-173`):
|
|
||||||
```
|
|
||||||
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
|
|
||||||
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
|
|
||||||
goUrl("http://www.bnr.ro") && din wwutils.prg
|
|
||||||
Endif
|
|
||||||
```
|
|
||||||
|
|
||||||
**NOU:**
|
|
||||||
```
|
|
||||||
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
|
|
||||||
lnStareBnr = This.citeste_curs_bnr(1)
|
|
||||||
If lnStareBnr = 2
|
|
||||||
lnStareBnr = This.citeste_curs_bnr(2)
|
|
||||||
Endif
|
|
||||||
Do Case
|
|
||||||
Case lnStareBnr = 0
|
|
||||||
&& curs preluat din arhiva sau fisierul anual BNR
|
|
||||||
Case lnStareBnr = 2
|
|
||||||
amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
|
|
||||||
Otherwise
|
|
||||||
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina cursbnr.ro?",4+32,"Confirmare") = 6
|
|
||||||
goUrl("https://www.cursbnr.ro/")
|
|
||||||
Endif
|
|
||||||
Endcase
|
|
||||||
```
|
|
||||||
|
|
||||||
Cascada, exact cum a cerut Marius:
|
|
||||||
1. incearca arhiva 10 zile (`citeste_curs_bnr(1)`);
|
|
||||||
2. daca intoarce `2` (data lipsa din raspuns), incearca fisierul anual (`citeste_curs_bnr(2)`);
|
|
||||||
3. daca si acela intoarce `2` -> `"Nu exista cursul BNR pentru data specificata!"`;
|
|
||||||
4. daca oricare din cele doua intoarce `1` (eroare HTTP/timeout) -> mesajul de indisponibilitate,
|
|
||||||
cu oferta de a deschide **`https://www.cursbnr.ro/`** (nu mai `http://www.bnr.ro`, doar in aceasta
|
|
||||||
ramura — celelalte doua aparitii ale `goUrl("http://www.bnr.ro")` din fisier, ramura de dinainte
|
|
||||||
de ora 13 si `Otherwise`-ul cu `nTip` necunoscut, raman neschimbate).
|
|
||||||
|
|
||||||
`lnStareBnr` nu e declarata explicit — la fel ca `lcSql`, `lnSucces`, `ltDataOra` deja folosite in
|
|
||||||
aceeasi metoda fara declarare explicita (scop implicit PRIVATE al VFP); pastrat pentru consistenta
|
|
||||||
stilistica.
|
|
||||||
|
|
||||||
## Raspuns despre `nTip`
|
|
||||||
|
|
||||||
Neschimbat fata de v2: se acopera doar `nTip=1`; `nTip=2` si `nTip=3` raman neatinse, prin decizie
|
|
||||||
explicita a lui Marius.
|
|
||||||
|
|
||||||
## 4. Ce acopera
|
|
||||||
|
|
||||||
| Scenariu | Rezultat |
|
|
||||||
|---|---|
|
|
||||||
| Azi, dupa ora 13, Oracle nu are cursul zilei | arhiva 10 zile gaseste ziua anterioara (`lnStareBnr=0`), fara mesaj |
|
|
||||||
| Weekend (luni, dupa ora 13, vinerea nu era salvata local) | arhiva gaseste `Cube`-ul de vineri prin mersul inapoi zi cu zi |
|
|
||||||
| Sarbatoare legala (o zi lucratoare inconjurata de nelucratoare) | arhiva merge inapoi pana gaseste ultima zi publicata, fara lista de sarbatori in cod |
|
|
||||||
| Data mai veche de 10 zile lucratoare, dar in anul curent sau anul trecut | arhiva 10 zile esueaza cu `2`, se incearca fisierul anual (`cLinkAn`), care acopera restul anului |
|
|
||||||
| Factura pe inceput de ianuarie (ex. 2 ianuarie), cursul cerut e din 31 decembrie anul trecut | fisierul anual porneste pe anul curerii (`Year(This.dData-1)`), cautarea trece granita anului, se face o singura reincercare pe fisierul anului precedent |
|
|
||||||
| Data mai veche decat acopera si arhiva, si fisierul anual (inclusiv anul precedent, dupa reincercare) | `"Nu exista cursul BNR pentru data specificata!"` |
|
|
||||||
| BNR picat / timeout la oricare din cele doua incercari (arhiva sau an) | mesajul de indisponibilitate, cu oferta de a deschide `https://www.cursbnr.ro/` |
|
|
||||||
|
|
||||||
## 5. Riscuri deschise
|
|
||||||
|
|
||||||
1. **Al doilea request HTTP inline in bucla, doar pentru `tnSursa=2` la trecerea de an** — cost
|
|
||||||
suplimentar minor (o cerere HTTP in plus, o singura data, doar cand data ceruta cade in primele
|
|
||||||
zile ale unui an); restul cautarii ramane pe sirul deja descarcat in memorie.
|
|
||||||
2. **Confirmat prin fetch real**: structura `nbrfxrates2026.xml` e identica per-Cube cu celelalte
|
|
||||||
doua fisiere BNR, doar cu mai multe elemente `<Cube date="...">`, in ordine crescatoare — extractia
|
|
||||||
printr-un singur `Cube` inainte de `Xmltocursor` ramane corecta si aici.
|
|
||||||
3. **`repune_backup_cursoare` pe cursor deja inchis — risc preexistent, nu introdus acum** (aceeasi
|
|
||||||
observatie ca in v1/v2): `citeste_curs_bnr` inchide `This.cCursor` inainte de orice apel HTTP;
|
|
||||||
`repune_backup_cursoare` (`oproceduri_comune.prg:6358-6375`) face `Select` pe un alias deja inchis
|
|
||||||
in acel moment — comportament mostenit, nu nou, si in afara scope-ului acestui patch.
|
|
||||||
4. **URL-uri BNR pe host `curs.bnr.ro`**, confirmate live acum (`nbrfxrates10days.xml`,
|
|
||||||
`nbrfxrates2026.xml`); se pot schimba fara preaviz — de reverificat manual daca reapare mesajul de
|
|
||||||
indisponibilitate des dupa aplicare.
|
|
||||||
5. **Fisierul anual creste an de an** (~245 KB la mijlocul lui 2026) — cautarea foloseste tot
|
|
||||||
`Strextract` pe sirul deja incarcat in memorie, fara reincarcare per incercare; cost acceptabil
|
|
||||||
pentru o operatie declansata manual din UI, nu intr-o bucla de facturare in masa.
|
|
||||||
|
|
||||||
## 6. Cum se probeaza (fara sa ruleze acum)
|
|
||||||
|
|
||||||
Nu am rulat nimic din ce urmeaza — se lasa pentru proba manuala dupa aprobare si aplicare:
|
|
||||||
|
|
||||||
1. Aplica diff-ul (`docs\patch_curs_bnr_arhiva.diff`) pe `COMUN\clase\onom_curs.vc2` prin fluxul
|
|
||||||
text->binar existent (skill `roa-vfp-text-edit`, `txt2vcx.ps1` cu `-ProjectRoot D:\ROA\ROAFACTURARE`).
|
|
||||||
2. Caz "azi dupa ora 13": instantiaza `actualizare_curs_bnr` cu `nTip=1`, seteaza data sistemului
|
|
||||||
sau `ltDataOra` simulat dupa ora 13, cere `citeste_curs(Ttod(get_ora()))` pentru o zi pentru care
|
|
||||||
Oracle nu are inca rand — verifica in `poLog` ca al doilea link apelat e
|
|
||||||
`https://curs.bnr.ro/nbrfxrates10days.xml`.
|
|
||||||
3. Caz "mai vechi de 10 zile, in acelasi an": alege o data acoperita doar de fisierul anual —
|
|
||||||
verifica in `poLog` ca dupa arhiva urmeaza apelul catre `https://curs.bnr.ro/files/xml/years/nbrfxrates<an>.xml`.
|
|
||||||
4. Caz trecere de an: cere cursul pentru 2 ianuarie (anul curent), cand 31 decembrie anul trecut nu
|
|
||||||
e salvat local — verifica cele doua request-uri succesive catre fisierul anual curent si cel
|
|
||||||
anterior, si ca `This.dData` ramane 2 ianuarie (nu 31 decembrie).
|
|
||||||
5. Verifica in `This.cCursor` dupa apel: `Reccount()>0`, campurile `Currency`/`Curs`/`multiplier`
|
|
||||||
populate identic cu un fetch normal.
|
|
||||||
6. Verifica in Oracle, dupa `scrie_curs_bnr`, ca randul scris in `CURS` are `data` = ziua ceruta.
|
|
||||||
7. Caz negativ, mai vechi decat orice arhiva: seteaza `This.dData` cu cativa ani in urma — asteapta
|
|
||||||
mesajul "Nu exista cursul BNR pentru data specificata!".
|
|
||||||
8. Caz eroare retea: schimba temporar `cLinkArhiva` (sau `cLinkAn`) la un URL invalid — asteapta
|
|
||||||
mesajul de indisponibilitate si confirma linkul nou `https://www.cursbnr.ro/` in loc de
|
|
||||||
`http://www.bnr.ro`, doar pe aceasta ramura.
|
|
||||||
|
|
||||||
## 7. Reparatie (v4) — `loHTTP.Send()` neprotejat (host nerezolvabil)
|
|
||||||
|
|
||||||
Defect gasit de testul headless (Cazul 6, `docs\test_curs_bnr_headless.md`): `loHTTP.Send()` nu era
|
|
||||||
in `Try/Catch`. La host complet nerezolvabil (DNS esuat, eroare OLE 1429), executia sarea peste
|
|
||||||
blocul `If loHTTP.Status = 200 ... Else ... Return 1 ... Endif`, intra in bucla de cautare `Cube`
|
|
||||||
cu `lcFisier` nedefinit si iesea gresit pe `Return 2` ("data lipsa" in loc de "BNR picat").
|
|
||||||
|
|
||||||
Reparatie, pe ambele aparitii ale `loHTTP.Send()` din metoda (fisierul zilei/arhiva/an, si
|
|
||||||
reincercarea pe anul precedent):
|
|
||||||
```
|
|
||||||
Try
|
|
||||||
loHTTP.Send()
|
|
||||||
lnStatus = loHTTP.Status
|
|
||||||
Catch
|
|
||||||
lnStatus = 0
|
|
||||||
Endtry
|
|
||||||
```
|
|
||||||
`Status` se citeste o singura data, in interiorul lui `Try` — citit dupa, a doua exceptie OLE
|
|
||||||
("The data necessary...") ar fi picat imediat. `lnStatus` inlocuieste toate referintele la
|
|
||||||
`loHTTP.Status` din metoda si e adaugat la lista `Local` existenta. `Catch` gol, fara logare noua;
|
|
||||||
pe al doilea `Send()` comportamentul la esec ramane neschimbat (`repune_backup_cursoare` +
|
|
||||||
`Return 1`). Cod complet, integrat in blocul "NOU" de la sectiunea 2.
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
# Plan QA + corectii: adaugare/editare FACTURA si AVIZ
|
|
||||||
|
|
||||||
Stare: **APROBAT 17.09.2026**. Mandat de executie continua: toate stories, commit dupa fiecare,
|
|
||||||
fara aprobare per story; singura exceptie e mockup-ul de la S4, care se arata inainte de
|
|
||||||
implementare. Handoff la limita de context. Ultima actualizare: 17.09.2026.
|
|
||||||
Fisier de stare viu asociat: `docs/progres_qa_factura.md` (se actualizeaza dupa FIECARE story).
|
|
||||||
Harti de pornire (deja scrise, read-only): `docs/qa_factura_harta.md`,
|
|
||||||
`docs/qa_factura_unelte.md`, `docs/qa_factura_hooks_ref.md`.
|
|
||||||
|
|
||||||
## 0. Unde se executa
|
|
||||||
|
|
||||||
Executia trece pe **VM 304** (are D:\ROA, VFP 9, acces Oracle ROA_CENTRAL/MARIUSM_AUTO).
|
|
||||||
Motiv: UI vizibil fara sa fure focusul de pe masina lui Marius. O sesiune noua acolo porneste
|
|
||||||
din acest fisier + `docs/progres_qa_factura.md`.
|
|
||||||
|
|
||||||
Momentul mutarii (decizie Marius): **dupa S0**, cu prototipul de context/handoff deja functional.
|
|
||||||
S0 se face pe masina curenta (nu cere UI); de la S1 incolo totul e pe VM 304.
|
|
||||||
|
|
||||||
Conexiune probe: `DO test_init_env_auto_roafacturare WITH 'CENTRAL','MARIUSM_AUTO','ROMFASTSOFT'`
|
|
||||||
(`COMUN\utile\Teste\test_init_env_auto_roafacturare.prg:11,16-18`). `gnAn=2026`, `gnLuna=8`,
|
|
||||||
`gnIdFirma=110`, `gnIdUtil=8` inainte de serii.
|
|
||||||
|
|
||||||
## 1. Constatari confirmate pe cod (baza planului)
|
|
||||||
|
|
||||||
| # | Constatare | Dovada |
|
|
||||||
|---|---|---|
|
|
||||||
| C1 | Ecranul e clasa `frm_facturare_articole2`, nu `.scx`; instantiata doar cand `llFacturareNoua` | `COMUN\clase\ofacturare.vc2:15936-22614`; `COMUN\programe\ofacturare.prg:246-248` |
|
|
||||||
| C2 | Pragul de 3 caractere = `ncharcountbegin=2` pe cele doua combo-uri + garda `Len(...) <= nCharCountBegin` | `ofacturare.vc2:17524-17537`, `17560-17574`, `463-496` |
|
|
||||||
| C3 | Cautarea implicita e "incepe cu" (`RefreshData(1)`); "contine" exista dar e ascunsa dupa Ctrl+Enter | `COMUN\clase\_cb_base.vc2:636-649,720`; `ofacturare.vc2:476-489` |
|
|
||||||
| C4 | Zero feedback in timpul cautarii: `KeyPress` cheama direct `RefreshData`, fara indicator, fara numar de rezultate, fara mesaj "nimic gasit" | `_cb_base.vc2:610-776` |
|
|
||||||
| C5 | Nu exista debounce: se cauta la fiecare tasta peste prag | idem C4 |
|
|
||||||
| C6 | Sursa duplicatelor pe articol e **in Oracle**, in `pack_facturare.cursor_preturi` (join cu politicile de pret) - nu in VFP | `ofacturare.vc2:446-461`; tabela `crm_politici_pret_art` folosita la `ofacturare.vc2:21755` |
|
|
||||||
| C7 | Nu exista niciun selector de lista de preturi in formular; se trimite `poDate.id_gestiune_init`, sau `poDate.listaid` doar cand `tip=45` | `ofacturare.vc2:453-457` |
|
|
||||||
| C8 | Controalele de jos sunt copii directe ale formularului, fara container, cu Top 742/745/767/789/792 si Anchor mixt 4 vs 12; nimic nu le repozitioneaza la runtime | `ofacturare.vc2:18082-18195`; `Resize` la `21847-21854` nu le atinge |
|
|
||||||
| C9 | Factura vs aviz = `poDate.nIdTipDoc` 5 vs 6; helper `EsteAviz()` | `COMUN\programe\ofacturare_antet.prg:16-18` |
|
|
||||||
|
|
||||||
**Necunoscuta blocanta**: corpul `pack_facturare.cursor_preturi` nu e in working copy. Se citeste
|
|
||||||
din `ALL_SOURCE` pe MARIUSM_AUTO inainte de orice decizie despre listele de preturi (Story S3).
|
|
||||||
|
|
||||||
## 2. Constrangeri de perimetru
|
|
||||||
|
|
||||||
- `combosql` (`COMUN\clase\_cb_base.vc2:519`) e clasa de baza folosita de TOATE produsele ROA.
|
|
||||||
**Nu se modifica.** Tot comportamentul nou de cautare se suprascrie in `combosql_cautare`
|
|
||||||
(`ofacturare.vc2:407-531`), care e specifica facturarii.
|
|
||||||
- `ofacturare.vc2` traieste in `COMUN\` - dublu-versionat SVN+git, commit tintit din `COMUN\`.
|
|
||||||
- `.vc2` se editeaza cu script Python binar (nu `sed -i`, nu Edit pe linii cu octeti >0x7F);
|
|
||||||
diacriticele sunt cp1250. Write-back doar `txt2vcx.ps1 -ProjectRoot D:\ROA\ROAFACTURARE`.
|
|
||||||
- Dupa fiecare write-back: `MODIFY CLASS` + captura, apoi designerul se INCHIDE inainte de
|
|
||||||
urmatorul write-back.
|
|
||||||
- Un singur agent scrie pe `ofacturare.vc2` la un moment dat. Probele pe aceeasi clasa nu ruleaza
|
|
||||||
in paralel cu write-back-ul.
|
|
||||||
|
|
||||||
## 3. Stories
|
|
||||||
|
|
||||||
### S0 - Prototip context/handoff (se face PRIMUL, se foloseste pe restul planului)
|
|
||||||
|
|
||||||
Reutilizeaza ce exista: `context_watch.ps1` (in `D:\ROA\ROAGEST\COMUN\utile\`) deja citeste
|
|
||||||
`usage` din transcriptul JSONL si alerteaza la 250k/275k. Nu se rescrie. Se repara si se extinde:
|
|
||||||
|
|
||||||
1. **Bug activ**: hook-ul `SubagentStop` din `~/.claude/settings.json` re-injecteaza acelasi
|
|
||||||
mesaj runda dupa runda (observat azi, si notat deja in memorie). Cauza documentata: hook-ul
|
|
||||||
nu citeste `stop_hook_active` din JSON-ul de pe stdin si nu iese cu 0 cand e adevarat
|
|
||||||
(plafon 8 blocari consecutive). Fix: garda `stop_hook_active` la inceputul hook-ului.
|
|
||||||
2. **Fisier de stare scris de hook, nu de model**: `context_watch.ps1` primeste `-StareFile` si,
|
|
||||||
la pragul de avertisment, scrie/actualizeaza un antet in `docs/progres_<subiect>.md` (procent
|
|
||||||
context, ora, ultimul bloc). Modelul completeaza continutul; hook-ul garanteaza ca fisierul
|
|
||||||
exista si e datat chiar daca sesiunea moare.
|
|
||||||
3. **Compactare mai devreme si previzibila**: `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` in `env` din
|
|
||||||
settings (ex. 70), ca auto-compact-ul sa cada DUPA pragul de avertisment al lui
|
|
||||||
`context_watch`, nu inaintea lui. Compactarea NU poate fi declansata programatic - documentat,
|
|
||||||
deci mecanismul ramane: hook avertizeaza -> modelul scrie handoff -> compactarea vine singura.
|
|
||||||
4. Se muta in `COMUN` (cerinta Marius) ca sa fie refolosibil de toate produsele ROA.
|
|
||||||
|
|
||||||
Proba: o sesiune de proba cu un subagent care se termina => hook-ul ruleaza o singura data;
|
|
||||||
fisierul de stare exista si are marca de timp.
|
|
||||||
|
|
||||||
### S1 - Baseline QA: dovada vizuala si scenarii
|
|
||||||
|
|
||||||
Pe VM 304, formular vizibil prin `vfp_ui_harness.ps1`:
|
|
||||||
- capturi la 1366x768 si la rezolutia de lucru, pentru FACTURA si pentru AVIZ;
|
|
||||||
- captura zonei de jos (totaluri+discount) cu formularul la inaltime minima si maximizat -
|
|
||||||
dovada pentru C8 si pentru comportamentul Anchor 4 vs 12;
|
|
||||||
- scenariul de cautare: tastare progresiva pe un cod cunoscut din MARIUSM_AUTO, captura dupa
|
|
||||||
fiecare caracter - dovada pentru C2/C4;
|
|
||||||
- articol prezent in mai multe politici: captura cu randurile duplicate - dovada pentru C6.
|
|
||||||
|
|
||||||
Livrabil: `docs/qa_factura_baseline.md` + capturi. Nicio modificare de cod in S1.
|
|
||||||
|
|
||||||
### S2 - Cautare articole (toate cele patru cerinte)
|
|
||||||
|
|
||||||
In `combosql_cautare`, fara sa atinga `combosql`:
|
|
||||||
- prag de la primul caracter (`ncharcountbegin=0` pe cele doua instante + garda actualizata);
|
|
||||||
- cautare "contine" implicita, si pe cod si pe denumire;
|
|
||||||
- debounce ~250ms + limita de randuri, ca sa nu plece o interogare per tasta;
|
|
||||||
- feedback: indicator "se cauta...", numarul de rezultate, mesaj explicit la zero potriviri.
|
|
||||||
|
|
||||||
Proba: headless pe filtrul construit (asertii pe sirul trimis catre `cursor_preturi`) + proba UI
|
|
||||||
vizibila pe VM pentru feedback si latenta.
|
|
||||||
|
|
||||||
### S3 - Selector de lista de preturi (implicit lista clientului)
|
|
||||||
|
|
||||||
**Depinde de citirea `pack_facturare.cursor_preturi` din `ALL_SOURCE` pe MARIUSM_AUTO.**
|
|
||||||
Decizie Marius 17.09.2026: citirea `ALL_SOURCE` e permisa, iar modificarea pachetului e permisa
|
|
||||||
**doar pe MARIUSM_AUTO**, prin script `wip13_*.sql.txt` idempotent (`CREATE OR REPLACE`),
|
|
||||||
inregistrat in registrul de scripturi. Nimic nu pleaca spre clienti.
|
|
||||||
Tinta functionala: un combo in formular, prefixat cu lista din politica clientului, plus optiunea
|
|
||||||
"Toate listele"; in modul implicit fiecare articol apare o singura data.
|
|
||||||
|
|
||||||
### S4 - Zona totaluri + discount, regandita ca bloc
|
|
||||||
|
|
||||||
Mockup HTML inainte de implementare (mockup-urile stau online, nu in `docs/`). Dupa aprobarea
|
|
||||||
mockup-ului: container dedicat, Anchor unitar, aliniere pe grila, etichete. Atentie: `Anchor=4`
|
|
||||||
face ca Top-ul design-time sa conteze la rulare - orice schimbare de inaltime se probeaza la
|
|
||||||
1366 maximizat.
|
|
||||||
|
|
||||||
### S5 - Regresie
|
|
||||||
|
|
||||||
Suita existenta + probele noi, pe FACTURA si pe AVIZ, adaugare si editare. Raport prin
|
|
||||||
`raport_teste.ps1`, nu loguri brute.
|
|
||||||
|
|
||||||
## 4. Ordine si commit
|
|
||||||
|
|
||||||
S0 -> S1 -> S2 -> S3 -> S4 -> S5. Commit pe branch de lucru dupa fiecare story (cod `COMUN` din
|
|
||||||
`COMUN\`, restul din radacina), fara push; SVN si merge raman la Marius.
|
|
||||||
`docs/progres_qa_factura.md` se actualizeaza dupa fiecare story, nu la final.
|
|
||||||
Documentele de lucru din `docs/` nu se comit pe main.
|
|
||||||
|
|
||||||
## 5. Datorii deschise cunoscute la scrierea planului
|
|
||||||
|
|
||||||
- Corpul `pack_facturare.cursor_preturi` necitit - blocheaza S3.
|
|
||||||
- Cifra plafonului de blocari repetate ale unui stop hook: 8 (confirmat in ghid) vs 100
|
|
||||||
(`CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`), nereconciliat.
|
|
||||||
- Daca `PreCompact` poate bloca compactarea prin exit 2: nedocumentat.
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
# Progres QA factura/aviz
|
|
||||||
|
|
||||||
Plan: `docs/plan_qa_factura_aviz.md` (aprobat 17.09.2026, mandat de executie continua).
|
|
||||||
Acest fisier spune **unde s-a ajuns**, nu ce e de facut. Se actualizeaza dupa FIECARE story.
|
|
||||||
|
|
||||||
Harti de pornire (read-only, nu se refac):
|
|
||||||
`docs/qa_factura_harta.md` - harta de cod a formularului
|
|
||||||
`docs/qa_factura_unelte.md` - harness UI, headless, Oracle, hook-uri existente
|
|
||||||
`docs/qa_factura_hooks_ref.md` - referinta oficiala de hook-uri
|
|
||||||
|
|
||||||
## Stare pe story
|
|
||||||
|
|
||||||
| Story | Stare | Unde |
|
|
||||||
|---|---|---|
|
|
||||||
| S0 prototip context/handoff | **TERMINAT** 17.09.2026 | `docs/s0_context_hook.md`, `docs/handoff_activ.md` |
|
|
||||||
| S1 baseline QA cu capturi | de facut, **pe VM 304** | - |
|
|
||||||
| S2 cautare articole | de facut | - |
|
|
||||||
| S3 selector lista de preturi | blocat pana se citeste `pack_facturare.cursor_preturi` | - |
|
|
||||||
| S4 zona totaluri+discount | de facut, mockup inainte | - |
|
|
||||||
| S5 regresie | de facut | - |
|
|
||||||
|
|
||||||
## S0 - terminat (doua runde)
|
|
||||||
|
|
||||||
**Runda 2 (dupa ce prima s-a dovedit nedovedita).** Prima varianta a lui S0 a fost probata doar
|
|
||||||
cu stdin sintetic; in ciclul real bucla a continuat. Am instalat logare temporara pe hook si am
|
|
||||||
capturat inputul real al lui `SubagentStop`. Ce a iesit:
|
|
||||||
|
|
||||||
- `stop_hook_active` EXISTA in input si trece pe `true` la a doua declansare - garda e corecta.
|
|
||||||
Bucla observata venea de la agenti porniti inainte de fix.
|
|
||||||
- Mecanismul buclei: mesajul injectat trezeste agentul idle, el raspunde, redevine idle, hook-ul
|
|
||||||
se declanseaza iar.
|
|
||||||
- **Inputul contine `agent_transcript_path`** - transcriptul separat al subagentului oprit.
|
|
||||||
Deci un hook POATE masura contextul fiecarui subagent, nu doar al sesiunii.
|
|
||||||
|
|
||||||
Consecinta: `context_watch.ps1` are acum `-Subagent` (citeste `agent_transcript_path`, praguri
|
|
||||||
150k/200k, mai joase decat la sesiunea principala) si `-Json` (ambaleaza in
|
|
||||||
`hookSpecificOutput`). Garda `stop_hook_active` a intrat in script, deci comanda hook-ului e o
|
|
||||||
singura linie. Logarea temporara e scoasa.
|
|
||||||
|
|
||||||
Probe pe input real capturat: subagent peste prag -> mesaj cu numele agentului si 40k; sub prag
|
|
||||||
-> tacere; `stop_hook_active=true` -> tacere. Comis: `9823f1e` pe `qa-factura-s0`.
|
|
||||||
|
|
||||||
Compromis acceptat: mesajul vechi "verifica daca subagentul si-a scris starea pe disc" nu mai
|
|
||||||
apare la fiecare oprire, ci doar in avertismentul de context.
|
|
||||||
|
|
||||||
**Corectie de fond**, dupa ce Marius a contestat afirmatia: e fals ca "niciun hook nu poate afla
|
|
||||||
contextul". Niciun hook nu primeste tokenii de-a gata (nici `command`, nici function hook din
|
|
||||||
SDK), dar orice hook primeste `transcript_path` si poate citi singur `usage` din JSONL. Doar
|
|
||||||
`statusLine` primeste `context_window.used_percentage` gata calculat, si nu e hook.
|
|
||||||
Detalii: `docs/hooks_functii_context.md`, condensate in `docs/handoff_activ.md`.
|
|
||||||
|
|
||||||
## S0 - runda 1
|
|
||||||
|
|
||||||
Trei modificari, toate probate (rezultatele probelor sunt in `docs/handoff_activ.md`):
|
|
||||||
|
|
||||||
1. `C:\Users\mmari\.claude\settings.json`, hook `SubagentStop`: garda `stop_hook_active`. Era un
|
|
||||||
`echo` static care reinjecta acelasi `additionalContext` la fiecare oprire de subagent - de
|
|
||||||
aici bucla observata azi (patru agenti invartindu-se in gol). Backup:
|
|
||||||
`settings.json.bak_20260917`.
|
|
||||||
2. `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`: parametru nou `-StareFile`. La atingerea
|
|
||||||
pragului scrie/inlocuieste un singur antet pe primul rand al fisierului de stare
|
|
||||||
(`<!-- context_watch: data ora | tokeni: N | nivel: ... -->`), fara sa atinga restul
|
|
||||||
continutului. Fara `-StareFile` comportamentul e neschimbat.
|
|
||||||
3. `settings.json`, `env`: `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`. Pus initial pe 70 - **gresit**:
|
|
||||||
fereastra e 1M, deci 70% = 700k, la trei ori distanta de pragul de handoff de 250k. Marius a
|
|
||||||
prins eroarea. Valoarea corecta, aplicata: **30** (=300k), imediat peste pragul max de 275k.
|
|
||||||
|
|
||||||
Ce s-a stabilit si nu se mai rediscuta:
|
|
||||||
- Compactarea **nu** poate fi declansata programatic dintr-un hook. Mecanismul ramas: hook
|
|
||||||
avertizeaza -> modelul scrie handoff-ul -> compactarea vine singura la pragul coborat.
|
|
||||||
- Niciun hook nu primeste numarul de tokeni. Singura sursa e transcriptul JSONL
|
|
||||||
(`transcript_path`), pe care `context_watch.ps1` il citea deja.
|
|
||||||
- `D:\ROA\ROAGEST\COMUN` si `D:\ROA\ROAFACTURARE\COMUN` sunt checkout-uri separate ale aceluiasi
|
|
||||||
repo `comun.git`. Scriptul e deci deja in biblioteca partajata; nu trebuie mutat.
|
|
||||||
|
|
||||||
Ramas deschis din S0:
|
|
||||||
- Proba pe un ciclu real cu subagent viu (garda a fost probata in izolare, cu stdin sintetic).
|
|
||||||
Se confirma implicit la prima rulare de subagent din sesiunea urmatoare.
|
|
||||||
- `settings.json` trimite la `context_watch.ps1` prin calea din checkout-ul ROAGEST. Pe VM 304
|
|
||||||
trebuie verificat ca `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1` exista acolo, altfel
|
|
||||||
hook-ul tace fara sa dea eroare.
|
|
||||||
|
|
||||||
## VM 304 - ce s-a aflat
|
|
||||||
|
|
||||||
Din `docs/vm304_acces.md`: Proxmox VM ID 304 "Win11-Marius", nod `pvemini`, clona lui VM 303.
|
|
||||||
Acces probat si functional: `ssh root@10.0.20.201 'qm agent 304 ping'` -> raspunde;
|
|
||||||
comenzi in VM prin `qm guest exec 304 -- <cmd>`. **Nu exista share UNC** catre discul ei.
|
|
||||||
Documentatia NU mentioneaza VFP sau client Oracle instalat - de verificat pe teren, planul le
|
|
||||||
presupune. Credentialele sunt documentate in `E:\proiecte\ROMFASTSQL\docs\` (nu se copiaza).
|
|
||||||
|
|
||||||
Marius a cerut explicit copierea directa si `roa_sync` rulat pe VM. Pachetul de pornire e
|
|
||||||
pregatit in scratchpad (`pachet_vm304\` cu `PORNIRE.md` + cele 8 documente + `context_watch.ps1`);
|
|
||||||
roa_sync pe VM a fost anulat (svn fara credentiale in sesiunea 0); il ruleaza Marius.
|
|
||||||
|
|
||||||
## Urmatorul pas
|
|
||||||
|
|
||||||
S1, **pe VM 304**: sesiune noua care porneste din `docs/plan_qa_factura_aviz.md` + acest fisier.
|
|
||||||
Baseline vizual pentru FACTURA si AVIZ, capturi la 1366x768 si maximizat, dovada pentru
|
|
||||||
constatarile C2, C4, C6 si C8 din plan. Nicio modificare de cod in S1.
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
# Harta cod: adaugare/editare FACTURA si AVIZ (read-only)
|
|
||||||
|
|
||||||
Sursa: text FoxBin2Prg deja in-tree (`.vc2`/`.prg`), citit cu `Grep`/`Read` si indexat cu
|
|
||||||
`vfp_symbols.ps1` (`-CacheRoot/-ProjectRoot D:\ROA\ROAFACTURARE`,
|
|
||||||
`-IndexFile D:\ROA\_vfp_textcache\roafacturare\_symbols.tsv`). Nicio modificare de fisier.
|
|
||||||
|
|
||||||
## 1. Formulare si clase implicate
|
|
||||||
|
|
||||||
Nu exista formular `.scx` pentru factura/aviz in acest proiect (`.scx`-urile din arbore sunt
|
|
||||||
doar utilitare: `frm_borderou_facturi.scx`, `frm_import_efactura.scx` etc.). Ecranul de
|
|
||||||
adaugare/editare e o **clasa `.vcx` instantiata prin `Createobject()`**, nu un `DO FORM`:
|
|
||||||
|
|
||||||
- **`frm_facturare_articole2`** — `COMUN\clase\ofacturare.vcx` (text `COMUN\clase\ofacturare.vc2:15936-22614`).
|
|
||||||
Formularul unificat, curent, folosit in productie pentru factura SI aviz. Instantiat in
|
|
||||||
`COMUN\programe\ofacturare.prg:248` (`ofrmdetaliifactura = Createobject('frm_facturare_articole2')`)
|
|
||||||
si in `COMUN\clase\ofacturare_comun.vc2:4028`, doar cand `llFacturareNoua` e adevarat
|
|
||||||
(`COMUN\programe\ofacturare.prg:246-248`).
|
|
||||||
- **`frm_facturare_articole`** — acelasi fisier, `ofacturare.vc2:11123-15936`. Varianta mai veche;
|
|
||||||
in cod de productie nu mai e instantiata (doar in probele din
|
|
||||||
`COMUN\utile\Teste\facturare_unificat\*.prg` si `COMUN\utile\Teste\editare_factura\creeaza_documente_s8.prg:571`,
|
|
||||||
`emite_document_stoc_s93.prg:264`) — ramane in clasa dar calea curenta e `frm_facturare_articole2`.
|
|
||||||
- **Flux vechi, ne-unificat** (`llFacturareNoua = .F.`, `ofacturare.prg:255-266`) — alege un
|
|
||||||
formular de antet separat pe tip de document, fara grila unificata de articole:
|
|
||||||
- `frm_date_aviz_lucrare` — `ofacturare.vc2:7775` — pentru `tnTip` 27 sau 30.
|
|
||||||
- `frm_date_factura` — `ofacturare.vc2:8637` — pentru `tnTip < 21` sau in `(45,48,49,51,52)`.
|
|
||||||
- `frm_date_aviz` — `ofacturare.vc2:6721` — altfel (aviz).
|
|
||||||
- **`oAntetFacturare`** (`Define Class ... As Custom`) — `COMUN\programe\ofacturare_antet.prg:14`.
|
|
||||||
Logica de antet a lui `frm_facturare_articole2`: cautari (`do_cauta_*`), validare
|
|
||||||
(`valideaza_antet`), schimbarea tipului de document (`alege_tipdoc`, `schimba_tipdoc`).
|
|
||||||
Header-ul fisierului (`ofacturare_antet.prg:1-7`) spune explicit ca a fost **portata din
|
|
||||||
`frm_date_factura`/`frm_date_aviz`** cand s-a facut unificarea. Instantiata in
|
|
||||||
`frm_facturare_articole2.Init`: `This.oAntet = Createobject('oAntetFacturare')` (`ofacturare.vc2:21365`).
|
|
||||||
- **`oDateFactura`** (`poDate`) — `COMUN\programe\ofacturare_comun.prg:131`. Obiectul de stare al
|
|
||||||
documentului curent (creat in `ofacturare.prg:196`); tine `nIdTipDoc`, `nIdTipDocFactura`(=5),
|
|
||||||
`nIdTipDocAvizExpeditie`(=6), `id_gestiune_init`, `listaid`, `id_pol`, `zi_curs` etc.
|
|
||||||
- **`ofacturare_editare.prg`** (`COMUN\programe\ofacturare_editare.prg`, 1444 linii) — flux **separat**,
|
|
||||||
pentru editarea liniilor unei facturi/aviz deja emise (nu formularul de creare):
|
|
||||||
`IncarcaAntetFacturaEditare` (63), `IncarcaLiniiFacturaEditare` (209),
|
|
||||||
`PregatesteArticoleFacturaEditare` (605), `ScrieArticoleFacturaEditate` (741),
|
|
||||||
clasa `ArticoleNotaEditor As Custom` (1226). Inregistrat via
|
|
||||||
`Set Procedure To ofacturare_editare.prg Additive` (`Programe\roafacturare.prg:219`).
|
|
||||||
- **`combosql_cautare`** — clasa de baza pentru comboboxurile de cautare din grid (vezi pct. 2),
|
|
||||||
`ofacturare.vc2:407-531`, mostenind `combosql As combobox` din `COMUN\clase\_cb_base.vc2:519`.
|
|
||||||
|
|
||||||
## 2. Zona de introducere articole (cautare cod material / denumire)
|
|
||||||
|
|
||||||
In grila `grd_factura` a lui `frm_facturare_articole2`, coloanele au cate un combobox de tip
|
|
||||||
`combosql_cautare`:
|
|
||||||
|
|
||||||
- `grd_factura.cCodMat.cboCodmat` — `ADD OBJECT` la `ofacturare.vc2:17524-17537`,
|
|
||||||
`ncharcountbegin = 2`, `pcursorname = crsCodmat`, `pfieldactiv = codmat`.
|
|
||||||
- `grd_factura.cDenumire.cCboDenumire` — `ADD OBJECT` la `ofacturare.vc2:17560-17574`,
|
|
||||||
`ncharcountbegin = 2`, `pcursorname = crsDenumire`, `pfieldactiv = denumire`.
|
|
||||||
|
|
||||||
**Pragul de caractere**: logica e in `combosql_cautare.refreshdata` (`ofacturare.vc2:463-496`):
|
|
||||||
|
|
||||||
```
|
|
||||||
lnTip = Iif(Type('tnTip') = 'N', m.tnTip, 0)
|
|
||||||
If m.lnTip <> 0 And Len(Alltrim(This.cSearchString)) <= This.nCharCountBegin
|
|
||||||
Return .T. && nu cauta, iese
|
|
||||||
Endif
|
|
||||||
```
|
|
||||||
|
|
||||||
Cu `ncharcountbegin = 2` pe ambele combo-uri, filtrarea porneste abia cand
|
|
||||||
`Len(cSearchString) > 2`, adica de la **al treilea caracter tastat**.
|
|
||||||
|
|
||||||
**Interactivechange/Keypress**: nu sunt suprascrise in `combosql_cautare` — vin din parintele
|
|
||||||
`combosql` (`COMUN\clase\_cb_base.vc2:519`):
|
|
||||||
- `InteractiveChange` (linia 606-608): doar reseteaza `cSearchString`.
|
|
||||||
- `KeyPress` (linia 610-776): construieste `csearchstring` caracter cu caracter (sageti,
|
|
||||||
Del/Backspace tratate separat), apoi cheama `This.RefreshData(1)` = cautare "incepe cu"
|
|
||||||
(linia 720) sau, la Ctrl+Enter, `RefreshData(2)` = cautare "contine" (liniile 636-649).
|
|
||||||
|
|
||||||
**Filtrul/SQL aplicat**: `refreshdata` seteaza `cfiltrucod`/`cfiltruden` in functie de tip
|
|
||||||
(`ofacturare.vc2:476-489`):
|
|
||||||
```
|
|
||||||
Case lnTip = 1 (incepe cu): cfiltrucod/cfiltruden = cSearchString + '%'
|
|
||||||
Case lnTip = 2 (contine): cfiltrucod/cfiltruden = '%' + cSearchString + '%'
|
|
||||||
```
|
|
||||||
apoi `cursor_preturi_call()` (`ofacturare.vc2:446-461`) construieste apelul catre pachetul
|
|
||||||
Oracle `pack_facturare.cursor_preturi(?poDate.zi_curs,?poDate.tip,?poDate.id_valuta,
|
|
||||||
?poDate.id_gestiune_init sau ?poDate.listaid,?gnLuna,?gnAn,?gnIdUtil,?gnIdSucursala,
|
|
||||||
?pcFiltruCod,?pcFiltruDen)` (sau `pack_facturare.cursor_gestiune(...)` cand `poDate.tip = 41`),
|
|
||||||
executat prin `goExecutor.oExecuta` in `selectdata` (linia 514-529). Corpul PL/SQL al
|
|
||||||
pachetului nu e in acest repo VFP (schema Oracle) — nu l-am putut citi read-only de aici.
|
|
||||||
Proprietatile `csourcesql`/`csourcewhere` de pe `ADD OBJECT` (ex. `select codmat, denumire, ...
|
|
||||||
from vnom_articole ... where inactiv = 0`) sunt doar sablonul design-time al cursorului gol
|
|
||||||
(`creeaza_cursor_gol`, linia 426), nu interogarea reala rulata la tastare.
|
|
||||||
|
|
||||||
**Sincronizare cod<->denumire** dupa alegere: `grd_factura.cCodMat.cboCodmat.LostFocus`
|
|
||||||
(`ofacturare.vc2:22088-22109`) si perechea ei `cDenumire.cCboDenumire.LostFocus`
|
|
||||||
(22115-22136) cheama `thisform.do_adauga_articol_cautat(loArticol, loArticol.cantitate)`
|
|
||||||
si fortez re-creerea cursorului celeilalte combo (seteaza `cSearchString` cu valoarea gasita
|
|
||||||
si reseteaza `RowSource`), ca sa afiseze articolul ales in ambele coloane.
|
|
||||||
|
|
||||||
## 3. Lista de preturi / politici de pret
|
|
||||||
|
|
||||||
- `poDate.id_gestiune_init` e parametrul implicit trimis la `pack_facturare.cursor_preturi`;
|
|
||||||
cand `poDate.tip = 45` se trimite `poDate.listaid` in loc (`ofacturare.vc2:453-457`).
|
|
||||||
- De ce poate aparea un articol de mai multe ori: interogarea serverului (Oracle,
|
|
||||||
`pack_facturare.cursor_preturi`) uneste nomenclatorul de articole cu politicile de pret —
|
|
||||||
confirmarea client-side ca politicile sunt cheia vine din `modifica_lista_preturi`
|
|
||||||
(`ofacturare.vc2:21735-21837`), care interogheaza direct tabelele de politici:
|
|
||||||
`Select id_pol_art, id_venchelt, proc_tvav from crm_politici_pret_art where id_pol = ...
|
|
||||||
and id_articol = ...` (linia 21755) si scrie prin `pack_preturi.adauga_politica_pret_art`/
|
|
||||||
`pack_preturi.modifica_pret_pol_pret_art` (21779, 21792). Corpul exact al join-ului din
|
|
||||||
`cursor_preturi` e in PL/SQL, nevizibil din acest working copy VFP — afirmatia despre
|
|
||||||
"un rand per politica aplicabila" e o inferenta din tabelele folosite, nu o citire directa
|
|
||||||
a interogarii.
|
|
||||||
|
|
||||||
## 4. Zona de jos: controale de discount/totaluri
|
|
||||||
|
|
||||||
Toate sunt copii directe ale formularului `frm_facturare_articole2` (fara container
|
|
||||||
intermediar), clasa `_textbox` din `_baza.vcx`, asezate sub grila (Top ~742-792):
|
|
||||||
|
|
||||||
| Obiect | Top | Left | Width | Anchor | Note |
|
|
||||||
|---|---|---|---|---|---|
|
|
||||||
| `tx_total_baza_nat` (18123) | 745 | 17 | (implicit) | 4 | ReadOnly, `ControlSource=thisform.nbazaron` |
|
|
||||||
| `tx_total_baza_val` (18137) | 789 | 17 | (implicit) | 4 | ReadOnly, `thisform.nbazaval` |
|
|
||||||
| `tx_disc_factura_nat` (18082) | 745 | 351 | (implicit) | 4 | ReadOnly, `thisform.ndiscfactron` |
|
|
||||||
| `tx_disc_factura_procent` (18096) | 767 | 542 | 54 | 4 | editabil, `Value=0` |
|
|
||||||
| `tx_disc_factura_val` (18109) | 789 | 351 | (implicit) | 4 | ReadOnly, `thisform.ndiscfactval` |
|
|
||||||
| `tx_total_tva_nat` (18181) | 742 | 689 | (implicit) | 12 | ReadOnly, `thisform.ntvaron` |
|
|
||||||
| `tx_total_tva_val` (18195) | 792 | 689 | (implicit) | 12 | ReadOnly, `thisform.ntvaval` |
|
|
||||||
| `tx_total_factura_nat` (18151) | 742 | 833 | (implicit) | 12 | ReadOnly, FontBold, `thisform.ntotalron` |
|
|
||||||
| `tx_total_factura_val` (18166) | 792 | 835 | (implicit) | 12 | ReadOnly, FontBold, `thisform.ntotalval` |
|
|
||||||
|
|
||||||
(linii = `COMUN\clase\ofacturare.vc2`). `Anchor=4` = ancorat de Bottom (coboara odata cu
|
|
||||||
formularul); `Anchor=12` (4+8) = Bottom+Right, pe coloana totalurilor din dreapta. Niciun
|
|
||||||
`Height`/`Width` explicit pe majoritatea — mostenesc default-ul clasei `_textbox`; doar
|
|
||||||
`tx_disc_factura_procent` are `Width=54` explicit. Nu am gasit cod care repozitioneaza
|
|
||||||
aceste controale la runtime in `frm_facturare_articole2` — pozitionarea e strict design-time
|
|
||||||
+ `Anchor` (fara logica de `Resize`/`Move` pe ele; `Resize` al formularului, linia
|
|
||||||
21847-21854, nu le atinge explicit).
|
|
||||||
|
|
||||||
## 5. Diferente factura vs aviz pe `frm_facturare_articole2`
|
|
||||||
|
|
||||||
- **Discriminator unic**: `poDate.nIdTipDoc`, comparat cu `poDate.nIdTipDocFactura` (=5) /
|
|
||||||
`poDate.nIdTipDocAvizExpeditie` (=6). Helper: `oAntetFacturare.EsteAviz()`
|
|
||||||
(`ofacturare_antet.prg:16-18`): `Return poDate.nIdTipDoc = poDate.nIdTipDocAvizExpeditie`.
|
|
||||||
- **Setare initiala**, dupa `tnTip` primit la deschidere (`COMUN\programe\ofacturare.prg:196-206`):
|
|
||||||
```
|
|
||||||
Case tnTip = 27 or 30 -> nIdTipDoc = 6 (AVIZ)
|
|
||||||
Case tnTip < 21 sau tnTip in (45,48,49,51,52) -> nIdTipDoc = 5 (FACTURA)
|
|
||||||
Otherwise -> nIdTipDoc = 6 (AVIZ)
|
|
||||||
```
|
|
||||||
- **Schimbare interactiva**: combo `clb_fdoc.cboFdoc` -> `do_schimba_tipdoc`
|
|
||||||
(`ofacturare.vc2:20046-20071`) -> `oAntet.alege_tipdoc(valoare_combo)` mapeaza textul
|
|
||||||
("FACTURA"/"AVIZ"/"PROFORMA"/"BON FISCAL") pe id-ul de tip, apoi `oAntet.schimba_tipdoc(id)`
|
|
||||||
(`ofacturare_antet.prg:889-923`) actualizeaza `poDate.nIdTipDoc` si seriile. Exista o garda:
|
|
||||||
daca documentul e proforma si au fost deja adaugate articole, schimbarea tipului e blocata
|
|
||||||
cu `amessagebox` (`ofacturare.vc2:20050-20055`).
|
|
||||||
- **Ramuri `EsteAviz()` in `oAntetFacturare`** (aceleasi metode de cautare/validare servesc
|
|
||||||
ambele tipuri, dar cu cai diferite): `do_cauta_altele` (20-45), `do_cauta_client` (97-100),
|
|
||||||
`do_cauta_comanda` (240-243), `do_cauta_contract` (296-299), `do_cauta_gestiune_init`
|
|
||||||
(518-521), `do_cauta_lucrare` (553-556), `do_cauta_sectie` (633-636), `do_cauta_venchelt`
|
|
||||||
(708-711), `valideaza_antet` (762-766) — toate in `COMUN\programe\ofacturare_antet.prg`.
|
|
||||||
- **Fluxul vechi (ne-unificat)** folosea deja formulare separate per tip pentru antet
|
|
||||||
(`frm_date_factura` / `frm_date_aviz` / `frm_date_aviz_lucrare`, vezi pct. 1) — unificarea
|
|
||||||
descrisa in header-ul `ofacturare_antet.prg` a mutat logica lor comuna intr-o singura clasa,
|
|
||||||
ramura aleasa la runtime pe `poDate.nIdTipDoc`.
|
|
||||||
@@ -1,393 +0,0 @@
|
|||||||
# Referinta oficiala hooks Claude Code (pentru diagnosticul "subagent stop violation")
|
|
||||||
|
|
||||||
Surse fetch-uite azi (2026-09-17), toate redirecteaza de pe `docs.claude.com` pe `code.claude.com`:
|
|
||||||
- https://code.claude.com/docs/en/hooks (fost https://docs.claude.com/en/docs/claude-code/hooks)
|
|
||||||
- https://code.claude.com/docs/en/hooks-guide (fost .../hooks-guide)
|
|
||||||
- https://code.claude.com/docs/en/settings (fost .../settings)
|
|
||||||
- https://code.claude.com/docs/en/settings-reference
|
|
||||||
- https://code.claude.com/docs/en/settings-example
|
|
||||||
- https://code.claude.com/docs/en/env-vars
|
|
||||||
|
|
||||||
**Limitare tehnica intalnita**: paginile `hooks` si `settings-reference` sunt prea mari pentru
|
|
||||||
fetch-ul folosit (WebFetch trece continutul brut printr-un model mic inainte sa-l intoarca); la
|
|
||||||
cereri repetate pe aceeasi pagina, portiuni identice (tabelul "Common input fields", tabelul
|
|
||||||
"Exit code 2 behavior per event" pana la randul `Stop`, exemplele din `hooks-guide`) au iesit
|
|
||||||
IDENTIC de mai multe ori — acelea sunt tratate mai jos ca sigure/verbatim. O extractie initiala,
|
|
||||||
mai larga, a produs scheme JSON pentru `SessionStart`/`Stop`/`SubagentStop`/`PreCompact` care NU
|
|
||||||
s-au mai reprodus la cereri ulterioare tintite pe aceleasi sectiuni (acelea au raspuns explicit
|
|
||||||
"nu e in continutul furnizat, pagina e trunchiata") — acea extractie e tratata ca **nesigura** si
|
|
||||||
nu e citata mai jos. Sectiunile marcate **NEDOCUMENTAT** de mai jos sunt cele pe care nu am putut
|
|
||||||
sa le confirm verbatim, nu neaparat cele care lipsesc din documentatia reala.
|
|
||||||
|
|
||||||
## 1. Lista completa a evenimentelor de hook
|
|
||||||
|
|
||||||
Tabelul de mai jos e citat verbatim din `hooks-guide` (sectiunea "How hooks work"), confirmat prin
|
|
||||||
citire directa a continutului brut al fetch-ului (nu prin sumarizare):
|
|
||||||
|
|
||||||
> | Event | When it fires |
|
|
||||||
> | `SessionStart` | When a session begins or resumes |
|
|
||||||
> | `Setup` | When you start Claude Code with `--init-only`, or with `--init` or `--maintenance` in `-p` mode. For one-time preparation in CI or scripts |
|
|
||||||
> | `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
|
|
||||||
> | `UserPromptExpansion` | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
|
|
||||||
> | `PreToolUse` | Before a tool call executes. Can block it |
|
|
||||||
> | `PermissionRequest` | When a tool call needs a permission decision |
|
|
||||||
> | `PermissionDenied` | When auto mode denies a tool call, including denials without a classifier verdict. ... |
|
|
||||||
> | `PostToolUse` | After a tool call succeeds |
|
|
||||||
> | `PostToolUseFailure` | After a tool call fails |
|
|
||||||
> | `PostToolBatch` | After a full batch of parallel tool calls resolves, before the next model call |
|
|
||||||
> | `Notification` | When Claude Code sends a notification |
|
|
||||||
> | `MessageDisplay` | While assistant message text is displayed |
|
|
||||||
> | `SubagentStart` | When a subagent is spawned |
|
|
||||||
> | `SubagentStop` | When a subagent finishes |
|
|
||||||
> | `TaskCreated` | When a task is being created via `TaskCreate` |
|
|
||||||
> | `TaskCompleted` | When a task is being marked as completed |
|
|
||||||
> | `Stop` | When Claude finishes responding |
|
|
||||||
> | `StopFailure` | When the turn ends due to an API error |
|
|
||||||
> | `TeammateIdle` | When an agent team teammate is about to go idle |
|
|
||||||
> | `InstructionsLoaded` | When a CLAUDE.md or `.claude/rules/*.md` file is loaded into context. ... |
|
|
||||||
> | `ConfigChange` | When a configuration file changes during a session |
|
|
||||||
> | `CwdChanged` | When the working directory changes, for example when Claude executes a `cd` command. ... |
|
|
||||||
> | `DirectoryAdded` | When a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request |
|
|
||||||
> | `FileChanged` | When a watched file changes on disk. The `matcher` field specifies which filenames to watch |
|
|
||||||
> | `WorktreeCreate` | When a worktree is being created via `--worktree`, `isolation: "worktree"`, or for a background session. ... |
|
|
||||||
> | `WorktreeRemove` | When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session |
|
|
||||||
> | `PreCompact` | Before context compaction |
|
|
||||||
> | `PostCompact` | After context compaction completes |
|
|
||||||
> | `PreModelSwitch` | Before Claude Code applies a model switch that you or a client requested. Can block the switch |
|
|
||||||
> | `PostModelSwitch` | After the session's model changes, including changes Claude Code makes on its own, ... |
|
|
||||||
> | `Elicitation` | When an MCP server requests user input during a tool call |
|
|
||||||
> | `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
|
|
||||||
> | `SessionEnd` | When a session terminates |
|
|
||||||
|
|
||||||
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "How hooks work".
|
|
||||||
|
|
||||||
### Schema JSON exacta pe stdin/stdout pentru PreCompact / SessionStart / Stop / SubagentStop
|
|
||||||
|
|
||||||
**NEDOCUMENTAT (in sensul de mai sus: nu am putut confirma verbatim)**. Fetch-ul repetat pe
|
|
||||||
`hooks` (inclusiv varianta `.md`) intoarce constant tabelul "Common input fields" (comun tuturor
|
|
||||||
evenimentelor, vezi mai jos) si tabelul "Exit code 2 behavior per event" doar pana la randul
|
|
||||||
`Stop`; sectiunile individuale `### SessionStart`, `### PreCompact`, `### Stop`, `### SubagentStop`
|
|
||||||
cu exemplele lor JSON complete nu au putut fi extrase — raspunsul explicit al fetch-ului a fost
|
|
||||||
"the actual detailed 'Hook events' section ... appears to be cut off or not included" si "I
|
|
||||||
cannot find a subsection literally titled 'PreCompact'... in the provided content". Nu inseamna
|
|
||||||
ca documentatia oficiala nu contine acele scheme (aproape sigur le contine, pagina fiind
|
|
||||||
"Hooks reference" completa), ci ca uneltele disponibile in aceasta sesiune nu au putut sa le
|
|
||||||
aduca integral.
|
|
||||||
|
|
||||||
Ce **s-a confirmat verbatim** despre campurile comune (tabelul "Common input fields", identic la
|
|
||||||
doua fetch-uri separate pe `hooks` si pe `hooks.md`):
|
|
||||||
|
|
||||||
> | Field | Description |
|
|
||||||
> | `session_id` | Current session identifier |
|
|
||||||
> | `prompt_id` | UUID identifying the user prompt currently being processed. ... Absent until the first user input. Requires Claude Code v2.1.196 or later |
|
|
||||||
> | `transcript_path` | Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires. Hooks that need the final assistant text of the current turn should use `last_assistant_message` on Stop and SubagentStop instead of reading the transcript |
|
|
||||||
> | `cwd` | Current working directory when the hook is invoked |
|
|
||||||
> | `scratchpad_dir` | Path to the session's scratchpad directory, where Claude keeps temporary working files. Absent when the session has no scratchpad or the temp directory is unavailable. Requires Claude Code v2.1.257 or later |
|
|
||||||
> | `permission_mode` | Current permission mode: `"default"`, `"plan"`, `"acceptEdits"`, `"auto"`, `"dontAsk"`, or `"bypassPermissions"`. ... |
|
|
||||||
> | `effort` | Object with a `level` field holding the effort level in effect when the hook runs... Present for events that fire within a tool-use context, such as `PreToolUse`, `PostToolUse`, `Stop`, and `SubagentStop`, when the current model supports the effort parameter. |
|
|
||||||
> | `hook_event_name` | Name of the event that fired |
|
|
||||||
>
|
|
||||||
> When running with `--agent` or inside a subagent, two additional fields are included:
|
|
||||||
>
|
|
||||||
> | Field | Description |
|
|
||||||
> | `agent_id` | Unique identifier for the subagent. Present only when the hook fires inside a subagent call. Use this to distinguish subagent hook calls from main-thread calls. |
|
|
||||||
> | `agent_type` | Agent name (for example, `"Explore"` or `"security-reviewer"`). Present when the session uses `--agent` or the hook fires inside a subagent. For subagents, the subagent's type takes precedence over the session's `--agent` value. |
|
|
||||||
|
|
||||||
Sursa: https://code.claude.com/docs/en/hooks, sectiunea "Common input fields" (confirmat identic
|
|
||||||
si pe varianta .md).
|
|
||||||
|
|
||||||
Nota: campul `trigger` (pentru `PreCompact`/`PostCompact`, valori `manual`/`auto`) si `source`
|
|
||||||
(pentru `SessionStart`, valori `startup`/`resume`/`clear`/`compact`/`fork`) sunt mentionate ca
|
|
||||||
**valori de matcher**, nu confirmate ca nume exact de camp JSON pe stdin — vezi tabelul de
|
|
||||||
matchere de la punctul 3.
|
|
||||||
|
|
||||||
Ce s-a confirmat despre `SessionStart` prin exemplu real de configurare (verbatim, din
|
|
||||||
`hooks-guide`, sectiunea "Re-inject context after compaction"):
|
|
||||||
|
|
||||||
> When Claude's context window fills up, compaction summarizes the conversation to free space.
|
|
||||||
> This can lose important details. Use a `SessionStart` hook with a `compact` matcher to
|
|
||||||
> re-inject critical context after every compaction.
|
|
||||||
>
|
|
||||||
> Claude Code adds plain text your command writes to stdout to Claude's context.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hooks": {
|
|
||||||
"SessionStart": [
|
|
||||||
{
|
|
||||||
"matcher": "compact",
|
|
||||||
"hooks": [
|
|
||||||
{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. ...'" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Schema de iesire (`hookSpecificOutput`, `additionalContext`, `systemMessage`, `decision`,
|
|
||||||
`continue`, `stopReason`) pentru cele 4 evenimente cerute: **NEDOCUMENTAT** in sensul de mai sus
|
|
||||||
pentru forma completa exacta. S-a confirmat insa, verbatim, forma generala de output pentru
|
|
||||||
`UserPromptSubmit` (acelasi tipar `hookSpecificOutput.additionalContext`, aplicabil probabil si
|
|
||||||
altor evenimente, dar nu s-a putut confirma explicit pentru `SessionStart`/`Stop`/`SubagentStop`):
|
|
||||||
|
|
||||||
> For `UserPromptSubmit` hooks, use `hookSpecificOutput.additionalContext` instead to inject text
|
|
||||||
> into Claude's context. Nest `additionalContext` inside `hookSpecificOutput`; if you place it at
|
|
||||||
> the top level of the JSON, Claude Code silently ignores it.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hookSpecificOutput": {
|
|
||||||
"hookEventName": "UserPromptSubmit",
|
|
||||||
"additionalContext": "Current branch: release-42. Deploy freeze until Friday."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Other events use different decision patterns. For example, `PostToolUse` and `Stop` hooks use a
|
|
||||||
> top-level `decision: "block"` field, while `PermissionRequest` uses
|
|
||||||
> `hookSpecificOutput.decision.behavior`.
|
|
||||||
|
|
||||||
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "Structured JSON output".
|
|
||||||
|
|
||||||
## 2. Tokeni consumati / marimea contextului in input-ul hook-urilor
|
|
||||||
|
|
||||||
**Confirmat, de doua ori, cautare pe intreaga pagina `hooks`**: nu exista niciun camp de tokeni
|
|
||||||
in input-ul hook-urilor.
|
|
||||||
|
|
||||||
> Search for "stop_hook_active": No matches found for the term "stop_hook_active" on this page.
|
|
||||||
> Search for "token", "usage", and "input_tokens": No matches found for these terms on this page.
|
|
||||||
|
|
||||||
(cautarea a fost facuta explicit pe pagina "Hooks reference"; rezultatul e consistent la doua
|
|
||||||
apeluri separate — semnal ca reflecta continutul real, nu o presupunere a modelului de sumarizare)
|
|
||||||
|
|
||||||
Concluzie: singura sursa documentata pentru starea conversatiei ramane `transcript_path`
|
|
||||||
(fisier `.jsonl`), descris astfel (verbatim, vezi campurile comune de mai sus):
|
|
||||||
|
|
||||||
> Path to conversation JSON. The transcript file is written asynchronously and may lag the
|
|
||||||
> in-memory conversation, so it may not yet include the current turn's most recent messages when
|
|
||||||
> a hook fires.
|
|
||||||
|
|
||||||
**NEDOCUMENTAT**: schema exacta a unei linii din `transcript_path` (daca fiecare linie JSONL are
|
|
||||||
un obiect `usage` cu `input_tokens`/`cache_read_input_tokens`). Nu am gasit nicio sectiune care sa
|
|
||||||
descrie formatul intern al transcriptului; pagina de hooks il trateaza doar ca „path to
|
|
||||||
conversation JSON", fara schema de linie.
|
|
||||||
|
|
||||||
Singurul loc unde a aparut o notiune de "context folosit" e statusline-ul (alta functionalitate,
|
|
||||||
nu un hook), in exemplul din `settings-example`:
|
|
||||||
|
|
||||||
> "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
|
|
||||||
|
|
||||||
adica statusline-ul primeste `context_window.used_percentage` — dar acesta e inputul JSON al
|
|
||||||
comenzii de `statusLine`, nu al vreunui hook (`PreCompact`/`Stop`/etc.). Sursa:
|
|
||||||
https://code.claude.com/docs/en/settings-example, sectiunea "Your own settings".
|
|
||||||
|
|
||||||
## 3. Declansarea programatica a compactarii; PreCompact poate bloca?
|
|
||||||
|
|
||||||
Confirmat verbatim (tabelul de matchere, `hooks-guide`, sectiunea "Filter hooks with matchers"):
|
|
||||||
|
|
||||||
> | Event | What the matcher filters | Example matcher values |
|
|
||||||
> | `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |
|
|
||||||
|
|
||||||
Nu exista alta explicatie a diferentei functionale dintre `manual` si `auto` in continutul pe care
|
|
||||||
am putut sa-l confirm — **NEDOCUMENTAT** in acest fetch (probabil documentat in sectiunea
|
|
||||||
"Hooks reference" pe care nu am putut-o extrage integral).
|
|
||||||
|
|
||||||
Despre blocare: tabelul "Exit code 2 behavior per event" s-a confirmat identic de 3 ori, dar
|
|
||||||
mereu trunchiat la randul `Stop`:
|
|
||||||
|
|
||||||
> | Hook event | Can block? | What happens on exit 2 |
|
|
||||||
> | `PreToolUse` | Yes | Blocks the tool call |
|
|
||||||
> | `PermissionRequest` | No | Exit code 2 isn't honored for this event and the permission flow proceeds unchanged. ... |
|
|
||||||
> | `UserPromptSubmit` | Yes | Blocks prompt processing and erases the prompt |
|
|
||||||
> | `UserPromptExpansion` | Yes | Blocks the expansion |
|
|
||||||
> | `Stop` | Yes | Prevents Claude from stopping, continues the conversation |
|
|
||||||
|
|
||||||
Randul pentru `PreCompact` (si `SubagentStop`, `SessionStart`) nu a putut fi extras — nici prin
|
|
||||||
fetch normal, nici prin varianta `.md`, nici prin cereri tintite doar pe randul lipsa.
|
|
||||||
**NEDOCUMENTAT explicit aici**: daca `PreCompact` poate bloca compactarea prin exit code 2.
|
|
||||||
(Rationament indirect, NEconfirmat ca fapt: linia generala din ghid — "Some events can't be
|
|
||||||
blocked: for SessionStart and others, exit 2 shows stderr to the user and execution continues" —
|
|
||||||
sugereaza ca exista o categorie de evenimente needitabile prin exit 2, dar nu specifica daca
|
|
||||||
`PreCompact` e in acea categorie sau in cealalta.)
|
|
||||||
|
|
||||||
Citat sigur, din `hooks-guide`, care mentioneaza explicit ca `SessionStart` NU poate fi blocat:
|
|
||||||
|
|
||||||
> Some events can't be blocked: for `SessionStart` and others, exit 2 shows stderr to the user and
|
|
||||||
> execution continues.
|
|
||||||
|
|
||||||
Declansare programatica a compactarii: **NEDOCUMENTAT** in continutul confirmat — nu am gasit un
|
|
||||||
flag/comanda explicita de tip "trigger compaction now" in paginile fetch-uite (hooks, hooks-guide,
|
|
||||||
settings, settings-reference, env-vars). Doar variabila urmatoare influenteaza PRAGUL, nu
|
|
||||||
declansarea manuala programatica:
|
|
||||||
|
|
||||||
> `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` — Set the percentage (1-100) of the auto-compact window at
|
|
||||||
> which auto-compaction triggers. Use lower values like `50` to compact earlier; the variable
|
|
||||||
> can't raise the threshold, so values above the default percentage are ignored. It applies only
|
|
||||||
> in sessions that compact before the model's context limit. Applies to both main conversations
|
|
||||||
> and subagents.
|
|
||||||
|
|
||||||
Sursa: https://code.claude.com/docs/en/env-vars.
|
|
||||||
|
|
||||||
## 4. Hook-uri in subagenti (Task/Agent); SubagentStop; bucla stop_hook_active
|
|
||||||
|
|
||||||
**Confirmat verbatim** (hooks-guide, sectiunea "Limitations"):
|
|
||||||
|
|
||||||
> Background subagents can't show a prompt in non-interactive mode. Claude Code still runs the
|
|
||||||
> hooks for their tool calls, and if no hook returns a decision, it denies the call. In an
|
|
||||||
> interactive session, background subagent prompts surface in your main session and the hooks
|
|
||||||
> fire as usual.
|
|
||||||
|
|
||||||
Deci: hook-urile de tip `PreToolUse`/`PostToolUse` etc. **se declanseaza si in interiorul
|
|
||||||
subagentilor**, pentru apelurile lor de unelte — confirmat explicit doar pentru cazul
|
|
||||||
`PermissionRequest`/permisiuni; pentru restul evenimentelor (`PreToolUse`, `PostToolUse` propriu-zise
|
|
||||||
in subagent) nu am gasit o fraza separata la fel de explicita, dar tabelul de scope de configurare
|
|
||||||
confirma ca hook-urile de subagent exista ca mecanism dedicat:
|
|
||||||
|
|
||||||
> | Location | Scope | Shareable |
|
|
||||||
> | [Subagent](/docs/en/sub-agents) frontmatter | While that subagent is running | Yes, defined in the subagent file |
|
|
||||||
|
|
||||||
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "Configure hook location".
|
|
||||||
|
|
||||||
`SubagentStop` — definitie confirmata din tabelul de evenimente (punctul 1):
|
|
||||||
|
|
||||||
> `SubagentStop` | When a subagent finishes
|
|
||||||
|
|
||||||
**NEDOCUMENTAT** (nesigur): o extractie initiala a afirmat ca exista fraza "Claude Code converts
|
|
||||||
a `Stop` hook here to `SubagentStop`, the event it fires when a subagent completes" — aceasta
|
|
||||||
fraza NU s-a mai reprodus la recitirea directa a continutului brut al `hooks-guide` (1065 de
|
|
||||||
linii citite integral), asa ca nu o citez ca fapt confirmat. Nu neg ca ar fi adevarata (e
|
|
||||||
plauzibila si consistenta cu restul mecanismului de scope pe subagent), doar ca nu am reusit sa o
|
|
||||||
verific verbatim in aceasta sesiune.
|
|
||||||
|
|
||||||
### stop_hook_active si bucla
|
|
||||||
|
|
||||||
**Confirmat verbatim**, din `hooks-guide`, sectiunea "Stop hook hits the block cap":
|
|
||||||
|
|
||||||
> Claude keeps working instead of stopping, then ends the turn with a warning that the Stop hook
|
|
||||||
> blocked too many consecutive times.
|
|
||||||
>
|
|
||||||
> Claude Code overrides a Stop hook after it blocks eight times in a row without progress. Your
|
|
||||||
> hook script needs to check whether it already triggered a continuation. Parse the
|
|
||||||
> `stop_hook_active` field from the JSON input and exit early if it's `true`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
INPUT=$(cat)
|
|
||||||
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
|
|
||||||
exit 0 # Allow Claude to stop
|
|
||||||
fi
|
|
||||||
# ... rest of your hook logic
|
|
||||||
```
|
|
||||||
|
|
||||||
> If your hook legitimately needs more than eight iterations to converge, raise the cap with
|
|
||||||
> `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`.
|
|
||||||
|
|
||||||
Deci: **cauza tipica a buclei** e un hook `Stop`/`SubagentStop` care intoarce mereu o decizie de
|
|
||||||
blocare (exit 2 / `decision: "block"`) fara sa verifice `stop_hook_active`, astfel incat Claude
|
|
||||||
Code il tot re-invoca; plafonul e 8 blocari consecutive "fara progres", dupa care Claude Code
|
|
||||||
suprascrie hook-ul si opreste turul cu un avertisment. Campul `stop_hook_active` exista exact ca
|
|
||||||
sa permita hook-ului sa detecteze ca a mai fost invocat o data in acelasi ciclu de "stop" si sa
|
|
||||||
cedeze (`exit 0`) in loc sa continue sa blocheze.
|
|
||||||
|
|
||||||
Variabila de mediu asociata, confirmata din `env-vars` (extras separat, cu wording plauzibil dar
|
|
||||||
NEverificat printr-un al doilea fetch identic — trateaza ca moderat sigur, nu ca sigur):
|
|
||||||
|
|
||||||
> `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP` — Maximum number of blocks a stop hook can request before
|
|
||||||
> Claude Code stops respecting further requests from that hook and logs a warning (default:
|
|
||||||
> `100`). Useful when a hook inadvertently loops and repeatedly requests stops.
|
|
||||||
|
|
||||||
Nota: valoarea implicita citata aici de fetch (`100`) **contrazice** cifra "eight times in a row"
|
|
||||||
din `hooks-guide` (confirmata de doua ori, sigura). Nu pot reconcilia cele doua cifre din
|
|
||||||
continutul disponibil — posibil ca 8 sa fie plafonul implicit "fara progres" mentionat in ghid, iar
|
|
||||||
`100` sa fie un plafon absolut diferit citit gresit de fetch-ul pe `env-vars` (surse nereconciliate,
|
|
||||||
posibil eroare de extractie pe aceasta a doua cifra). **Trateaza cifra "100" ca NEDOCUMENTAT/de
|
|
||||||
reverificat manual**, foloseste "8" ca fiind confirmat de doua ori pe pagina oficiala a ghidului.
|
|
||||||
|
|
||||||
## 5. Setari relevante in settings.json
|
|
||||||
|
|
||||||
Confirmat din tabelul settings-reference (randuri, fara detaliu de default/exemplu — sectiunile
|
|
||||||
detaliate de sub tabel nu au putut fi extrase, pagina prea mare):
|
|
||||||
|
|
||||||
> | Key | Description | Topic | Scope |
|
|
||||||
> | `autoCompactEnabled` | Turn automatic compaction off or on | Memory and context | Any file |
|
|
||||||
> | `autoCompactWindow` | Set how full the context gets before Claude Code compacts | Memory and context | Any file |
|
|
||||||
> | `cleanupPeriodDays` | Choose how many days Claude Code keeps transcripts before deleting them | Privacy and telemetry | Any file |
|
|
||||||
> | `env` | Set environment variables for every session and its subprocesses | Memory and context | Any file |
|
|
||||||
|
|
||||||
Sursa: https://code.claude.com/docs/en/settings-reference. **NEDOCUMENTAT** aici: valorile
|
|
||||||
implicite exacte pentru `autoCompactEnabled` si `autoCompactWindow` (sectiunile detaliate nu s-au
|
|
||||||
putut extrage).
|
|
||||||
|
|
||||||
Exemple reale confirmate (`cleanupPeriodDays`, `env`), din https://code.claude.com/docs/en/settings-example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
// ~/.claude/settings.json — un dezvoltator
|
|
||||||
{
|
|
||||||
"...": "...",
|
|
||||||
"cleanupPeriodDays": 20
|
|
||||||
}
|
|
||||||
```
|
|
||||||
> Delete session transcripts and other local session data older than 20 days
|
|
||||||
|
|
||||||
```json
|
|
||||||
// .claude/settings.json — o echipa
|
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
|
|
||||||
"OTEL_METRICS_EXPORTER": "otlp",
|
|
||||||
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
|
|
||||||
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4317"
|
|
||||||
},
|
|
||||||
"hooks": {
|
|
||||||
"PreToolUse": [
|
|
||||||
{
|
|
||||||
"matcher": "Bash",
|
|
||||||
"hooks": [
|
|
||||||
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
// managed-settings.json — o organizatie
|
|
||||||
{
|
|
||||||
"...": "...",
|
|
||||||
"cleanupPeriodDays": 7
|
|
||||||
}
|
|
||||||
```
|
|
||||||
> Delete session transcripts and other local session data after 7 days
|
|
||||||
|
|
||||||
Variabile `CLAUDE_CODE_*` legate de context/compactare, confirmate din
|
|
||||||
https://code.claude.com/docs/en/env-vars:
|
|
||||||
|
|
||||||
> `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` — Set the percentage (1-100) of the auto-compact window at
|
|
||||||
> which auto-compaction triggers. Use lower values like `50` to compact earlier; the variable
|
|
||||||
> can't raise the threshold, so values above the default percentage are ignored. Applies to both
|
|
||||||
> main conversations and subagents.
|
|
||||||
|
|
||||||
> `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP` — vezi punctul 4 (cifra de default nereconciliata).
|
|
||||||
|
|
||||||
**NEDOCUMENTAT** in acest fetch: nu am gasit alte variabile `CLAUDE_CODE_*` explicit legate de
|
|
||||||
"context size" ca numar de tokeni (cautarea pe `env-vars` a fost limitata la cuvintele
|
|
||||||
COMPACT/CONTEXT/TOKEN/STOP_HOOK; nu a intors nimic cu "CONTEXT" in nume).
|
|
||||||
|
|
||||||
## Rezumat pentru cine investigheaza "subagent stop violation"
|
|
||||||
|
|
||||||
- Nu exista niciun camp de tokeni/marime-context in inputul niciunui hook (confirmat, cautare
|
|
||||||
directa pe pagina oficiala). Singura sursa e `transcript_path`, iar formatul intern al liniilor
|
|
||||||
JSONL nu e documentat in paginile verificate.
|
|
||||||
- Bucla clasica de `Stop`/`SubagentStop` are o cauza documentata si un mecanism de iesire:
|
|
||||||
campul `stop_hook_active` pe input, plafon confirmat de "8 blocari la rand fara progres" dupa
|
|
||||||
care Claude Code preia controlul si opreste turul cu avertisment (`hooks-guide`, sectiunea
|
|
||||||
"Stop hook hits the block cap").
|
|
||||||
- Hook-urile ruleaza si in subagenti; input-ul lor primeste in plus `agent_id`/`agent_type` fata de
|
|
||||||
campurile comune.
|
|
||||||
- Schemele JSON complete (toate campurile) pentru `PreCompact`/`SessionStart`/`Stop`/`SubagentStop`
|
|
||||||
si detaliul exact al blocarii pentru `PreCompact` NU au putut fi confirmate verbatim in aceasta
|
|
||||||
sesiune — pagina oficiala "Hooks reference" (https://code.claude.com/docs/en/hooks) le contine
|
|
||||||
aproape sigur, dar depaseste ce a putut extrage fetch-ul disponibil; de reluat cu acces direct
|
|
||||||
(browser) daca e nevoie de schema exacta camp-cu-camp.
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
# Inventar unelte QA/testare UI si infrastructura de progres — ROAFACTURARE
|
|
||||||
|
|
||||||
Inventar read-only, 17.09.2026. Fara propuneri, doar stare (citate `fisier:linie`).
|
|
||||||
|
|
||||||
## 1. `vfp_ui_harness.ps1` — orchestrator UI
|
|
||||||
|
|
||||||
Locatie: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\vfp_ui_harness.ps1`.
|
|
||||||
Complement in VFP: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\ui_harness.prg` (proceduri de handshake
|
|
||||||
incluse in testul .prg via `SET PROCEDURE TO ui_harness ADDITIVE`).
|
|
||||||
|
|
||||||
Parametri (`vfp_ui_harness.ps1:20-28`):
|
|
||||||
- `-TestPrg` (obligatoriu) — calea `.prg` a testului.
|
|
||||||
- `-Steps` (obligatoriu) — array de etichete, una per pas asteptat (`ready_<n>.txt`/`cont_<n>.txt`).
|
|
||||||
- `-StepTimeoutSec` (implicit 130), `-ReadyTimeoutSec` (implicit 180, pt. `ready_0` = afisarea formularului).
|
|
||||||
- `-ShotsDir` (implicit `<folder test>\screenshots`), `-SyncDir` (implicit `<folder test>\uisync`).
|
|
||||||
- `-Vfp` (implicit `C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe`).
|
|
||||||
|
|
||||||
Ce face exact:
|
|
||||||
- Precompileaza izolat (`_precompile.ps1`, proces copil) inainte de lansare (`vfp_ui_harness.ps1:145-154`).
|
|
||||||
- Lanseaza `.FXP`-ul cu `vfp9.exe -A <test.fxp>` (nu `.prg`), cu pana la 8 incercari daca testul nu
|
|
||||||
scrie START in 30s (`vfp_ui_harness.ps1:160-190`).
|
|
||||||
- Fereastra principala e mutata **off-screen** (`x=-4000`, `HWND_BOTTOM`, `SWP_NOACTIVATE`) imediat
|
|
||||||
ce apare `MainWindowHandle`, si re-impinsa la fiecare pas — **fara furt de focus**, cerinta Marius
|
|
||||||
17/07/2026 (`vfp_ui_harness.ps1:13-19,73-77`).
|
|
||||||
- Screenshot per pas via `PrintWindow` pe handle (flag `PW_RENDERFULLCONTENT=2`, fallback flag 0
|
|
||||||
daca iese gol) — nu `CopyFromScreen` (`vfp_ui_harness.ps1:106-131`).
|
|
||||||
- Bucla pasi: asteapta `ready_<n>.txt` (scris de test din `ui_harness.prg`), face screenshot, scrie
|
|
||||||
`cont_<n>.txt` ca sa continue testul (`vfp_ui_harness.ps1:200-221`).
|
|
||||||
- La final asteapta `done.txt`, omoara doar instantele `vfp9.exe` proprii (linie de comanda contine
|
|
||||||
folderul testului) (`vfp_ui_harness.ps1:133-140,223-231`).
|
|
||||||
|
|
||||||
`ui_harness.prg` — API apelat din testul VFP:
|
|
||||||
- `HarnessLog(mesaj)` (`ui_harness.prg:19-28`), `HarnessReady(n, nota)` (`ui_harness.prg:30-35`),
|
|
||||||
`HarnessWaitContinue(n, autoSec=30)` (`ui_harness.prg:37-51`), `HarnessStep(n, nota, autoSec)`
|
|
||||||
= Ready+WaitContinue (`ui_harness.prg:53-57`), `HarnessDone(status)` (`ui_harness.prg:59-64`).
|
|
||||||
- `HarnessWaitContinue` are auto-continue dupa `autoSec` (implicit 30s) daca orchestratorul nu
|
|
||||||
raspunde — testul merge si fara `vfp_ui_harness.ps1`, doar fara capturi (`ui_harness.prg:8-9,43-49`).
|
|
||||||
- `HarnessInit`: `SET SAFETY OFF` + `SET TALK OFF` defensiv (`ui_harness.prg:11-17`).
|
|
||||||
|
|
||||||
Limitari cunoscute:
|
|
||||||
- **Nu trimite input real** (fara `SendInput`/`keybd_event`/click injectat) — harness-ul doar
|
|
||||||
citeste semafoare si face screenshot; actiunile UI (click, taste) sunt executate **din interiorul
|
|
||||||
testului VFP** (apeluri directe de metode/evenimente), nu de PowerShell din afara.
|
|
||||||
- Watchdog-ul de dialoguri (`watchdog_vfp.ps1`) e un instrument separat, folosit doar pt. dialoguri
|
|
||||||
native neasteptate; dismiss-ul se face STRICT prin mesaje Windows tintite pe handle (`BM_CLICK`,
|
|
||||||
`WM_COMMAND IDCANCEL`, `WM_KEYDOWN/UP` ESCAPE, `WM_CLOSE`) — interzis explicit input real de
|
|
||||||
tastatura/mouse, masina fiind partajata cu utilizatorul (`watchdog_vfp.ps1:16-26`).
|
|
||||||
- Dialogurile VFP owner-drawn (ex. "View Parameter") pot sa nu raspunda la niciun mesaj — dismiss-ul
|
|
||||||
esueaza cinstit, ramane deschis pana la timeout (`watchdog_vfp.ps1:23-26`).
|
|
||||||
- Coloanele de grid nu se materializeaza sub `-A -T`/headless (`ColumnCount=0`, `RecordSource` sunt
|
|
||||||
artefacte) — cunoscut, documentat separat (memorie `grid-coloane-nu-se-materializeaza-headless`);
|
|
||||||
simptomul apare si in suitele UI (ex. `test_page3_articole` 14/2 in `docs\progres.md:67`, cele 2
|
|
||||||
FAIL = artefactul de baseline).
|
|
||||||
- `watchdog_vfp.ps1` clasifica orice fereastra noua diferita de `MainWindowHandle` ca dialog blocant
|
|
||||||
(nu dupa numele clasei — VFP refoloseste acelasi prefix de clasa si pt. shell, si pt. dialoguri
|
|
||||||
proprii) (`watchdog_vfp.ps1:6-9`).
|
|
||||||
|
|
||||||
## 2. Harness headless (skill `roa-vfp-headless-test`)
|
|
||||||
|
|
||||||
Skill: `D:\ROA\ROAFACTURARE\COMUN\skills\roa-vfp-headless-test\SKILL.md`.
|
|
||||||
|
|
||||||
Lansare probe (`SKILL.md:102-103`): `vfp9.exe -A -T "<script.prg>" <param>`, din PowerShell, cu
|
|
||||||
timeout si `$p.Kill()` daca nu iese (`$p.WaitForExit(120000)`). `-A` si `-T` obligatorii amandoua
|
|
||||||
(`SKILL.md:45-46`). Precompilare izolata obligatorie inainte (`_precompile.ps1`, proces copil),
|
|
||||||
altfel `vfp9 -A` poate deschide editorul in loc sa ruleze (`SKILL.md:28-29,99-100`).
|
|
||||||
|
|
||||||
Loguri: fiecare test scrie propriul `<test>_log.txt` langa `.prg` (convenit prin `gcUILog`/logica
|
|
||||||
proprie a testului); sinteza vine din `raport_teste.ps1` (`D:\ROA\ROAFACTURARE\COMUN\utile\Teste\raport_teste.ps1`),
|
|
||||||
care citeste **doar** fisierele `*_log.txt` dintr-un folder (implicit `achizitie_import`, parametrizabil
|
|
||||||
cu `-Dir`), numara linii `^PASS` si `^(FAIL|BUG)` si scoate PASS/FAIL per fisier + varsta (minute de
|
|
||||||
la ultima scriere) (`raport_teste.ps1:9-33`). `-Baseline <fisier>` marcheaza NOU vs. preexistent
|
|
||||||
(`raport_teste.ps1:7-8,16-19`). Regula: orchestratorul citeste doar acest raport, nu logurile brute
|
|
||||||
(`raport_teste.ps1:2-3`, trimite la `COMUN\docs\orchestrare-subagenti.md`).
|
|
||||||
|
|
||||||
Mock-uri disponibile in `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\`:
|
|
||||||
- `mock_amessagebox.prg` — inlocuieste `FUNCTION amessagebox` din `oproceduri_comune.prg`; returneaza
|
|
||||||
6 (Da/OK) implicit, configurabil per apel prin `PUBLIC gnMockRaspuns` (raspuns generic),
|
|
||||||
`gnMockRaspunsTotal` (raspuns separat pt. mesajul "Actualizati totalul facturii"),
|
|
||||||
`gcMockUltimMesaj`/`gnMockUltimTip` (captura textul/tipul ultimului dialog, pt. asertii)
|
|
||||||
(`mock_amessagebox.prg:1-44`). **Trebuie incarcat PRIMUL** in `SET PROCEDURE` (VFP foloseste, la
|
|
||||||
nume duplicat, fisierul cautat primul, nu cel deschis cel mai recent) — vezi
|
|
||||||
`test_init_env_auto_roafacturare.prg:119-121` unde e adaugat inaintea listei aplicatiei.
|
|
||||||
LIMITA 1: nu acopera apeluri intra-fisier (ex. `verifica_partener_show_info` -> `amessagebox` in
|
|
||||||
acelasi `oproceduri_comune.prg`) — necesita mock dedicat per caz (`mock_amessagebox.prg:13-15`).
|
|
||||||
LIMITA 2: **nu acopera dialogurile de eroare ale lui `goExecutor`** — o interogare gresita agata
|
|
||||||
headless fara nicio linie in log (`mock_amessagebox.prg:16-17`). Nu exista un mock generic pentru
|
|
||||||
`goExecutor` la radacina `Teste\`; exista doar mock-uri punctuale per test in subfoldere (ex.
|
|
||||||
`achizitie_import\mock_cauta_alfa_tva11.prg`, `achizitie_import\mock_oscrie_in_fisiere.prg`).
|
|
||||||
- `watchdog_vfp.ps1` — pt. dialoguri native neprinse de ON ERROR/mock-uri (`SKILL.md:97-98`).
|
|
||||||
|
|
||||||
Init de mediu specific ROAFACTURARE: `test_init_env_auto_roafacturare.prg` — vezi punctul 3.
|
|
||||||
|
|
||||||
## 3. Conectare Oracle in probe (schema MARIUSM_AUTO)
|
|
||||||
|
|
||||||
Script: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto_roafacturare.prg`.
|
|
||||||
|
|
||||||
Apel: `DO test_init_env_auto_roafacturare WITH 'CENTRAL', 'MARIUSM_AUTO', 'parola'`
|
|
||||||
(`test_init_env_auto_roafacturare.prg:11`).
|
|
||||||
|
|
||||||
Valori implicite daca parametrii sunt goi (`test_init_env_auto_roafacturare.prg:16-18`):
|
|
||||||
- `tcHost` = `CENTRAL`
|
|
||||||
- `tcSchema` = `MARIUSM_AUTO`
|
|
||||||
- `tcPassword` = `ROMFASTSOFT`
|
|
||||||
|
|
||||||
Conexiunea propriu-zisa: `goConn = createobject("oConn")` +
|
|
||||||
`goConn.Connect(tcHost, tcSchema, tcPassword)` (`test_init_env_auto_roafacturare.prg:209-214`),
|
|
||||||
verificata prin `gnHandle > 0`. Executorul de SQL e `goExecutor = createobject("oExecutor")`
|
|
||||||
(`test_init_env_auto_roafacturare.prg:209`), folosit apoi pt. `oExecuta(...)`.
|
|
||||||
|
|
||||||
An/luna de lucru fixe (independente de `Date()`): `gnAn=2026`, `gnLuna=8` daca nu sunt pasate ca
|
|
||||||
parametri 4/5 (`test_init_env_auto_roafacturare.prg:36-37`). Firma: `gnIdFirma=110` implicit
|
|
||||||
(parametru 6 optional) (`test_init_env_auto_roafacturare.prg:39,221-224`).
|
|
||||||
|
|
||||||
Cale aplicatie fixa: `gcAppPath = 'D:\ROA\ROAFACTURARE\'` (`test_init_env_auto_roafacturare.prg:57`) —
|
|
||||||
seteaza `SET PATH`/`SET CLASSLIB`/`SET PROCEDURE` identic cu `roafacturare.prg`, plus mock-ul
|
|
||||||
`amessagebox` incarcat primul (`test_init_env_auto_roafacturare.prg:82-176`).
|
|
||||||
|
|
||||||
Nota din antet: variantele `test_init_env_auto_<produs>.prg` (ROACONT/ROAGEST/ACNPRO/ROADEF) au
|
|
||||||
cale + SET-uri specifice produsului, dar **partea de conexiune Oracle e identica** in toate
|
|
||||||
(`test_init_env_auto_roafacturare.prg:6-9`). Alegerea harness-ului gresit incarca alt working copy
|
|
||||||
silentios (`SKILL.md:21-23`).
|
|
||||||
|
|
||||||
## 4. Fisiere de progres/status existente
|
|
||||||
|
|
||||||
In `D:\ROA\ROAFACTURARE\docs\`:
|
|
||||||
- `progres.md` — **fisierul curent de stare**, actualizat de fiecare sesiune inainte sa se incheie;
|
|
||||||
planurile (`plan_0*.md`) spun *ce*, `progres.md` spune *unde s-a ajuns* (`progres.md:1-7`). Format:
|
|
||||||
titlu cu punctele acoperite, "Ultima actualizare: DD.MM.YYYY", apoi blocuri
|
|
||||||
`> **RUNDA DD.MM.YYYY — titlu.**` cu subsectiuni in proza (ce s-a schimbat, decizii numerotate,
|
|
||||||
teste rulate cu PASS/FAIL, capcane platite, ramas deschis) — nu tabel, istoricul sesiunilor nu se
|
|
||||||
pastreaza, doar starea la zi (`progres.md:1-9`).
|
|
||||||
- `handoff_*.md` — 6 fisiere curent (`handoff_update_romfast.md`, `handoff_cont_discount_667_709.md`,
|
|
||||||
`handoff_id_set_skilluri.md`, `handoff_idempotenta_id_set.md`, plus altele). Format observat in
|
|
||||||
`handoff_idempotenta_id_set.md:1-13`: titlu cu subiect+data, sectiuni numerotate H2 — "Livrabile
|
|
||||||
terminate" (tabel Ce/Unde/Stare), "Ce face X", "APROBAT dar NEEXECUTAT", "NEFINALIZAT/necomis",
|
|
||||||
"Comenzi de reluat" (bloc powershell), incheiat de regula cu inventar fisier:linie si stare
|
|
||||||
write-back per fisier atins.
|
|
||||||
- `diff_*.md` / `diff_*.patch` — diff-uri de revizuit inainte de commit (ex.
|
|
||||||
`diff_cont_discount_667_709.md`).
|
|
||||||
- `plan_index.md` + `plan_1*.md` — planuri pe story-uri (ex. `plan_10_integrare_contracte.md`).
|
|
||||||
- `docs\cercetare\*.md` — ~60 de fisiere de cercetare/investigatie punctuala (un subiect per fisier).
|
|
||||||
- `erori_deschise.md`, `livrare_13.md`, `raport_doc.md`, `raport_src.md`.
|
|
||||||
|
|
||||||
In `D:\ROA\ROAFACTURARE\COMUN\docs\` (relevante pt. orchestrare/testare, nu fisiere de progres in
|
|
||||||
sine ci proceduri):
|
|
||||||
- `orchestrare-subagenti.md` — procedura Regula zero (predare context), citata de `raport_teste.ps1:2-3`.
|
|
||||||
- `reguli_lucru.md` — index de reguli de lucru/testare, ruteaza pe zona la skill-ul potrivit.
|
|
||||||
- `depanare_testare_vfp.md`, `testare-ui-vfp.md` — capcanele detaliate din `SKILL.md` (sec. 6-7 / integral).
|
|
||||||
|
|
||||||
Nu exista fisiere de progres/status in `D:\ROA\COMUNROA\` — directorul `COMUNROA` (shared suite-wide,
|
|
||||||
distinct de `COMUN\` din interiorul ROAFACTURARE) nu are un `docs\progres.md` propriu vizibil din
|
|
||||||
acest working copy.
|
|
||||||
|
|
||||||
## 5. Hook-uri configurate
|
|
||||||
|
|
||||||
### `C:\Users\mmari\.claude\settings.json` (nivel user, global)
|
|
||||||
|
|
||||||
- `UserPromptSubmit` -> `context_watch.ps1 -Stdout` (mesaj injectat, exit 0).
|
|
||||||
- `SessionStart` -> `docs_revizie_check.ps1 -Stdout`.
|
|
||||||
- `PostToolUse` -> `context_watch.ps1 -OSinguraData` (async, timeout 20s).
|
|
||||||
- `PreCompact` -> mesaj fix de sistem (Regula zero — predare la compactare).
|
|
||||||
- `PostCompact` -> mesaj fix + `additionalContext` (Regula zero — predare obligatorie dupa compactare).
|
|
||||||
- `SubagentStop` -> mesaj fix (`additionalContext`) care aminteste orchestratorului sa verifice
|
|
||||||
daca subagentul si-a scris starea pe disc inainte sa-i dea sarcina urmatoare.
|
|
||||||
|
|
||||||
**Da, exista deja un hook `context_watch`**, script:
|
|
||||||
`D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1` (in COMUNROA/ROAGEST, partajat intre proiecte, invocat
|
|
||||||
din `settings.json` cu cale absoluta acolo).
|
|
||||||
|
|
||||||
Ce face (`context_watch.ps1:1-9,55-84`):
|
|
||||||
- Citeste ultimul `"usage":{...}` din coada transcriptului JSONL al sesiunii curente (ultimii 400KB
|
|
||||||
din fisier, nu tot fisierul), insumeaza `input_tokens + cache_creation_input_tokens +
|
|
||||||
cache_read_input_tokens`.
|
|
||||||
- Praguri: `-Prag 250000` (implicit, nivel "avertisment") si `-PragMax 275000` (implicit, nivel
|
|
||||||
"max" — peste limita, predare OBLIGATORIE acum).
|
|
||||||
- La `avertisment`: mesaj care cere incheierea blocului curent, actualizarea handoff-ului pe disc,
|
|
||||||
delegarea catre subagenti a oricarei citiri/testari ramase.
|
|
||||||
- La `max`: mesaj cu procedura completa Regula zero (opreste lucrul, scrie handoff pe disc, confirma
|
|
||||||
in doua randuri, preda unei sesiuni noi).
|
|
||||||
- `-Stdout`: scrie mesajul pe stdout si iese cu 0 (folosit pe `UserPromptSubmit`, injecteaza context
|
|
||||||
fara sa esueze hook-ul). Fara `-Stdout`: scrie pe stderr si iese cu exit 2 (semnaleaza agentului).
|
|
||||||
- `-OSinguraData` (folosit pe `PostToolUse`): emite alerta o singura data per sesiune+prag, marcaj
|
|
||||||
fisier in `%TEMP%\claude_ctxwatch_<sesiune>_<nivel>.flag` — altfel mesajul s-ar repeta la fiecare
|
|
||||||
tool call (`context_watch.ps1:9-10,74-80`).
|
|
||||||
- Fara `-TranscriptPath` explicit, il citeste din stdin (JSON-ul hook-ului) sau, ca fallback, cauta
|
|
||||||
cel mai recent `.jsonl` din `~/.claude/projects/<cwd-encodat>\` (`context_watch.ps1:18-34`).
|
|
||||||
|
|
||||||
### `D:\ROA\ROAFACTURARE\.claude\` (nivel proiect)
|
|
||||||
|
|
||||||
Singurul fisier: `settings.local.json`, continut integral `{"outputStyle": "Concise"}`. **Niciun
|
|
||||||
hook definit la nivel de proiect** — toate hook-urile active vin din `settings.json` global de user.
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
# Raport — mesajul "Nu poate fi preluat cursul BNR" la facturare pe politici de preturi
|
|
||||||
|
|
||||||
Investigatie READ-ONLY. Sursa: `D:\ROA\ROAFACTURARE` + `D:\ROA\ROAFACTURARE\COMUN`. Nicio editare,
|
|
||||||
nicio rulare, niciun write-back.
|
|
||||||
|
|
||||||
## 1. Textul exact al mesajului si locatia lui
|
|
||||||
|
|
||||||
Gasit in **`COMUN\clase\onom_curs.vc2`**, clasa `actualizare_curs_bnr`, `PROCEDURE initializeaza`:
|
|
||||||
|
|
||||||
- `onom_curs.vc2:171` — textul exact reclamat de utilizator:
|
|
||||||
```
|
|
||||||
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
|
|
||||||
goUrl("http://www.bnr.ro") && din wwutils.prg
|
|
||||||
Endif
|
|
||||||
```
|
|
||||||
- Variante inrudite, in aceeasi metoda:
|
|
||||||
- `onom_curs.vc2:175` si `:181` si `:190` — `amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")`
|
|
||||||
- `onom_curs.vc2:193` — `amessagebox("Doriti sa accesati pagina BNR?",4+32,"Confirmare")` (ramura `Otherwise`, `nTip` necunoscut)
|
|
||||||
|
|
||||||
Nu exista alt loc in `.prg`/`.vc2`/`.sc2` (in ROAFACTURARE sau COMUN) cu text asemanator
|
|
||||||
("cursul BNR", "nbrfxrates", "curs BNR") legat de un mesaj de eroare — un singur punct de emitere,
|
|
||||||
in `actualizare_curs_bnr::initializeaza`.
|
|
||||||
|
|
||||||
## 2. Lantul de apel complet, de la facturare pana la mesaj
|
|
||||||
|
|
||||||
Punct de intrare din meniul de facturare pe lista/politica de preturi:
|
|
||||||
|
|
||||||
1. `COMUN\programe\oproceduri_facturare.prg:114-116` —
|
|
||||||
```
|
|
||||||
Procedure facturare_lista_de_preturi
|
|
||||||
Do politica.mpr
|
|
||||||
Endproc
|
|
||||||
```
|
|
||||||
Apelat din butonul `Page2.Cw1.do_actiune` (confirmat deja in `docs\plan_11_integrare_politici_preturi.md:16-18`).
|
|
||||||
2. `Meniuri\politica.mn2:12,37,43,46` — meniul `politica.mpr` cheama `factureaza(1)` (lei),
|
|
||||||
`factureaza(5)`/`(6)` (invoice), `factureaza(10)` (factura fiscala in valuta), `factureaza(9)`
|
|
||||||
(retur valuta) etc.
|
|
||||||
3. `COMUN\programe\ofacturare.prg:103` — `Procedure factureaza(tnTip, toFactura, toSursa)`. Pentru
|
|
||||||
tipurile de lista de preturi (1,5,7,10,22,23,29 — vezi comentariul din cod, `ofacturare.prg:140-149`)
|
|
||||||
populeaza articolele prin SQL server-side care trimite `poDate.zi_curs`:
|
|
||||||
`ofacturare.prg:266-308` (dupa cercetarea deja existenta `docs\cercetare\s4d_zi_curs_reactiv.md:144-146`):
|
|
||||||
```
|
|
||||||
Case Inlist(tnTip, 1,22,5,29,7,10,23) -> cursor_preturi(?poDate.zi_curs, ...)
|
|
||||||
```
|
|
||||||
4. Oracle, `pack_facturare.cursor_preturi` cheama **necondiționat**
|
|
||||||
`pack_facturare.verifica_cursuri_valute(V_DATA_CURS, V_ID_UTIL)`
|
|
||||||
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2153`), care
|
|
||||||
arunca `RAISE_APPLICATION_ERROR(-20005, 'Nu este setat cursul din data de ... pentru <valuta>!')`
|
|
||||||
(`:16268-16272`) daca vreo valuta din listele de preturi ale utilizatorului nu are curs valabil la
|
|
||||||
data ceruta (exclude doar moneda nationala).
|
|
||||||
5. VFP prinde eroarea Oracle: `COMUN\programe\ofacturare.prg:313-317` (identic la `:828-832`):
|
|
||||||
```
|
|
||||||
If lnSucces < 0
|
|
||||||
AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare")
|
|
||||||
If goExecutor.nEroare = 20005
|
|
||||||
vizualizeaza_curs(poDate.zi_curs)
|
|
||||||
ENDIF
|
|
||||||
```
|
|
||||||
6. `COMUN\programe\oproceduri_curs.prg:8-42` — `Procedure vizualizeaza_curs(tdDataCurs)` — deschide
|
|
||||||
ecranul de administrare a cursurilor: `loFrmCurs = Createobject("frm_curs",tdDataCurs)` (`:34`),
|
|
||||||
`loFrmCurs.Show(1)` (`:36`, modal). `frm_curs` (clasa de cautare/lista) NU declanseaza singura
|
|
||||||
preluarea BNR — utilizatorul apasa "Nou" (`But_nou1`) ca sa adauge cursul lipsa, ceea ce deschide
|
|
||||||
`frm_curs_nou`.
|
|
||||||
7. `COMUN\clase\onom_curs.vc2:1305-1326` — `frm_curs_nou::Init` leaga schimbarea datei/valutei
|
|
||||||
inregistrarii noi la fetch automat:
|
|
||||||
```
|
|
||||||
onom_curs.vc2:1323-1325
|
|
||||||
Bindevent(poRec,"data",Thisform,"actualizeaza_curs_bnr",1)
|
|
||||||
Bindevent(poRec,"iso_valuta",Thisform,"actualizeaza_curs_bnr",1)
|
|
||||||
Thisform.oactualizare = Createobject("actualizare_curs_bnr",gnTipActualizareCurs)
|
|
||||||
```
|
|
||||||
8. `COMUN\clase\onom_curs.vc2:1209-1226` — `frm_curs_nou::actualizeaza_curs_bnr` (declansat la fiecare
|
|
||||||
schimbare a datei/valutei din formular):
|
|
||||||
```
|
|
||||||
onom_curs.vc2:1211
|
|
||||||
This.oactualizare.citeste_curs(poRec.Data,poRec.iso_valuta)
|
|
||||||
```
|
|
||||||
9. `COMUN\clase\onom_curs.vc2:46-62` — `actualizare_curs_bnr::citeste_curs(tdZiua,tcIsoValuta)` —
|
|
||||||
`:50-52`: `If !Used(This.cCursor) Or tdZiua <> This.dData Then This.initializeaza(tdZiua)`.
|
|
||||||
10. `COMUN\clase\onom_curs.vc2:152-202` — `actualizare_curs_bnr::initializeaza(tdZiua)` — interogheaza
|
|
||||||
Oracle (`pack_curs.citeste_cotatii_curs`, `:158-159`); daca **`Reccount(This.cCursor)=0`**
|
|
||||||
(nimic salvat local pentru data ceruta), intra pe `Do Case` descris la punctul 3 mai jos, unde se
|
|
||||||
afla mesajul de la punctul 1.
|
|
||||||
|
|
||||||
**A doua cale de intrare, directa, manuala**, separata de fluxul de facturare de mai sus, spre acelasi
|
|
||||||
mesaj: `Clase\ofundal_facturare.vc2:929-931` (fereastra principala de facturare, tab Page1, buton Cw7):
|
|
||||||
```
|
|
||||||
PROCEDURE Page1.Cw7.do_actiune
|
|
||||||
DO vizualizeaza_curs IN oproceduri_curs.prg WITH Ttod(get_ora())
|
|
||||||
ENDPROC
|
|
||||||
```
|
|
||||||
Trimite explicit **data curenta** (`Ttod(get_ora())`), deci un utilizator care apasa acest buton la
|
|
||||||
ora 19 si cursul zilei nu exista inca in Oracle intra pe acelasi drum (pasii 6-10 de mai sus).
|
|
||||||
|
|
||||||
## 3. Cum se decide data pentru care se cere cursul
|
|
||||||
|
|
||||||
Doua surse posibile pentru `tdZiua`/`This.dData`, ambele **data curenta a sistemului**, nu data
|
|
||||||
documentului si nu "data - 1":
|
|
||||||
|
|
||||||
- Ruta automata (eroare -20005): `poDate.zi_curs`, care e populat **necondiționat** cu data curenta
|
|
||||||
la initializarea antetului de factura —
|
|
||||||
`COMUN\programe\ofacturare_comun.prg:247` (`Init`) si `:496` (`Reset`): `.zi_curs = ldData`, unde
|
|
||||||
`ldData = Ttod(get_ora())` ajustat doar la luna/anul de facturare deschisa
|
|
||||||
(`docs\cercetare\zi_curs_validare.md:198-204`, citat ca sursa deja verificata). Nu e data
|
|
||||||
documentului (`dataact`) decat daca operatorul a sincronizat manual campul (acelasi punct, §4b).
|
|
||||||
- Ruta manuala (buton Cw7): `Ttod(get_ora())` — literal ora curenta a sistemului, transformata in data
|
|
||||||
(`ofundal_facturare.vc2:930`).
|
|
||||||
|
|
||||||
Decizia efectiva "azi sau nu" si "inainte sau dupa ora 13" se ia in
|
|
||||||
`actualizare_curs_bnr::initializeaza`, `onom_curs.vc2:164-176`:
|
|
||||||
```
|
|
||||||
onom_curs.vc2:164-176
|
|
||||||
ltDataOra = get_Ora()
|
|
||||||
Do Case
|
|
||||||
Case This.nTip = 1
|
|
||||||
Do Case
|
|
||||||
Case (This.dData = Ttod(ltDataOra) And Hour(ltDataOra) <13) Or (This.dData = Ttod(ltDataOra) + 1 And Hour(ltDataOra) >= 13)
|
|
||||||
This.citeste_curs_bnr()
|
|
||||||
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
|
|
||||||
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
|
|
||||||
goUrl("http://www.bnr.ro")
|
|
||||||
Endif
|
|
||||||
Otherwise
|
|
||||||
amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
|
|
||||||
Endcase
|
|
||||||
```
|
|
||||||
`ltDataOra` = ora curenta a sistemului (`get_Ora()`). Pentru scenariul raportat (data ceruta = azi,
|
|
||||||
ora curenta = 19:00): `This.dData = Ttod(ltDataOra)` si `Hour(ltDataOra) >= 13` -> intra direct pe
|
|
||||||
**a doua ramura** (linia 170), care **nu incearca deloc** `This.citeste_curs_bnr()` — afiseaza direct
|
|
||||||
mesajul de la linia 171. Prima ramura (fetch live, linia 168) se executa **doar** cand se cere ziua
|
|
||||||
curenta inainte de ora 13, sau ziua urmatoare dupa ora 13 — niciodata pentru "azi, dupa ora 13".
|
|
||||||
|
|
||||||
## 4. Fallback la curs anterior — nu exista
|
|
||||||
|
|
||||||
In toata `actualizare_curs_bnr::initializeaza` (`onom_curs.vc2:152-202`) nu exista nicio ramura care sa
|
|
||||||
citeasca "ultimul curs disponibil" sau "ziua lucratoare anterioara" cand data ceruta nu are curs local
|
|
||||||
si e prea tarziu sa se mai incerce live-ul. Cele trei ramuri posibile pentru `nTip=1` (liniile 166-176)
|
|
||||||
sunt exhaustiv: (a) incearca live fetch, (b) cere confirmare pentru deschidere manuala a bnr.ro, (c)
|
|
||||||
"Nu exista cursul BNR pentru data specificata!". Niciuna nu cade inapoi pe un rand `data2 >= data
|
|
||||||
anterioara` din tabela locala. Confirmat si de cercetarea independenta anterioara
|
|
||||||
(`docs\cercetare\s4d_zi_curs_reactiv.md:160-171`): reteta Oracle de sub `verifica_cursuri_valute`
|
|
||||||
verifica strict daca exista curs care sa acopere data ceruta — daca nu, arunca -20005, fara fallback pe
|
|
||||||
partea de baza de date.
|
|
||||||
|
|
||||||
Exista o a treia "sursa alternativa" definita in cod, `actualizare_curs_bnr::citeste_curs_altesurse`
|
|
||||||
(`onom_curs.vc2:64-98`, `nTip=2`), care foloseste clasa `curs` (INFOVALUTAR, nu BNR direct) — dar se
|
|
||||||
activeaza **doar** cand `This.nTip = 2` sau `= 3` (`onom_curs.vc2:177-191`). Valoarea lui `nTip` vine
|
|
||||||
din parametrul `gnTipActualizareCurs` transmis la creare (`onom_curs.vc2:1325`, `:1699`). **Nu am gasit
|
|
||||||
nicio declarare/atribuire a variabilei `gnTipActualizareCurs`** in tot codul text-cache din
|
|
||||||
`ROAFACTURARE` sau `COMUN` (cautare exhaustiva, case-insensitive) — deci nu se poate confirma din cod
|
|
||||||
ce valoare are efectiv la rulare; `actualizare_curs_bnr::Init` (`onom_curs.vc2:144-146`) o trateaza ca
|
|
||||||
`Iif(Empty(tnTip),1,tnTip)`, deci daca variabila e goala/nedefinita comportamentul cade pe `nTip=1`
|
|
||||||
(ramura fara alte surse, cea din mesajul raportat).
|
|
||||||
|
|
||||||
## 5. Sursa BNR: cum se apeleaza, timeout, tratarea erorii
|
|
||||||
|
|
||||||
`actualizare_curs_bnr::citeste_curs_bnr` — `onom_curs.vc2:100-136`:
|
|
||||||
```
|
|
||||||
onom_curs.vc2:100-118
|
|
||||||
PROCEDURE citeste_curs_bnr
|
|
||||||
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
|
|
||||||
...
|
|
||||||
lcServer = This.cLink && "https://curs.bnr.ro/nbrfxrates.xml" (onom_curs.vc2:28)
|
|
||||||
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
|
|
||||||
loHTTP.Open('GET', lcServer, .F.)
|
|
||||||
loHTTP.setRequestHeader("Content-Type", "application/xml;")
|
|
||||||
poLog.Log(m.lcServer)
|
|
||||||
loHTTP.Send()
|
|
||||||
|
|
||||||
If loHTTP.Status = 200
|
|
||||||
lcFisier = loHTTP.Responsebody
|
|
||||||
...
|
|
||||||
Xmltocursor(lcFisier,This.cCursor)
|
|
||||||
This.dData = Ttod(Ctot(lcData+[T000000]))+1
|
|
||||||
sterge_backup_cursoare(This.cCursor)
|
|
||||||
Else
|
|
||||||
repune_backup_cursoare(This.cCursor)
|
|
||||||
Endif
|
|
||||||
```
|
|
||||||
- Componenta: `WinHTTP.WinHTTPRequest.5.1` (COM), cerere sincrona (`Open(..., .F.)` = async=False).
|
|
||||||
- URL fix: `https://curs.bnr.ro/nbrfxrates.xml` (`onom_curs.vc2:28`, proprietatea `cLink`).
|
|
||||||
- **Niciun timeout explicit setat** pe `loHTTP` (nu apare `SetTimeouts`/`.Timeout` in metoda) — foloseste
|
|
||||||
timeout-ul implicit WinHTTP.
|
|
||||||
- Tratare eroare: **un singur test**, `If loHTTP.Status = 200` — orice alt rezultat (server jos, DNS,
|
|
||||||
timeout, 404, XML gol, 500) cade pe `Else -> repune_backup_cursoare(...)` (`:131`), fara mesaj propriu
|
|
||||||
si fara sa arunce exceptie — pur si simplu `This.cCursor` ramane fara date noi (`Reccount=0`).
|
|
||||||
- **Important**: aceasta metoda (`citeste_curs_bnr`) e cea care ar face fetch-ul live, dar in scenariul
|
|
||||||
raportat (azi, ora 19) **nu e apelata deloc** — vezi punctul 3, ramura (b) sare peste ea. Deci mesajul
|
|
||||||
"Nu poate fi preluat automat cursul BNR" **nu vine dintr-un fetch HTTP esuat** in acest scenariu — vine
|
|
||||||
dintr-o decizie de orar (`Hour(ltDataOra) >= 13`) care presupune ca preluarea trebuia sa se fi
|
|
||||||
intamplat deja (manual sau altfel) inainte de ora 13, si nu mai incearca live dupa aceea.
|
|
||||||
- Consecinta directa la intrebarea 5: mesajul de eroare NU distinge "serviciu indisponibil" de "data
|
|
||||||
lipsa din raspuns", pentru simplul motiv ca **in cazul raportat nici macar nu se ajunge sa se apeleze
|
|
||||||
serviciul** — testul e pur temporal (`Hour>=13`), nu bazat pe rezultatul unei incercari HTTP. Pe
|
|
||||||
ramura care CHIAR apeleaza HTTP (`citeste_curs_bnr`, cazul "azi, inainte de ora 13" sau "maine, dupa
|
|
||||||
ora 13"), un eventual esec HTTP (status <>200) e complet tacut fata de operator (fara amessagebox) —
|
|
||||||
duce doar la `Reccount=0`, ceea ce las document fara curs, fara mesaj dedicat de "server indisponibil".
|
|
||||||
|
|
||||||
## 6. Stocare si import
|
|
||||||
|
|
||||||
- View Oracle citit de UI: `gcS.vcurs` (schema curenta), coloane confirmate in
|
|
||||||
`oproceduri_curs.prg:20-21`: `id_curs, id_valuta, data, data2, nume_val, curs, multiplicator,
|
|
||||||
id_valuta_iso, iso_valuta, curs_bnr`.
|
|
||||||
- Scriere: `actualizare_curs_bnr::scrie_curs_bnr` (`onom_curs.vc2:204-225`) apeleaza
|
|
||||||
`pack_curs.scrie_cotatii_curs(?pdData,?pcSirCursuri,?gnIdUtil)` — daca ziua e vineri
|
|
||||||
(`Dow(pdData,2)=6`), scrie si pentru sambata/duminica urmatoare (`:213-216`, curs valabil pe
|
|
||||||
weekend). Aceasta scriere se intampla **doar dupa** un fetch reusit (`citeste_curs_bnr` cu
|
|
||||||
status 200), apelata din `initializeaza` la linia 198 (`If Reccount(This.cCursor)>0 Then
|
|
||||||
This.scrie_curs_bnr()`).
|
|
||||||
- Citire cotatii curente pentru o data: `pack_curs.citeste_cotatii_curs(?pdData)`
|
|
||||||
(`onom_curs.vc2:158`).
|
|
||||||
- **Nu exista, in codul text-cache din `ROAFACTURARE`/`COMUN`, niciun job/scheduler/task separat**
|
|
||||||
care sa apeleze automat `citeste_curs_bnr`/`scrie_curs_bnr` fara interventia unui utilizator care
|
|
||||||
deschide un formular de curs (`frm_curs_nou`/`frm_curs_nou_multiplu`) sau declanseaza fluxul de
|
|
||||||
eroare -20005. Singurele doua puncte de instantiere a clasei `actualizare_curs_bnr` in tot
|
|
||||||
codul (`onom_curs.vc2:1325`, `:1699`) sunt ambele in formulare UI, niciuna intr-un `.prg` de
|
|
||||||
pornire/batch/scheduler. Nu am gasit un al doilea produs/proces (in acest repo) care scrie in
|
|
||||||
aceeasi tabela `CURS` — nu pot confirma sau infirma existenta unui import extern din alt sistem
|
|
||||||
(ex. ROAPRETURI sau alt exe din suita), care nu are cache text generat si nu a fost cautat aici.
|
|
||||||
|
|
||||||
## Rezumat lant, pe scurt
|
|
||||||
|
|
||||||
`facturare_lista_de_preturi` (`oproceduri_facturare.prg:114`) -> `politica.mpr` -> `factureaza(tnTip)`
|
|
||||||
(`ofacturare.prg:103`) -> SQL `cursor_preturi(?poDate.zi_curs)` -> Oracle
|
|
||||||
`pack_facturare.verifica_cursuri_valute` -> `ORA-20005` daca lipseste cursul zilei curente -> VFP
|
|
||||||
`ofacturare.prg:313-317` -> `vizualizeaza_curs(poDate.zi_curs)` (`oproceduri_curs.prg:8`) ->
|
|
||||||
`frm_curs`/`frm_curs_nou` (`onom_curs.vc2`) -> `actualizare_curs_bnr.citeste_curs` (`:46`) ->
|
|
||||||
`initializeaza` (`:152`) -> la ora >=13 pentru data curenta, ramura care afiseaza direct
|
|
||||||
**"Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?"** (`:171`), fara sa mai
|
|
||||||
incerce fetch live si fara fallback pe ultimul curs disponibil.
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# raport doc
|
|
||||||
|
|
||||||
1. Facut: `COMUN\docs\depanare-server-update-roa.md` - adaugate 2 sectiuni la final (doar insertii, 60 linii; nimic sters/modificat):
|
|
||||||
- „Anatomia fallback-ului PACK_UTILS: de ce ORA-29024 nu inseamna certificat" (linia 181).
|
|
||||||
- „Incident 16.09.2026 (2): ROMFAST 10.0.20.36, acelasi simptom" (linia 221, se termina la 239).
|
|
||||||
2. Verificari: octeti non-ASCII = 0 (scris strict ASCII); CR = 0 (LF pastrat, ca fisierul original); `git diff --stat` = 60 insertions, 0 deletions; liniile 118-160 neat inse.
|
|
||||||
3. Corectii de linii in PACK_UTILS fata de sarcina: niciuna - 347/298, 353/304, 367/318 corespund exact. O corectie de continut: „codul de iesire nu e capturat" e prea tare - in co_2026_09_16_02 `$LASTEXITCODE` (linia 758) decide redenumirea `.part`, dar valoarea nu se logheaza; am scris exact asa.
|
|
||||||
4. Nefacut/blocat: nimic. Fara commit, fara write-back, fara Oracle.
|
|
||||||
5. Stare periculoasa: niciuna.
|
|
||||||
@@ -1,350 +0,0 @@
|
|||||||
# Raport lane src — de ce cade ramura 1 (curl/PowerShell) pe ROMFAST 10.0.20.36
|
|
||||||
|
|
||||||
READ ONLY. Nu s-a rulat nimic pe Oracle, nu s-a modificat niciun fisier de cod.
|
|
||||||
Comanda ceruta pentru teste: niciuna (nu e caz de test). Dovezi = citate din scripturi.
|
|
||||||
|
|
||||||
## Fisiere de referinta (aliasuri folosite mai jos)
|
|
||||||
|
|
||||||
- **PU** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\co_2026_09_16_02_COMUN_PACK_UTILS.sql` (azi)
|
|
||||||
- **PU_old** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\01\co_2026_01_14_01_PACK_UTILS.sql` (versiunea anterioara)
|
|
||||||
- **PUF** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\co_2026_09_16_01_COMUN_PACK_UTILS_FILE.sql` (azi)
|
|
||||||
- **PUPD** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\co_2026_09_16_03_COMUN_PACK_UPDATE.sql` (azi)
|
|
||||||
- **ESO** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2025\09\sys_2025_09_24_03_EXECUTESCRIPTOS.sql`
|
|
||||||
- **SRV** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2025\09\co_2025_09_24_02_COMUN_SERVER_INFO.sql`
|
|
||||||
- **UPD25** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2025\10\co_2025_10_05_01_COMUN_UPDATE.sql`
|
|
||||||
- **DMP** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\co_2026_08_20_01_DIAG_SPATIU_PACK.sql`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. `DownloadFileOS` integral + preconditiile de pe serverul Oracle
|
|
||||||
|
|
||||||
### 1.1 Codul (PU:681-786)
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
681: function DownloadFileOS(tcURL in varchar2,
|
|
||||||
682: tcProgramUPDDirectory in varchar2,
|
|
||||||
683: tcFileName in varchar2) return boolean as
|
|
||||||
684: lcScript clob;
|
|
||||||
685: lcDir varchar2(20) := 'DMPDIR';
|
|
||||||
686: lcFileName varchar2(60) := 'download_' ||
|
|
||||||
687: to_char(systimestamp, 'YYYYMMDDHH24MISSFF') ||
|
|
||||||
688: '.ps1';
|
|
||||||
689: lcPowerShellPath server_info.value%type;
|
|
||||||
690: lcCURLPath server_info.value%type;
|
|
||||||
691: lcDMPDIR varchar2(1000);
|
|
||||||
692: lcScriptFilePath varchar2(1000);
|
|
||||||
693: llReturn boolean := False;
|
|
||||||
694: tcDestination varchar2(1000) := PACK_UTILS_FILE.DirectoryPath(tcProgramUPDDirectory) ||
|
|
||||||
695: tcFileName;
|
|
||||||
696: lcTimeOut varchar2(5) := '60';
|
|
||||||
697: lnTimeOut number(5) := 60;
|
|
||||||
698: begin
|
|
||||||
699: BEGIN
|
|
||||||
700: select value
|
|
||||||
701: into lcPowerShellPath
|
|
||||||
702: from SERVER_INFO
|
|
||||||
703: WHERE UPPER(name) = 'POWERSHELLPATH';
|
|
||||||
704:
|
|
||||||
705: EXCEPTION
|
|
||||||
706: WHEN NO_DATA_FOUND THEN
|
|
||||||
707: NULL;
|
|
||||||
708: END;
|
|
||||||
709:
|
|
||||||
710: BEGIN
|
|
||||||
711: select value
|
|
||||||
712: into lcTimeOut
|
|
||||||
713: from SERVER_INFO
|
|
||||||
714: WHERE UPPER(name) = 'POWERSHELLTIMEOUT';
|
|
||||||
715:
|
|
||||||
716: EXCEPTION
|
|
||||||
717: WHEN NO_DATA_FOUND THEN
|
|
||||||
718: NULL;
|
|
||||||
719: END;
|
|
||||||
720: lnTimeOut := GREATEST(TO_NUMBER(lcTimeOut), 5);
|
|
||||||
721:
|
|
||||||
722:
|
|
||||||
723: BEGIN
|
|
||||||
724: select value
|
|
||||||
725: into lcCURLPath
|
|
||||||
726: from SERVER_INFO
|
|
||||||
727: WHERE UPPER(name) = 'CURLPATH';
|
|
||||||
728:
|
|
||||||
729: EXCEPTION
|
|
||||||
730: WHEN NO_DATA_FOUND THEN
|
|
||||||
731: NULL;
|
|
||||||
732: END;
|
|
||||||
733:
|
|
||||||
734: lcDMPDIR := PACK_UTILS_FILE.DirectoryPath(lcDir);
|
|
||||||
735:
|
|
||||||
736: if lcDMPDIR is not null then
|
|
||||||
737: lcScriptFilePath := lcDMPDIR || lcFileName;
|
|
||||||
738: END if;
|
|
||||||
739:
|
|
||||||
740: -- Nu se continua daca nu exista calea catre POWERSHELL
|
|
||||||
741: if lcPowerShellPath is null or lcScriptFilePath is null then
|
|
||||||
742: goto sfarsit;
|
|
||||||
743: end if;
|
|
||||||
744:
|
|
||||||
745: -- Sterg scriptul ps anterior si fisierul destinatie ramas de la o rulare anterioara
|
|
||||||
746: pack_utils_file.filedelete(lcDir, lcFileName);
|
|
||||||
747: pack_utils_file.filedelete(tcProgramUPDDirectory, tcFileName);
|
|
||||||
748:
|
|
||||||
749: -- Creez pe disk scriptul ps pentru download
|
|
||||||
750: -- Descarc in <fisier>.part si redenumesc la final, ca fisierul destinatie
|
|
||||||
751: -- sa apara doar complet si eliberat de curl (altfel citirea lui da ORA-29283/ORA-29291)
|
|
||||||
752: if lcCURLPath is null then
|
|
||||||
753: lcCURLPath := 'C:\Windows\System32\curl.exe';
|
|
||||||
754: end if;
|
|
||||||
755:
|
|
||||||
756: lcScript := '& "' || lcCURLPath || '" -k -o "' || tcDestination ||
|
|
||||||
757: '.part" "' || tcURL || '"' || chr(13) || chr(10) ||
|
|
||||||
758: 'if ($LASTEXITCODE -eq 0) { Move-Item -Force -LiteralPath "' ||
|
|
||||||
759: tcDestination || '.part" -Destination "' || tcDestination ||
|
|
||||||
760: '" } else { Remove-Item -Force -ErrorAction SilentlyContinue -LiteralPath "' ||
|
|
||||||
761: tcDestination || '.part" }';
|
|
||||||
762:
|
|
||||||
763: pack_utils_file.clob2fileX(lcScript, lcDir, lcFileName);
|
|
||||||
764:
|
|
||||||
765: -- Execut scriptul ps
|
|
||||||
766: sys.ExecuteScriptOS(lcPowerShellPath, lcScriptFilePath);
|
|
||||||
767:
|
|
||||||
768: llReturn := True;
|
|
||||||
769:
|
|
||||||
770: -- Astept maxim 10 minute sa se descarce fisierul
|
|
||||||
771: for lnSleep in 1 .. lnTimeOut loop
|
|
||||||
772: exit when PACK_UTILS_FILE.FileExists(tcProgramUPDDirectory,
|
|
||||||
773: tcFileName);
|
|
||||||
774: sys.dbms_lock.sleep(1);
|
|
||||||
775: end loop;
|
|
||||||
776:
|
|
||||||
777: llReturn := PACK_UTILS_FILE.FileExists(tcProgramUPDDirectory,
|
|
||||||
778: tcFileName);
|
|
||||||
779:
|
|
||||||
780: -- Sterg scriptul ps (are nume unic, altfel se aduna in DMPDIR)
|
|
||||||
781: pack_utils_file.filedelete(lcDir, lcFileName);
|
|
||||||
782:
|
|
||||||
783: <<sfarsit>>
|
|
||||||
784:
|
|
||||||
785: return llReturn;
|
|
||||||
786: end DownloadFileOS;
|
|
||||||
```
|
|
||||||
|
|
||||||
Intrarea in functie, dar si toata `URL2Clob` (contractul apelantului), se citeste la PU:333-376; ramura 1 se deschide doar la PU:347 (`IF lcPowerShellDownload = '1' THEN`), citirea fiind PU:341-344.
|
|
||||||
|
|
||||||
### 1.2 Fiecare preconditie care trebuie indeplinita PE SERVERUL ORACLE
|
|
||||||
|
|
||||||
| # | Preconditie | Unde se citeste/consuma | Valoare asteptata | Daca nu e indeplinita |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| P1 | `SERVER_INFO(NAME='POWERSHELLDOWNLOAD')` | PU:341-347 | `VALUE = '1'` exact | ramura 1 nu se intra deloc → direct `HTTPURITYPE` (PU:367) → ORA-29273/29024. Daca randul lipseste: `NO_DATA_FOUND` neprins, alta eroare. |
|
|
||||||
| P2 | `SERVER_INFO(NAME='POWERSHELLPATH')` | PU:700-703, garda PU:741 | non-NULL, ex. `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe` (SRV:7) | `goto sfarsit` (PU:741-743) → `return FALSE`, FARA nicio urma |
|
|
||||||
| P3 | directorul Oracle `DMPDIR` exista (si, mai jos, e scriibil) | `DirectoryPath('DMPDIR')` PUF:327-343 → PU:734-738 | rand in `all_directories` | `lcScriptFilePath` ramane NULL → `goto sfarsit` (PU:741) → `FALSE`, fara urma. Scriptul `.ps1` nu se poate scrie (PUF `CLOB2FILEX` → `dbms_xslprocessor.clob2file`) → exceptie |
|
|
||||||
| P4 | directorul de destinatie `tcProgramUPDDirectory` (ex. `UPD_ROACONT`, `DMPDIR`) exista | `DirectoryPath(...)` PUF:327-343 → PU:694 | rand in `all_directories` | `DirectoryPath` intoarce NULL → `tcDestination = NULL||tcFileName = tcFileName` (doar numele, fara cale, PU:694) → curl scrie in CWD-ul jobului; `FileExists` pe un director inexistent ridica ORA-29280 (PUF:169 `utl_file.fgetattr`) |
|
|
||||||
| P5 | `sys.ExecuteScriptOS` exista si e executabil din `CONTAFIN_ORACLE` | PU:766, definit in ESO:1-21 | procedura in schema SYS + grant | `ORACLE` nu-l vede / PLS-00201 sau ORA-06550; `ESO:16` in varianta curenta NU mai contine grant-ul (vezi 1.4) |
|
|
||||||
| P6 | contul OS al jobului extern | DBMS_SCHEDULER job `job_type=>'executable'` (ESO:7-13) | contul de rulare al serviciului OracleJobScheduler are drept de executare `powershell.exe`, citire `.ps1`, scriere in DMPDIR si in directorul destinatie, executare `curl.exe`, acces retea+TLS catre `https://roa.romfast.ro` | jobul extern nu porneste / porneste si nu are drepturi → `.ps1` scris pe disc dar fara efect. **Punctul cel mai probabil de esec pe o instanta neconfigurata.** |
|
|
||||||
| P7 | `SERVER_INFO(NAME='POWERSHELLTIMEOUT')` | PU:710-720, folosit PU:771 | numeric, ex. `'30'` (SRV:11) sau lipsa → default `'60'` (PU:696) | daca randul EXISTA dar e gol/NULL: `GREATEST(TO_NUMBER(NULL),5)=NULL` → `for lnSleep in 1..NULL` = 0 iteratii → verificarea de existenta se face imediat → `FALSE` (fals negativ), desi descarcarea ar fi reusit in cateva secunde |
|
|
||||||
| P8 | `SERVER_INFO(NAME='CURLPATH')` (optional) | PU:723-727, folosit PU:752-754 | cale valida la `curl.exe` | NU e preconditie: daca lipseste, codul cade pe `C:\Windows\System32\curl.exe` (PU:753). Daca acel fisier nu exista pe OS, scriptul PowerShell da eroare → fara fisier |
|
|
||||||
|
|
||||||
**Criteriul de succes este exclusiv existenta fisierului** (PU:777-778). Codul de iesire al `curl.exe` este folosit doar in interiorul scriptului `.ps1` ca sa decida redenumirea (PU:758) — Oracle nu il vede niciodata. Preconditiile P6/P7/P8 sunt OS, deci SQL nu le poate confirma direct.
|
|
||||||
|
|
||||||
### 1.3 Unde scrie `.part` si unde il muta
|
|
||||||
|
|
||||||
- `tcDestination` = `DirectoryPath(tcProgramUPDDirectory) || tcFileName` (PU:694-695) — calea FIZICA a directorului Oracle.
|
|
||||||
- curl scrie in `<tcDestination>.part` (`-o`, PU:756-757), apoi `Move-Item -Force` in `<tcDestination>` doar daca `$LASTEXITCODE -eq 0`; altfel `Remove-Item` pe `.part` (PU:758-761).
|
|
||||||
- Daca directorul nu exista (P4) → `tcDestination` fara cale → fisierul ajunge in directorul curent al procesului jobului.
|
|
||||||
- Daca directorul nu e scriibil pentru contul OS (P6): curl da eroare → nu apare `.part` si nu apare nici destinatia → `FALSE`. Pe vechiul cod (fara `.part`) eroarea se manifesta la CITIRE (ORA-29283/29291), pe cel nou se manifesta ca „fisier lipsa“ (vezi sectiunea 3).
|
|
||||||
|
|
||||||
### 1.4 `sys.ExecuteScriptOS` — ce e exact si ce cere
|
|
||||||
|
|
||||||
Definitie (ESO:1-21):
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
1: create or replace procedure ExecuteScriptOS(tcPowerShellPath in varchar2,
|
|
||||||
2: tcScriptPath in varchar2) as
|
|
||||||
3: lcJobName varchar2(500);
|
|
||||||
4: begin
|
|
||||||
5: lcJobName := 'exec_ps_' || SUBSTR(SYS_GUID(), 1, 8);
|
|
||||||
6: DBMS_SCHEDULER.CREATE_JOB(
|
|
||||||
7: job_name => lcJobName,
|
|
||||||
8: job_action => tcPowerShellPath,
|
|
||||||
9: number_of_arguments => 4,
|
|
||||||
10: job_type => 'executable',
|
|
||||||
11: enabled => FALSE
|
|
||||||
12: );
|
|
||||||
13: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 1, '-ExecutionPolicy');
|
|
||||||
14: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 2, 'Bypass');
|
|
||||||
15: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 3, '-File');
|
|
||||||
16: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 4, tcScriptPath);
|
|
||||||
17: DBMS_SCHEDULER.ENABLE(lcJobName);
|
|
||||||
18: end ExecuteScriptOS;
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Nu** e Java stored proc, **nu** e DBMS_SCHEDULER „program“ cu credential, **nu** e extproc: e un **DBMS_SCHEDULER external executable job** (`job_type => 'executable'`, ESO:10). Comanda lansata pe OS: `<tcPowerShellPath> -ExecutionPolicy Bypass -File <tcScriptPath>` (ESO:13-16).
|
|
||||||
- Proprietar: **SYS** — fisierul e `sys_*`, iar `UpdateVersiune('sys_2025_09_24_03_EXECUTESCRIPTOS','','SYS')` (ESO:24); apelul e calificat `sys.ExecuteScriptOS` (PU:766). S-a schimbat de la 1 argument (2023/05 si 2024/02) la 4 argumente (`-ExecutionPolicy Bypass -File`) in `sys_2025_09_24_03`.
|
|
||||||
- Componente/privilegii cerute:
|
|
||||||
- `EXECUTE` pe `SYS.ExecuteScriptOS` pentru `CONTAFIN_ORACLE`. In ESO:24 (versiunea din 16.09.2026) **nu mai exista linia de grant** — trimite la `grant execute on EXECUTESCRIPTOS to CONTAFIN_ORACLE` din scriptul de instalare `ALTELE\Creare_server_scripturi\sys_2024_02_22_01_COMUN.sql:16`. Daca procedura a fost recreata de ESO (fara grant), grant-ul mai vechi **se pastreaza** (CREATE OR REPLACE nu-l pierde), dar pe o instanta curata trebuie rulat scriptul de instalare. **Neverificat pe ROMFAST.**
|
|
||||||
- Componenta `DBMS_SCHEDULER` si `CREATE JOB` — se executa in SYS (droits de definer), deci nu cere `CREATE EXTERNAL JOB` in `CONTAFIN_ORACLE`.
|
|
||||||
- **Contul OS al jobului extern** — conditionarea reala: procesul `OracleJobScheduler<SID>` (Windows) ruleaza ca un cont de serviciu; acel cont trebuie sa poata executa `powershell.exe`, sa citeasca `.ps1`-ul din DMPDIR, sa scrie in DMPDIR si in directorul destinatie, sa execute `curl.exe` si sa iasa in retea pe `https://roa.romfast.ro`. Fara aceste drepturi, jobul ruleaza si esueaza fara ca Oracle sa vada ceva (codul de iesire nu e citit).
|
|
||||||
- Nu s-a gasit niciun script in `SCRIPTURI_CLAR` / `ALTELE` care sa acorde `CREATE ANY DIRECTORY` sau `READ,WRITE ON DIRECTORY DMPDIR` (grep pe tot `D:\ROA\DATABASE`): DMPDIR vine din instalare — `create or replace directory DMPDIR as 'C:\DMPDIR'` (`ALTELE\Creare_server_scripturi\sys_2012_06_21_01_FIRMA.sql:119`) si `grant all on directory DMPDIR to CONTAFIN_ORACLE` (`SCRIPTURI_CLAR\2013\01\sys_2013_01_23_02.sql:8`). Atentie: in `SCRIPTURI_CLAR\2012\06\sys_2012_06_21_01_FIRMA.sql:388` linia de grant este **comentata** (`rem grant all on directory DMPDIR to CONTAFIN_ORACLE;`) — pe instantele instalate din acel script, grant-ul poate lipsi. **Neverificat pe ROMFAST.**
|
|
||||||
- Jobul NU are `auto_drop` (ESO:6-12) → dupa rulare ramane in `DBA_SCHEDULER_JOBS` ca job one-time, cu nume unic `exec_ps_<8 hex>`. Util pentru a dovedi daca jobul a fost lansat vreodata.
|
|
||||||
|
|
||||||
### 1.5 Normalizarea directorului — de unde vine `lcDir`
|
|
||||||
|
|
||||||
`lcDir` este **hardcodat** `'DMPDIR'` in ambele locuri (PU:336 in URL2Clob, PU:685 in DownloadFileOS); nu vine nici din parametru, nici din `SERVER_INFO`. `PACK_UTILS_FILE.DirectoryPath` (PUF:327-343) il rezolva la calea fizica prin `select DIRECTORY_PATH into lcPath from all_directories where directory_name = UPPER(tcDirectoryName)` si adauga bara (PUF:336). Deci:
|
|
||||||
|
|
||||||
- numele `DMPDIR` este un **obiect Oracle DIRECTORY**, nu o cale;
|
|
||||||
- drepturile semnificative sunt pe obiectul DIRECTORY: `READ` (pentru `FGETATTR`/`FREMOVE`/`READ2CLOB`) si `WRITE` (pentru `CLOBFILE2FILE`/`FREMOVE`) — PUF:169, PUF:185, PUF:302, PUF:321.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Ce se logheaza la esecul ramurii 1
|
|
||||||
|
|
||||||
**Raspuns explicit: in ramura 1 nu se logheaza absolut nimic.** `DownloadFileOS` nu are `UPD_LOG`, nu are `DBMS_OUTPUT`, nu are `RAISE`; singurul rezultat al esecului este `return FALSE` (PU:693, PU:777-785). Nu exista nici macar urma ca a fost incercata.
|
|
||||||
|
|
||||||
Traseul erorii, in continuare:
|
|
||||||
|
|
||||||
1. `URL2Clob` cade pe `HTTPURITYPE.createuri(tcURL).getclob()` (PU:367) → ORA-29273 (peste `https` fara wallet → ORA-29024).
|
|
||||||
2. Handler-ul `URL2Clob` (PU:369-375) sterge doar fisierul temporar si face `RAISE`; **nu logheaza**.
|
|
||||||
3. `UPDLOG` nu este apelat pe acest drum inainte de `URL2Clob` — singurul `UPDLOG` din pasul listelor este DUPĂ ambele descarcari, la PUPD:427, deci nu se executa niciodata daca `URL2Clob` a aruncat.
|
|
||||||
|
|
||||||
Locul exact al apelului (PUPD:416-427):
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
418: lcListaPrograme := PACK_UTILS.URL2Clob(lcURLAppXml);
|
|
||||||
...
|
|
||||||
423: lcListaRoastart := PACK_UTILS.URL2Clob(lcURLRoastartXml);
|
|
||||||
...
|
|
||||||
427: UPDLOG(lcText, ldDataOraS);
|
|
||||||
```
|
|
||||||
|
|
||||||
Apelurile 418/423 sunt in corpul procedurii `UpdateApp` (inceput la PUPD:309), **in afara** blocului `begin/exception` de la PUPD:434-520 (care prinde doar bucla de scanare a programelor, PUPD:514-519). `UpdateApp` este `pragma autonomous_transaction` (PUPD:310) si nu are handler propriu → exceptia iese din `UpdateApp`, iar `UpdateROA` **nu are nici el handler** (apel la PUPD:275; `UpdateROA` se termina la PUPD:303) → nu se ajunge nici la `EmailLog` de la PUPD:300-302.
|
|
||||||
|
|
||||||
**Unde s-ar fi putut loga:**
|
|
||||||
- PUPD:427 (primul `UPDLOG`) — dar e dupa `URL2Clob`;
|
|
||||||
- un `begin/exception` in jurul lui PUPD:418/423 (modelul exista la buletinul informativ, PUPD:729-737, care logheaza `'ERR: ' || lcURL... || sqlcode || sqlerrm`);
|
|
||||||
- in `DownloadFileOS` sau in handler-ul `URL2Clob` (PU:369-375).
|
|
||||||
|
|
||||||
**Ce urme ramane totusi:**
|
|
||||||
- `UPD_ISTORIC.STARE` = `UPDATE_STARE_APLICATII`, scris la PUPD:274 inainte de `UpdateApp` (`UpdateIstoric`, PUPD:2408-2436) → stare lasata „blocat la aplicatii“.
|
|
||||||
- Un rand de eroare in jobul DBMS_SCHEDULER `UPDATEROA_JOB` (creat la PUPD:178-186, `job_action => 'PACK_UPDATE.UpdateROA'`) — deci `DBA_SCHEDULER_JOB_LOG` / `_RUN_DETAILS.ADDITIONAL_INFO`, nu `UPD_LOG`.
|
|
||||||
- **Diferenta importanta de diagnostic:** daca esecul e pe buletinul informativ (PUPD:730), `UPD_LOG` **contine** un rand `'ERR: <url> ORA-29273 ... ORA-29024'` (PUPD:732-736). Daca `UPD_LOG` nu are niciun rand `ERR:` pentru rularea respectiva, esecul e la PUPD:418 sau 423.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Ce a schimbat scriptul de azi fata de versiunea anterioara
|
|
||||||
|
|
||||||
Versiunea precedenta a lui PACK_UTILS: **PU_old** (`co_2026_01_14_01_PACK_UTILS.sql`, 14.01.2026). Urmatoarea in istoricul de fisiere: `2025/09/co_2025_09_24_01_COMUN_PACK_UTILS.sql` (introduce DownloadFileOS + POWERSHELLDOWNLOAD).
|
|
||||||
|
|
||||||
### 3.1 Diff conceptual (numai `DownloadFileOS`)
|
|
||||||
|
|
||||||
| Element | PU_old | PU (azi) |
|
|
||||||
|---|---|---|
|
|
||||||
| numele scriptului `.ps1` | `varchar2(30) := 'download_file.ps1'` (PU_old:680) — FIX | `varchar2(60) := 'download_' || to_char(systimestamp,'YYYYMMDDHH24MISSFF') || '.ps1'` (PU:686-688) — unic |
|
|
||||||
| sterge fisierul destinatie inainte | nu | `pack_utils_file.filedelete(tcProgramUPDDirectory, tcFileName)` (PU:747) |
|
|
||||||
| scrie in | direct `<dest>` (PU_old:743-747) | `<dest>.part`, apoi `Move-Item -Force` (PU:756-761) |
|
|
||||||
| rezultat partial | fisier partial vizibil (de aceea bug-ul ORA-29283/29291, PU:750-751) | fisierul final apare doar dupa `Move-Item` reusit |
|
|
||||||
| sterge `.ps1` la final | nu | `pack_utils_file.filedelete(lcDir, lcFileName)` (PU:781) |
|
|
||||||
| preconditii de intrare | identice (POWERSHELLDOWNLOAD / POWERSSHELLPATH / DMPDIR / CURLPATH / TIMEOUT) | identice, neschimbate |
|
|
||||||
| `URL2Clob` / `URL2Blob` | — | neschimbate logic (se schimba doar diacriticele din comentarii) |
|
|
||||||
|
|
||||||
### 3.2 Verdict: **NU** — modificarea de azi nu a „rupt“ ramura 1 prin preconditii
|
|
||||||
|
|
||||||
Cu citat: garda de intrare din `URL2Clob` este neschimbata (`IF lcPowerShellDownload = '1' THEN` PU:347, identic PU_old:292), garda de iesire din `DownloadFileOS` este neschimbata (`if lcPowerShellPath is null or lcScriptFilePath is null then goto sfarsit;` PU:741-743, identic PU_old:733-735), aceleasi nume de `SERVER_INFO` (PU:703/714/727 vs PU_old:695/706/719) si acelasi `lcDir := 'DMPDIR'` (PU:685 vs PU_old:679). **Niciun director nou, nicio preconditie in minus.**
|
|
||||||
|
|
||||||
### 3.3 Ce s-a schimbat, totusi, in comportamentul de esec (relevant pentru simptom)
|
|
||||||
|
|
||||||
Modificarea poate fi **neverificat** cauza incidentului, dar nu prin preconditii, ci prin **felul in care se manifesta un esec deja existent**:
|
|
||||||
|
|
||||||
- Inainte: curl scria direct in `<dest>`; daca fisierul era tinut deschis de alt proces, citirea dadea **ORA-29283/ORA-29291** (exact motivul notat in comentariul de azi, PU:750-751).
|
|
||||||
- Acum: curl scrie in `<dest>.part`, iar `<dest>` apare **doar daca `Move-Item` reuseste** (PU:758). Un `Move-Item` care esueaza (fisier destinatie blocat, drepturi, antivirus) este **inghitit tacit** — scriptul nu scrie nimic in log, iar Oracle testeaza doar existenta fisierului (PU:777-778) → `FALSE` → fallback `HTTPURITYPE` → **ORA-29273/ORA-29024**.
|
|
||||||
- Deci: daca pe ROMFAST conditia de baza era deja marginala, eroarea raportata acum **se schimba** din ORA-29291 in ORA-29024, fara ca vreo preconditie sa fi fost scoasa.
|
|
||||||
- A doua schimbare de azi, in `PACK_UTILS_FILE` (**PUF**), merge in acelasi sens: `FileDelete` a devenit best-effort (`when others then null`, PUF:187-192), deci nu mai poate masca eroarea originala cu ORA-29291. Nici aceasta nu introduce preconditii.
|
|
||||||
|
|
||||||
Concluzie punct 3: **NU** (nu a rupt-o prin preconditii) + **neverificat** daca a contribuit la simptomul de azi (ar cere dovezi OS: exista `.part` sau `.ps1` ramase in DMPDIR?).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Interogari read-only de rulat pe ROMFAST (scrise, NU rulate)
|
|
||||||
|
|
||||||
Toate sunt `SELECT`. Pentru Oracle 10.2 compatibil (fara `FETCH FIRST`).
|
|
||||||
|
|
||||||
**Q1 — configurarea, cheia de departajare a ipotezelor.**
|
|
||||||
```sql
|
|
||||||
SELECT name, value FROM SERVER_INFO
|
|
||||||
WHERE UPPER(name) IN ('POWERSHELLDOWNLOAD','POWERSHELLPATH','POWERSHELLTIMEOUT',
|
|
||||||
'CURLPATH','DMPDIR','ROAUPDATEPATH','UPDATEPREREQ')
|
|
||||||
ORDER BY name;
|
|
||||||
```
|
|
||||||
- `POWERSHELLDOWNLOAD` ∈ {`'0'`, `''`, NULL} → **confirma** ca ramura 1 nu se intra (cauza = P1) si ca ORA-29024 e garantat; `='1'` → **infirma**, ramura 1 se incearca.
|
|
||||||
- `POWERSHELLPATH` NULL/`''` → **confirma** ca `DownloadFileOS` iese imediat cu `FALSE` (P2) — suspectul principal; non-NULL → infirma.
|
|
||||||
- `POWERSHELLTIMEOUT` prezent dar gol → **confirma** falsul negativ din P7; absent → default 60s (ok).
|
|
||||||
- `UPDATEPREREQ='1'` → explica de ce DMPDIR/update dirs **nu** mai sunt re-verificate la fiecare update.
|
|
||||||
|
|
||||||
**Q2 — directorul Oracle (P3/P4).**
|
|
||||||
```sql
|
|
||||||
SELECT directory_name, directory_path FROM all_directories
|
|
||||||
WHERE directory_name = 'DMPDIR' OR directory_name LIKE 'UPD_%' ORDER BY 1;
|
|
||||||
```
|
|
||||||
Lipsa lui `DMPDIR` → **confirma** ca ramura 1 e imposibila (`lcScriptFilePath` NULL); lipsa unui `UPD_<PROGRAM>` → alta eroare (ORA-29280), nu ORA-29024.
|
|
||||||
|
|
||||||
**Q3 — drepturile pe DMPDIR si pe SYS.ExecuteScriptOS (P5).**
|
|
||||||
```sql
|
|
||||||
SELECT grantee, privilege FROM all_tab_privs
|
|
||||||
WHERE table_name = 'DMPDIR' AND privilege IN ('READ','WRITE') ORDER BY 1;
|
|
||||||
SELECT owner, object_name, status FROM all_objects
|
|
||||||
WHERE object_name = 'EXECUTESCRIPTOS' AND object_type = 'PROCEDURE';
|
|
||||||
SELECT grantee FROM all_tab_privs
|
|
||||||
WHERE table_name = 'EXECUTESCRIPTOS' AND privilege = 'EXECUTE';
|
|
||||||
```
|
|
||||||
Lipsa `READ`/`WRITE` pentru `CONTAFIN_ORACLE`/`PUBLIC` → confirma ca fisierul nu se poate scrie/citi; `STATUS='INVALID'` sau lipsa grant → confirma P5.
|
|
||||||
|
|
||||||
**Q4 — urma din UPD_LOG / UPD_ISTORIC (punctul 2).**
|
|
||||||
```sql
|
|
||||||
SELECT secventa, TO_CHAR(dataora,'DD.MM.YYYY HH24:MI:SS') d, SUBSTR(explizatie,1,300) t
|
|
||||||
FROM UPD_LOG WHERE dataora > SYSDATE - 7 ORDER BY secventa;
|
|
||||||
SELECT TO_CHAR(dataora_start,'DD.MM.YYYY HH24:MI:SS') s, stare, id_util
|
|
||||||
FROM UPD_ISTORIC WHERE dataora_start > SYSDATE - 7 ORDER BY dataora_start;
|
|
||||||
```
|
|
||||||
Prezenta unui rand `ERR:` cu `ORA-29273`/`ORA-29024` → esecul e pe buletinul informativ (PUPD:730). **Absenta** oricarui `ERR:` + `STARE` = starea „aplicatii“ → confirma esecul la PUPD:418/423 (nelogat).
|
|
||||||
|
|
||||||
**Q5 — joburile externe si jobul de update (P6) — dovedeste daca PowerShell a fost lansat vreodata.**
|
|
||||||
```sql
|
|
||||||
SELECT job_name, TO_CHAR(log_date,'DD.MM.YYYY HH24:MI:SS') d, status, error#, SUBSTR(additional_info,1,500) info
|
|
||||||
FROM (SELECT * FROM dba_scheduler_job_log ORDER BY log_date DESC)
|
|
||||||
WHERE (job_name LIKE 'exec_ps_%' OR job_name = 'UPDATEROA_JOB') AND ROWNUM <= 50;
|
|
||||||
SELECT job_name, enabled, state, TO_CHAR(last_start_date,'DD.MM.YYYY HH24:MI:SS') last_start
|
|
||||||
FROM dba_scheduler_jobs WHERE job_name LIKE 'exec_ps_%' OR job_name = 'UPDATEROA_JOB';
|
|
||||||
```
|
|
||||||
Existenta joburilor `exec_ps_*` cu `status='SUCCEEDED'` → PowerShell a rulat cel putin o data (**infirma** „instanta neconfigurata“); zero joburi `exec_ps_*` → **confirma** ca `sys.ExecuteScriptOS` nu s-a executat niciodata; `UPDATEROA_JOB` esuat cu ORA-29273/29024 in `additional_info` → confirma locul erorii.
|
|
||||||
|
|
||||||
**Q6 — URL-urile reale (ca sa nu banuim degeaba https-ul).**
|
|
||||||
```sql
|
|
||||||
SELECT varname, varvalue FROM OPTIUNI WHERE varname LIKE 'UPD_URL%';
|
|
||||||
SELECT TO_NUMBER(detalii) customerid FROM SYS.AUTH_DETALII;
|
|
||||||
```
|
|
||||||
`UPD_URL_APP` fara `https://` sau NULL → s-ar lua default-ul http (PUPD:379/381), unde `HTTPURITYPE` ar putea functiona — deci **infirma** ipoteza „https fara wallet“ si muta cauza in alta parte. `https://roa.romfast.ro` → **confirma** ca esecul ramurii 1 duce obligatoriu in ORA-29024.
|
|
||||||
|
|
||||||
**Q7 — identitatea instantei (sa nu raportam de pe alt server).**
|
|
||||||
```sql
|
|
||||||
SELECT SYS_CONTEXT('USERENV','SERVER_HOST') host, SYS_CONTEXT('USERENV','DB_NAME') db,
|
|
||||||
SYS_CONTEXT('USERENV','INSTANCE_NAME') inst FROM dual;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verificari OS (NU se pot face prin SQL — marcat neverificat pana se fac)
|
|
||||||
|
|
||||||
In directorul fizic al `DMPDIR` (Q2/Q1), pe ROMFAST:
|
|
||||||
- exista fisiere `url2clob_*.tmp.part` sau `url2app_*.part` rămase → curl **a scris**, dar `Move-Item` a esuat (blocare/drepturi) → confirma scenariul din 3.3;
|
|
||||||
- exista fisiere `download_*.ps1` acumulate → scriptul se scrie, dar jobul extern nu ruleaza/vrea sa-l ruleze → confirma P5/P6;
|
|
||||||
- nu exista nici `.part`, nici `.ps1` → `DownloadFileOS` nu a ajuns la scriere deloc → confirma P1/P2/P3;
|
|
||||||
- `C:\Windows\System32\curl.exe` exista si depinde daca `CURLPATH` e setat (PU:752-754).
|
|
||||||
- in `DBA_SCHEDULER_JOB_RUN_DETAILS.ADDITIONAL_INFO` se vede codul de iesire OS al jobului extern PowerShell (singurul loc unde ajunge `$LASTEXITCODE`-ul real al scenariului).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Nefacut / deschis
|
|
||||||
|
|
||||||
- Nu s-a rulat nicio interogare si nicio comanda pe ROMFAST/10.0.20.36 (interzis de sarcina): **cine a produs incidentul de azi ramane neverificat pana la Q1-Q7 + verificarile OS.**
|
|
||||||
- Nu am putut confirma daca `POWERSHELLPATH` / `CURLPATH` / grant-urile pe `DMPDIR` sunt configurate pe ROMFAST — nu apar in niciun script de migrare (verificat prin grep pe `D:\ROA\DATABASE`), deci sunt setari de instalare/OS.
|
|
||||||
- Nu s-a facut write-back, nu s-a modificat niciun fisier (in afara de acest raport), nu s-a pornit niciun proces.
|
|
||||||
- Stare periculoasa: **niciuna**.
|
|
||||||
|
|
||||||
GATA src
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
# S0 - Prototip context/handoff: fix SubagentStop, -StareFile, compactare mai devreme
|
|
||||||
|
|
||||||
Executat 17.09.2026, pe masina curenta (fara UI, conform plan_qa_factura_aviz.md sectiunea S0).
|
|
||||||
|
|
||||||
## Ce am gasit
|
|
||||||
|
|
||||||
- Hook-ul `SubagentStop` din `C:\Users\mmari\.claude\settings.json` (era la cheia
|
|
||||||
`hooks.SubagentStop[0].hooks[0].command`) era un simplu `echo '{"hookSpecificOutput":...}'`
|
|
||||||
static, executat de fiecare data cand un subagent se opreste, fara sa citeasca deloc stdin-ul
|
|
||||||
hook-ului. Nu bloca explicit (nu avea `decision:block`/exit 2), dar reinjecta acelasi
|
|
||||||
`additionalContext` la fiecare `SubagentStop`, inclusiv la reincercarile in acelasi ciclu de
|
|
||||||
stop - de aici bucla observata (patru agenti raportand cicluri repetate in aceeasi sesiune).
|
|
||||||
Cauza + fix documentate in `docs/qa_factura_hooks_ref.md` sectiunea 4 ("stop_hook_active si
|
|
||||||
bucla"): campul `stop_hook_active` trebuie citit de pe stdin, iar la `true` hook-ul trebuie sa
|
|
||||||
iasa imediat cu 0, fara iesire.
|
|
||||||
- `context_watch.ps1` e in `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`. Verificat: acel `COMUN`
|
|
||||||
e checkout separat al aceluiasi repo `git@gitea.romfast.ro:romfast/comun.git` ca si
|
|
||||||
`D:\ROA\ROAFACTURARE\COMUN` (acelasi `origin`, ramuri `main`) - deci e intr-adevar biblioteca
|
|
||||||
partajata, doar clonata separat per produs. Nu l-am mutat: mutarea in `COMUN` (punctul 4 din
|
|
||||||
planul S0) nu a fost in cele trei sarcini primite de la team lead pentru aceasta sesiune, ramane
|
|
||||||
neacoperit (vezi mai jos).
|
|
||||||
- Scriptul deja avea parametrii `-Stdout` (UserPromptSubmit) si `-OSinguraData` (PostToolUse,
|
|
||||||
dedup prin fisier marcaj in `%TEMP%`); nu avea niciun mecanism de scriere pe disc a starii.
|
|
||||||
- `env` in `settings.json` nu exista `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`.
|
|
||||||
|
|
||||||
## Ce am schimbat
|
|
||||||
|
|
||||||
### 1. Garda `stop_hook_active` pe hook-ul SubagentStop
|
|
||||||
|
|
||||||
`C:\Users\mmari\.claude\settings.json`, cheia `hooks.SubagentStop[0].hooks[0].command`
|
|
||||||
(inainte de orice modificare am facut copie de siguranta la
|
|
||||||
`C:\Users\mmari\.claude\settings.json.bak_20260917`, verificata pe disc):
|
|
||||||
|
|
||||||
Inainte:
|
|
||||||
```
|
|
||||||
echo '{"hookSpecificOutput":{"hookEventName":"SubagentStop","additionalContext":"..."}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Dupa:
|
|
||||||
```
|
|
||||||
grep -q '"stop_hook_active"[[:space:]]*:[[:space:]]*true' && exit 0; echo '{"hookSpecificOutput":{"hookEventName":"SubagentStop","additionalContext":"..."}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Mesajul `additionalContext` a ramas identic (neschimbat), doar prefixat cu garda. `grep -q`
|
|
||||||
citeste stdin-ul hook-ului (JSON-ul cu `stop_hook_active`) fara dependenta de `jq` (nu e instalat
|
|
||||||
pe masina - verificat). Daca patternul gaseste `"stop_hook_active":true` (cu sau fara spatiu dupa
|
|
||||||
`:`), hook-ul iese cu 0 fara sa emita nimic; altfel continua la `echo` ca inainte.
|
|
||||||
|
|
||||||
### 2. `-StareFile` in `context_watch.ps1`
|
|
||||||
|
|
||||||
`D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`:
|
|
||||||
- linia 20: parametru nou `[string]$StareFile`.
|
|
||||||
- liniile 23-36: functia noua `Scrie-AntetStare` - daca fisierul exista si primul rand se
|
|
||||||
potriveste cu `^<!-- context_watch:`, il inlocuieste; altfel adauga antetul ca prim rand
|
|
||||||
(fisier nou sau fisier existent fara antet). Restul continutului ramane neschimbat.
|
|
||||||
- linia 92: apel `if ($StareFile) { Scrie-AntetStare -cale $StareFile -tokeni $total -nivel $nivel }`
|
|
||||||
in blocul `if ($mesaj)`, deci se declanseaza exact cand pragul de avertisment sau max e atins
|
|
||||||
(acelasi punct unde se decide mesajul), indiferent de `-Stdout`/`-OSinguraData`.
|
|
||||||
- Comportamentul fara `-StareFile` e neschimbat: parametrul e opt, iar apelul e conditionat de
|
|
||||||
prezenta lui.
|
|
||||||
|
|
||||||
### 3. `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` in settings
|
|
||||||
|
|
||||||
`C:\Users\mmari\.claude\settings.json`, cheia `env` (exista deja, avea doar
|
|
||||||
`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`): adaugat `"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "70"`.
|
|
||||||
Nicio alta setare atinsa.
|
|
||||||
|
|
||||||
## Rezultatul probelor cerute
|
|
||||||
|
|
||||||
1. **`settings.json` e JSON valid** dupa modificare:
|
|
||||||
`Get-Content ... -Raw | ConvertFrom-Json` -> a rulat fara eroare, a scris `JSON_VALID`.
|
|
||||||
2. **`context_watch.ps1 -StareFile` pe transcript de test** (usage sintetic la 260000 tokeni,
|
|
||||||
peste pragul implicit de 250000):
|
|
||||||
- rulare 1: fisier inexistent -> creat, un singur rand:
|
|
||||||
`<!-- context_watch: 17.09.2026 21:57 | tokeni: 260000 | nivel: avertisment -->`.
|
|
||||||
- am adaugat manual un rand "text scris de model" dupa antet (simuland continutul modelului).
|
|
||||||
- rulare 2 (o secunda mai tarziu): antetul de pe primul rand a fost INLOCUIT (ora actualizata),
|
|
||||||
randul "text scris de model" a ramas neatins, nu s-a adaugat un al doilea antet
|
|
||||||
(verificat cu citire BOM-aware: 2 linii total, 1 linie cu prefixul antetului).
|
|
||||||
3. **`context_watch.ps1` fara `-StareFile`**: acelasi transcript de test -> acelasi mesaj de
|
|
||||||
avertisment pe stderr, exit code 2, identic cu comportamentul dinaintea modificarii (nimic
|
|
||||||
scris pe disc).
|
|
||||||
4. **Hook-ul `SubagentStop`**: comanda extrasa din `settings.json` dupa modificare, rulata direct:
|
|
||||||
- stdin `{"stop_hook_active":true}` -> nicio iesire, exit 0.
|
|
||||||
- stdin `{"stop_hook_active":false}` -> a scos exact `additionalContext`-ul de dinainte, exit 0.
|
|
||||||
|
|
||||||
## Ce a ramas neacoperit
|
|
||||||
|
|
||||||
- Punctul 4 din S0 al planului ("se muta in COMUN ca sa fie refolosibil de toate produsele ROA")
|
|
||||||
NU a fost facut - nu a fost in lista de 3 sarcini primite de la team lead pentru aceasta rulare.
|
|
||||||
Scriptul ramane in `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`, neschimbat ca locatie.
|
|
||||||
- Proba "ciclu real cu subagent care se termina, o singura rulare a hook-ului" (mentionata in
|
|
||||||
plan la finalul sectiunii S0) nu a fost reprodusa printr-un subagent real - probele de mai sus
|
|
||||||
au testat garda si scrierea de stare direct, in izolare, nu printr-o rulare completa
|
|
||||||
Claude Code cu un subagent viu. Nu am lansat subagenti (interdictie explicita in sarcina).
|
|
||||||
- Nu am facut niciun commit (interdictie explicita); `settings.json` nu e sub control de versiuni
|
|
||||||
in acest repo, deci nu exista diff de revizuit acolo - doar backup-ul
|
|
||||||
`C:\Users\mmari\.claude\settings.json.bak_20260917`. `context_watch.ps1` e in repo-ul `COMUN`
|
|
||||||
(git) - diff-ul ramane de revizuit acolo inainte de commit, conform regulii "fara commit fara
|
|
||||||
review".
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
\# Referinta oficiala: SessionStart / PostCompact / Stop (surse: code.claude.com/docs)
|
|
||||||
|
|
||||||
Toate citatele de mai jos sunt verbatim din versiunile `.md` brute ale paginilor
|
|
||||||
(`https://code.claude.com/docs/en/hooks.md`, `.../hooks-guide.md`), fetch-uite direct cu
|
|
||||||
`curl` pe 2026-09-17 (WebFetch trunchiaza paginile astea, sunt prea mari pentru modelul
|
|
||||||
intern al tool-ului — vezi nota metodologica de la final). Nu am dedus nimic; unde n-am gasit
|
|
||||||
un raspuns explicit, scriu "NEDOCUMENTAT".
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. SessionStart — valori de matcher acceptate
|
|
||||||
|
|
||||||
**CONFIRMAT, dar lista din brief e incompleta: sunt 5 valori, nu 4.** Lipsea `fork`.
|
|
||||||
|
|
||||||
Sursa: `https://code.claude.com/docs/en/hooks.md`, sectiunea `### SessionStart`:
|
|
||||||
|
|
||||||
> The matcher value corresponds to how the session was initiated:
|
|
||||||
>
|
|
||||||
> | Matcher | When it fires |
|
|
||||||
> | :-------- | :------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
> | `startup` | New session |
|
|
||||||
> | `resume` | `--resume`, `--continue`, or `/resume` |
|
|
||||||
> | `clear` | `/clear` |
|
|
||||||
> | `compact` | Auto or manual compaction |
|
|
||||||
> | `fork` | A new session forked from an existing one: `--fork-session` with `--resume` or `--continue`, the `/fork` background copy, or `/branch` |
|
|
||||||
>
|
|
||||||
> Before v2.1.214, forked sessions reported source `"resume"`.
|
|
||||||
|
|
||||||
Important pentru mecanismul propus: **`compact` e si el un matcher de `SessionStart`**, separat
|
|
||||||
de evenimentul `PostCompact` (vezi punctul 4). Adica dupa o compactare (auto sau manuala),
|
|
||||||
Claude Code re-porneste efectiv un `SessionStart` cu `source: "compact"`, iar acela SUPORTA
|
|
||||||
`additionalContext` — spre deosebire de `PostCompact` propriu-zis, care nu suporta (punctul 4).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. SessionStart — campuri pe stdin, cum distinge `/clear` de pornire normala
|
|
||||||
|
|
||||||
Sursa: `https://code.claude.com/docs/en/hooks.md`, `#### SessionStart input`:
|
|
||||||
|
|
||||||
> In addition to the [common input fields](#common-input-fields), SessionStart hooks receive
|
|
||||||
> `source` and optionally `model`, `agent_type`, and `session_title`:
|
|
||||||
>
|
|
||||||
> | Field | Description |
|
|
||||||
> | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
> | `source` | How the session started: `"startup"` for new sessions, `"resume"` for resumed sessions, `"clear"` after `/clear`, `"compact"` after compaction, or `"fork"` for a new session forked from an existing one |
|
|
||||||
> | `model` | The active model identifier. It can be omitted, for example after `/clear` or when a session is restored through conversation recovery, so check for the field before reading it |
|
|
||||||
> | `agent_type` | The agent name, present when you start Claude Code with `claude --agent <name>` |
|
|
||||||
> | `session_title` | The current session title if one is already set, for example via `--name` or `/rename`. A hook that emits `sessionTitle` can check `session_title` first to avoid overwriting a title the user set explicitly |
|
|
||||||
|
|
||||||
**Distinctia `/clear` vs pornire normala se face STRICT prin campul `source`**: `"clear"` vs
|
|
||||||
`"startup"`. Nu exista alt semnal necesar.
|
|
||||||
|
|
||||||
Campuri comune (din `#### Common input fields`, aceeasi pagina), relevante pentru mecanism:
|
|
||||||
`session_id`, `prompt_id`, `transcript_path`, `cwd`, `scratchpad_dir`, `permission_mode`,
|
|
||||||
`effort`, `hook_event_name`. Citat exact pentru `transcript_path`:
|
|
||||||
|
|
||||||
> `transcript_path` — Path to conversation JSON. The transcript file is written asynchronously
|
|
||||||
> and may lag the in-memory conversation, so it may not yet include the current turn's most
|
|
||||||
> recent messages when a hook fires.
|
|
||||||
|
|
||||||
Cand `source` e `"resume"` sau `"fork"` si transcript-ul are cel putin un raspuns Claude, mai
|
|
||||||
vin 4 campuri (necesare v2.1.251+): `seconds_since_last_response`, `context_tokens`,
|
|
||||||
`prompt_cache_likely_expired`, `estimated_cache_write_usd`. **Aceste campuri NU apar pentru
|
|
||||||
`source: "clear"`** — deci hook-ul nu primeste de la Claude Code o estimare gata facuta a
|
|
||||||
cate token-i "costa" reluarea; asta ramane treaba handoff-ului scris pe disc.
|
|
||||||
|
|
||||||
`agent_type` si `agent_id` (subagent-only) — prezente doar cand hook-ul ruleaza intr-un
|
|
||||||
subagent sau sesiunea foloseste `--agent`.
|
|
||||||
|
|
||||||
**Timing**: la pornire/`--resume`/`--continue`/`/clear`, hook-urile `SessionStart` ruleaza in
|
|
||||||
fundal — poti scrie imediat, dar primul raspuns al lui Claude asteapta sa termine hook-urile.
|
|
||||||
La `/resume` in interiorul unei sesiuni, switch-ul asteapta hook-urile. Citat:
|
|
||||||
|
|
||||||
> When you start an interactive session, resume a conversation at launch with `--continue` or
|
|
||||||
> `--resume`, or run `/clear`, SessionStart hooks run in the background. You can type right
|
|
||||||
> away, and a conversation you resumed appears without waiting for the hooks. Claude's first
|
|
||||||
> response still waits for the hooks to finish, so their context reaches Claude.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. SessionStart — cum returneaza context, forma exacta, limita de marime
|
|
||||||
|
|
||||||
Sursa: `https://code.claude.com/docs/en/hooks.md`, `#### SessionStart decision control` +
|
|
||||||
exemplu JSON:
|
|
||||||
|
|
||||||
> In addition to the [JSON output fields](#json-output) available to all hooks, you can return
|
|
||||||
> these event-specific fields:
|
|
||||||
>
|
|
||||||
> | Field | Description |
|
|
||||||
> | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
> | `additionalContext` | String added to Claude's context at the start of the conversation, before the first prompt. |
|
|
||||||
> | `initialUserMessage` | String used as the first user message of the session. Applies in non-interactive mode with `-p`... |
|
|
||||||
> | `sessionTitle` | Sets the session title, with the same effect as `/rename`... Applies when `source` is `"startup"`, `"resume"`, or `"fork"`; ignored on `"clear"` and `"compact"` |
|
|
||||||
> | `watchPaths` | Array of absolute paths to watch for FileChanged events during this session |
|
|
||||||
> | `reloadSkills` | Boolean. When `true`, re-scans skill/command dirs after SessionStart hooks complete... |
|
|
||||||
>
|
|
||||||
> ```json
|
|
||||||
> {
|
|
||||||
> "hookSpecificOutput": {
|
|
||||||
> "hookEventName": "SessionStart",
|
|
||||||
> "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
|
|
||||||
> "sessionTitle": "auth-refactor"
|
|
||||||
> }
|
|
||||||
> }
|
|
||||||
> ```
|
|
||||||
|
|
||||||
Confirmat: e `hookSpecificOutput` cu `hookEventName: "SessionStart"` si `additionalContext`
|
|
||||||
imbricat inauntru — exact forma presupusa in brief.
|
|
||||||
|
|
||||||
**Limita de marime (generala, se aplica la SessionStart la fel ca la toate evenimentele)**,
|
|
||||||
sursa: `https://code.claude.com/docs/en/hooks.md`, `#### Add context for Claude`:
|
|
||||||
|
|
||||||
> The `additionalContext` field passes a string from your hook into Claude's context window.
|
|
||||||
> Claude Code wraps the string in a system reminder and inserts it into the conversation at the
|
|
||||||
> point where the hook fired.
|
|
||||||
>
|
|
||||||
> [...]
|
|
||||||
>
|
|
||||||
> If a value exceeds 10,000 characters, Claude Code writes the text to a file in the session
|
|
||||||
> directory and passes Claude the file path with a short preview instead.
|
|
||||||
|
|
||||||
Deci: **10.000 de caractere** e pragul. Peste el, Claude Code NU trunchiaza si NU refuza —
|
|
||||||
scrie continutul intr-un fisier in directorul sesiunii si trimite lui Claude calea + un preview
|
|
||||||
scurt. Pentru un handoff mare, asta e de fapt convenabil: hook-ul poate trimite tot textul, iar
|
|
||||||
peste prag Claude Code il redirectioneaza singur spre fisier.
|
|
||||||
|
|
||||||
Unde ajunge textul pentru `SessionStart` specific, din acelasi paragraf:
|
|
||||||
|
|
||||||
> * [SessionStart](#sessionstart) and [SubagentStart](#subagentstart): at the start of the
|
|
||||||
> conversation, before the first prompt
|
|
||||||
|
|
||||||
Nota despre re-rulare la resume, aceeasi sectiune:
|
|
||||||
|
|
||||||
> `SessionStart` hooks run again on resume with `source` set to `"resume"`, or `"fork"` if you
|
|
||||||
> added `--fork-session`, so they can refresh their context.
|
|
||||||
|
|
||||||
(Nu mentioneaza explicit re-rularea la `/clear`, dar tabelul de matchere de la punctul 1 o
|
|
||||||
confirma separat: `clear` e chiar unul dintre matcherele native ale evenimentului.)
|
|
||||||
|
|
||||||
Exit code: stdout simplu (fara JSON) e tratat ca text simplu si ajunge in context, la fel ca
|
|
||||||
`additionalContext` — nu trebuie neaparat JSON daca hook-ul nu seteaza si alte campuri. Citat:
|
|
||||||
|
|
||||||
> Claude Code adds stdout it treats as plain text to Claude's context. [...] Since plain stdout
|
|
||||||
> already reaches Claude for this event, a hook that only loads context can print to stdout
|
|
||||||
> directly without building JSON. Use the JSON form when you need to combine context with other
|
|
||||||
> fields such as `sessionTitle`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. PostCompact — aceleasi intrebari, plus `manual` vs `auto`
|
|
||||||
|
|
||||||
Sursa: `https://code.claude.com/docs/en/hooks.md`, `### PostCompact`:
|
|
||||||
|
|
||||||
> Runs after Claude Code completes a compact operation. Use this event to react to the new
|
|
||||||
> compacted state, for example to log the generated summary or update external state. Claude
|
|
||||||
> Code discards a PostCompact hook's `systemMessage` and `continue` fields.
|
|
||||||
>
|
|
||||||
> The same matcher values apply as for `PreCompact`:
|
|
||||||
>
|
|
||||||
> | Matcher | When it fires |
|
|
||||||
> | :------- | :----------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
> | `manual` | After `/compact` |
|
|
||||||
> | `auto` | After auto-compact when the conversation reaches the auto-compact window |
|
|
||||||
>
|
|
||||||
> #### PostCompact input
|
|
||||||
>
|
|
||||||
> In addition to the common input fields, PostCompact hooks receive `trigger` and
|
|
||||||
> `compact_summary`. The `compact_summary` field contains the conversation summary generated by
|
|
||||||
> the compact operation.
|
|
||||||
>
|
|
||||||
> ```json
|
|
||||||
> {
|
|
||||||
> "session_id": "abc123",
|
|
||||||
> "transcript_path": "...",
|
|
||||||
> "cwd": "...",
|
|
||||||
> "hook_event_name": "PostCompact",
|
|
||||||
> "trigger": "manual",
|
|
||||||
> "compact_summary": "Summary of the compacted conversation..."
|
|
||||||
> }
|
|
||||||
> ```
|
|
||||||
>
|
|
||||||
> **PostCompact hooks have no decision control. They can't affect the compaction result but can
|
|
||||||
> perform follow-up tasks.**
|
|
||||||
|
|
||||||
**Constatare critica pentru mecanismul propus: `PostCompact` NU poate injecta
|
|
||||||
`additionalContext` si nu poate influenta rezultatul compactarii — e strict pentru efecte
|
|
||||||
secundare (log, notificare externa etc.).** Daca planul se baza pe "PostCompact reinjecteaza
|
|
||||||
handoff-ul", premisa e falsa. Calea care CHIAR functioneaza pentru compactare e
|
|
||||||
`SessionStart` cu matcher `compact` (punctul 1 si 3) — un eveniment diferit, care ruleaza dupa
|
|
||||||
`PostCompact` si care are `additionalContext`. Exemplul oficial de "re-inject context after
|
|
||||||
compaction" din `hooks-guide.md` confirma asta explicit:
|
|
||||||
|
|
||||||
> When Claude's context window fills up, compaction summarizes the conversation to free space.
|
|
||||||
> This can lose important details. Use a `SessionStart` hook with a `compact` matcher to
|
|
||||||
> re-inject critical context after every compaction.
|
|
||||||
|
|
||||||
Pentru `PreCompact` (nu a fost cerut explicit, dar e relevant ca sa nu confundati): poate bloca
|
|
||||||
(`decision: "block"` sau exit 2) si primeste `trigger` + `custom_instructions`, dar la fel
|
|
||||||
"discards `systemMessage` and `continue`". `PreCompact` nu e mecanismul de reinjectare — ruleaza
|
|
||||||
INAINTE de compactare.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Poate un hook sa declanseze `/clear` sau `/compact`?
|
|
||||||
|
|
||||||
**NU exista niciun mecanism. Confirmat explicit, nu doar prin absenta.**
|
|
||||||
|
|
||||||
Sursa: `https://code.claude.com/docs/en/hooks-guide.md`, linia 952:
|
|
||||||
|
|
||||||
> Command hooks communicate through stdout, stderr, and exit codes only. **They can't trigger
|
|
||||||
> `/` commands or tool calls.** Text returned via `additionalContext` is injected as a system
|
|
||||||
> reminder that Claude reads as plain text. HTTP hooks communicate through the response body
|
|
||||||
> instead.
|
|
||||||
|
|
||||||
Am cautat explicit orice camp de tip `"command"` sau mecanism de rulare a unei comenzi slash
|
|
||||||
din output-ul unui hook, in tot `hooks.md` (3840 linii) si `hooks-guide.md` (1064 linii) — nu
|
|
||||||
exista. Singurele cai care declanseaza efectiv o compactare/clear raman actiunile native ale
|
|
||||||
utilizatorului (`/clear`, `/compact`) sau host-ul/SDK-ul care porneste sesiunea.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Stop hook — poate bloca oprirea si injecta o instructiune?
|
|
||||||
|
|
||||||
**Da, in doua moduri diferite**, sursa `https://code.claude.com/docs/en/hooks.md`,
|
|
||||||
`### Stop` + `#### Stop input` + `#### Stop decision control`:
|
|
||||||
|
|
||||||
Input:
|
|
||||||
|
|
||||||
> In addition to the common input fields, Stop hooks receive `stop_hook_active`,
|
|
||||||
> `last_assistant_message`, `background_tasks`, and `session_crons`. The `stop_hook_active`
|
|
||||||
> field is `true` when Claude Code is already continuing as a result of a stop hook. Check this
|
|
||||||
> value or process the transcript to avoid blocking on a condition that will never resolve.
|
|
||||||
> Claude Code overrides the hook and ends the turn after 8 consecutive blocks.
|
|
||||||
|
|
||||||
Decision control:
|
|
||||||
|
|
||||||
> `Stop` and `SubagentStop` hooks can control whether Claude continues. In addition to the JSON
|
|
||||||
> output fields available to all hooks, your hook script can return these event-specific
|
|
||||||
> fields:
|
|
||||||
>
|
|
||||||
> | Field | Description |
|
|
||||||
> | :-------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
> | `decision` | `"block"` prevents Claude from stopping. Omit to allow Claude to stop |
|
|
||||||
> | `reason` | Required when `decision` is `"block"`. Tells Claude why it should continue |
|
|
||||||
> | `hookSpecificOutput.additionalContext` | Non-error feedback for Claude. The conversation continues so Claude can act on it, but unlike `decision: "block"` it is shown in the transcript as hook feedback rather than a hook error |
|
|
||||||
>
|
|
||||||
> A hook that blocks by exiting 2 routes the same way as `reason`: Claude receives the stderr
|
|
||||||
> message as the explanation for why it should continue.
|
|
||||||
>
|
|
||||||
> ```json
|
|
||||||
> {
|
|
||||||
> "decision": "block",
|
|
||||||
> "reason": "Must be provided when Claude is blocked from stopping"
|
|
||||||
> }
|
|
||||||
> ```
|
|
||||||
>
|
|
||||||
> Use `additionalContext` when the hook is working as designed and giving Claude guidance, such
|
|
||||||
> as "run the test suite before finishing". It keeps the conversation going through the same
|
|
||||||
> loop protections as `decision: "block"`, namely the `stop_hook_active` input and the
|
|
||||||
> 8-consecutive-continuation cap, but the transcript labels it `Stop hook feedback` and no hook
|
|
||||||
> error notification is shown.
|
|
||||||
|
|
||||||
Deci: `decision: "block"` + `reason` (top-level, NU in `hookSpecificOutput`) forteaza
|
|
||||||
continuarea si arata `reason` ca eroare de hook; `hookSpecificOutput.additionalContext` face
|
|
||||||
acelasi lucru dar apare ca "Stop hook feedback", nu ca eroare. **Ambele variante trec prin
|
|
||||||
aceleasi limite de bucla**: campul `stop_hook_active` (hook-ul trebuie sa verifice singur ca sa
|
|
||||||
nu se blocheze la infinit) si plafonul intern de **8 blocari consecutive fara progres**, dupa
|
|
||||||
care Claude Code forteaza oprirea oricum.
|
|
||||||
|
|
||||||
Exit code 2 pe `Stop`: din tabelul citat separat in `hooks.md`, poate bloca (spre deosebire de
|
|
||||||
`SessionStart`, unde exit 2 doar arata mesajul utilizatorului si continua executia).
|
|
||||||
|
|
||||||
**Aplicabil direct la ideea "Stop scrie handoff-ul acum": da, se poate implementa** —
|
|
||||||
hook-ul `Stop` verifica o conditie (context mare, sarcina neterminata etc.), daca handoff-ul nu
|
|
||||||
exista inca pe disc raspunde cu `decision: "block"` + `reason: "scrie handoff-ul pe disc inainte
|
|
||||||
de oprire"`, Claude continua turul, scrie handoff-ul, iar hook-ul (cand ruleaza din nou la
|
|
||||||
urmatoarea incercare de Stop) vede handoff-ul pe disc si lasa oprirea sa treaca. Trebuie
|
|
||||||
verificat `stop_hook_active` ca sa nu intre in bucla si respectat plafonul de 8.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## VERDICT PRACTIC
|
|
||||||
|
|
||||||
**Lantul complet "handoff pe disc -> /clear -> reinjectare automata" se poate construi, cu doua
|
|
||||||
completari fata de premisa initiala:**
|
|
||||||
|
|
||||||
1. **Partea automata, confirmata**:
|
|
||||||
- `SessionStart` cu matcher `clear` (sau, pentru compactare, matcher `compact`) fireste
|
|
||||||
dupa `/clear`/compactare, primeste `source` explicit (`"clear"` / `"compact"`) — hook-ul
|
|
||||||
poate citi handoff-ul de pe disc si il injecta prin
|
|
||||||
`hookSpecificOutput.additionalContext`, INAINTE de primul prompt al sesiunii noi. Asta
|
|
||||||
merge din prima, fara nicio interventie manuala suplimentara, pentru ambele declansatoare
|
|
||||||
(`/clear` SI compactare — nu doar `/clear`).
|
|
||||||
- Nu exista limita blocanta de marime: sub 10.000 caractere textul intra direct in context;
|
|
||||||
peste, Claude Code il scrie singur intr-un fisier si trimite calea — deci un handoff mare
|
|
||||||
nu se pierde, doar se livreaza indirect.
|
|
||||||
- `Stop` poate fi folosit ca plasa de siguranta care FORTEAZA scrierea handoff-ului inainte
|
|
||||||
de oprire (`decision: "block"` + `reason`), cu conditia sa respecte `stop_hook_active` si
|
|
||||||
plafonul de 8 blocari.
|
|
||||||
|
|
||||||
2. **Ce ramane obligatoriu manual / in afara hook-urilor**:
|
|
||||||
- **Niciun hook nu poate declansa `/clear` sau `/compact` singur** — confirmat explicit in
|
|
||||||
documentatie ("can't trigger `/` commands or tool calls"). Utilizatorul (sau orchestrator-ul,
|
|
||||||
daca ruleaza in headless/SDK cu control asupra sesiunii) trebuie sa emita el actiunea.
|
|
||||||
- **Niciun hook nu anunta "am ajuns la ~50% context"** — nu exista un eveniment de tip
|
|
||||||
"context threshold reached". Campurile `context_tokens` etc. apar DOAR pe `SessionStart` cu
|
|
||||||
`source: "resume"/"fork"`, dupa fapt — nu in timp real, in timpul sesiunii curente. Decizia
|
|
||||||
de "e timpul sa scriu handoff-ul" ramane a modelului/sesiunii, exact cum descrie deja
|
|
||||||
regula din `CLAUDE.md` — hook-urile nu o pot automatiza.
|
|
||||||
- **Premisa gresita de reparat**: daca planul se baza pe `PostCompact` pentru reinjectare,
|
|
||||||
nu functioneaza — `PostCompact` n-are `decision control` deloc. Mecanismul corect pentru
|
|
||||||
compactare e `SessionStart` cu matcher `compact`, nu `PostCompact`.
|
|
||||||
|
|
||||||
**Pe scurt**: partea "citeste handoff de pe disc si baga-l inapoi in context la (re)pornire" e
|
|
||||||
100% automata prin `SessionStart`. Partea "declanseaza tu insuti /clear" ramane 100% manuala —
|
|
||||||
nu exista ocolire documentata. `Stop` poate automatiza doar "nu te opri pana nu ai scris
|
|
||||||
handoff-ul pe disc", nu si declansarea lui `/clear` dupa aceea.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Nota metodologica
|
|
||||||
|
|
||||||
`WebFetch` (tool-ul standard) trunchiaza/rezuma paginile `hooks` si `hooks-guide` inainte sa
|
|
||||||
ajunga la sectiunile per-eveniment (confirmat empiric: trei incercari diferite de prompt au
|
|
||||||
esuat sa extraga `### SessionStart`, `### Stop`, `### PostCompact` din pagina `hooks`, desi
|
|
||||||
tabelele de sus ale paginii ies corect). Documentatia Mintlify expune si varianta bruta:
|
|
||||||
`https://code.claude.com/docs/en/hooks.md` si `.../hooks-guide.md` (mentionate chiar de pagina:
|
|
||||||
"Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt"). Am fetch-uit
|
|
||||||
acele `.md`-uri direct prin `curl` (citire read-only, fara nicio scriere in afara acestui
|
|
||||||
livrabil) si am citat din ele. Toate citatele de mai sus sunt verbatim din acele fisiere.
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
# Test headless — `citeste_curs_bnr` (arhiva 10 zile + fisier anual BNR)
|
|
||||||
|
|
||||||
Tinta: `COMUN\clase\onom_curs.vcx`, clasa `actualizare_curs_bnr`, metoda `citeste_curs_bnr`
|
|
||||||
(patch v3, deja scris in binar — confirmat inainte de test, vezi sectiunea 0). Metoda a fost
|
|
||||||
apelata **DIRECT**, niciodata prin `initializeaza` — niciun apel la `initializeaza()` sau
|
|
||||||
`scrie_curs_bnr()` in tot scriptul, deci **nicio scriere in Oracle**. Conexiunea Oracle deschisa de
|
|
||||||
mediul de test (`test_init_env_auto_roafacturare.prg`) face doar `SELECT`-uri (firma, calendar,
|
|
||||||
`pack_sesiune.set*`) pentru initializarea sesiunii — nimic legat de cursuri BNR.
|
|
||||||
|
|
||||||
## 0. Confirmare inainte de test
|
|
||||||
|
|
||||||
`COMUN\clase\onom_curs.vc2` are deja proprietatile `clinkan`/`clinkarhiva` si noua
|
|
||||||
`citeste_curs_bnr` cu parametrul `tnSursa` (linia 106+), identice cu blocul `NOU` din
|
|
||||||
`docs\patch_curs_bnr_arhiva.md` — verificat cu grep inainte de a scrie testul. `.vcx`/`.VCT` au
|
|
||||||
acelasi mtime cu `.vc2` (22:11, 17.09.2026) — write-back-ul din `docs\writeback_curs_bnr.md`
|
|
||||||
confirmat aplicat, nu s-a atins din nou.
|
|
||||||
|
|
||||||
## 1. Artefact de harness gasit si ocolit (NU e defect de cod)
|
|
||||||
|
|
||||||
Apeland `citeste_curs_bnr` direct (fara `initializeaza`), primul apel a agatat testul (90s
|
|
||||||
timeout, 0 dialoguri detectate de `watchdog_vfp.ps1`). Cauza, confirmata prin proba minimala
|
|
||||||
(`test_curs_bnr_probe2.prg`, sters dupa confirmare):
|
|
||||||
|
|
||||||
- `citeste_curs_bnr` incepe cu `creeaza_backup_cursoare(This.cCursor)` (`onom_curs.vc2:118`), care
|
|
||||||
cheama `copiaza_structura_cursor()` din `COMUN\programe\oproceduri_comune.prg:3024`.
|
|
||||||
- Acolo, `If Used(tcSursa)` (`oproceduri_comune.prg:3027`) — daca `ccrsbnr` nu e deja deschis (cazul
|
|
||||||
cand chemi metoda direct, fara sa treci prin `initializeaza`, care il creeaza via
|
|
||||||
`goExecutor.oExecute`), cade pe `Else` si cheama
|
|
||||||
`amessagebox("Eroare interna 2 - copiaza structura cursor",...)` (`oproceduri_comune.prg:3036`).
|
|
||||||
- `amessagebox` (functia REALA, `oproceduri_comune.prg:1205`) e apelata **din acelasi fisier** ca
|
|
||||||
`copiaza_structura_cursor` — `mock_amessagebox.prg` documenteaza explicit aceasta limita
|
|
||||||
("LIMITA 1: nu acopera sigur apelurile INTRA-FISIER") — deci mock-ul nu intercepteaza, se
|
|
||||||
deschide `messagebox_form` real (`MessageBox.vcx`), modal, headless agata.
|
|
||||||
- In productie nu se intampla: `initializeaza()` creeaza `This.cCursor` cu
|
|
||||||
`goExecutor.oExecute(lcSql, This.cCursor)` (`onom_curs.vc2:159`) INAINTE de a chema
|
|
||||||
`citeste_curs_bnr`.
|
|
||||||
|
|
||||||
**Reparatia e in test, nu in cod**: inainte de FIECARE apel, testul precreaza `ccrsbnr` gol
|
|
||||||
(`CREATE CURSOR ccrsbnr (Currency C(10), multiplier N(10,4), Curs N(14,6))` — campurile confirmate
|
|
||||||
din `scrie_curs_bnr`, `onom_curs.vc2:282-283`, care citeste exact `Currency`/`multiplier`/`Curs`
|
|
||||||
din cursor). Metoda oricum inchide cursorul (`Use In`) si il reconstruieste din raspunsul HTTP
|
|
||||||
(`Xmltocursor`), deci structura precreata conteaza doar pentru backup, nu pentru rezultat.
|
|
||||||
Confirmat cu proba minimala: dupa precreare, Return=0, Reccount=37, EUR curs=5.2637 — fara
|
|
||||||
agatare. Niciun fisier de productie nu a fost atins pentru asta.
|
|
||||||
|
|
||||||
## 2. Rezultate — 7 cazuri cerute + 1 caz suplimentar (cascada)
|
|
||||||
|
|
||||||
| # | Caz | Parametri | Return asteptat | Return obtinut | Durata | Rezultat |
|
|
||||||
|---|---|---|---|---|---|---|
|
|
||||||
| 1 | sursa 1, azi (zi lucratoare) | `dData=17.09.2026` (joi) | 0 | 0 | 0.238s | **PASS** — Reccount=37, EUR curs=5.2637 |
|
|
||||||
| 2 | sursa 1, luni recenta | `dData=14.09.2026` (luni) | 0 | 0 | 0.048s | **PASS** — cade pe publicarea de vineri, Reccount=37, EUR curs=5.2557 |
|
|
||||||
| 3 | sursa 2, martie an curent | `dData=15.03.2026` | 0 | 0 | 0.084s | **PASS** — Reccount=37, EUR curs=5.0947 |
|
|
||||||
| 4 | sursa 2, 2 ianuarie an curent | `dData=02.01.2026` | 0 | 0 | 0.202s | **PASS** — trecere in 2025, Reccount=38, EUR curs=5.0985 |
|
|
||||||
| 5 | sursa 1, data mult mai veche decat arhiva | `dData=01.01.2026` | 2 | 2 | 0.047s | **PASS** — "data lipsa" distinct de "BNR picat" |
|
|
||||||
| 6 | sursa 1, host invalid (indisponibilitate) | `cLinkArhiva`=host `.invalid` inexistent, `dData`=azi | 1 | **2** | 0.181s | **FAIL** — vezi sectiunea 3, defect real in cod |
|
|
||||||
| 7 | fara parametru (compatibilitate inapoi) | `dData=18.08.2026` | 0 (comportament sursa 0) | 0 | 0.041s | **PASS** — `This.dData` s-a suprascris la 18.09.2026 (publicare+1), ca inainte |
|
|
||||||
| 8 (suplimentar, cerut de coordonator) | cascada reala: acelasi obiect/`dData` din caz 5 (`Return 2` pe sursa 1), apoi sursa 2 | `dData=01.01.2026` | 0 | 0 | 0.120s | **PASS** — Reccount=38, EUR curs=5.0985, FARA "Eroare interna 2" la al doilea apel |
|
|
||||||
|
|
||||||
**Sumar: 7 PASS, 1 FAIL** (cazul 6).
|
|
||||||
|
|
||||||
## 3. Cazul 5 — eroare tranzitorie auto-vindecata, nu afecteaza Return
|
|
||||||
|
|
||||||
La finalul cazului 5 (Return 2, data prea veche), logul a prins:
|
|
||||||
```
|
|
||||||
[ON ERROR] 13 Alias 'CCRSBNR' is not found. in REPUNE_BACKUP_CURSOARE linia 6363
|
|
||||||
```
|
|
||||||
Cauza: `citeste_curs_bnr` inchide `This.cCursor` (`Use In`) inainte de apelul HTTP; pe ramura
|
|
||||||
`Return 2` cheama `repune_backup_cursoare(This.cCursor)` (`onom_curs.vc2:175`), a carei prima
|
|
||||||
linie utila e `Select (tcOriginal)` (`oproceduri_comune.prg:6363`) pe un alias deja inchis —
|
|
||||||
**risc preexistent, deja documentat** in `docs\patch_curs_bnr_arhiva.md` sectiunea 5, punctul 3
|
|
||||||
("`repune_backup_cursoare` pe cursor deja inchis"), nu introdus de acest patch. Confirmat acum ca
|
|
||||||
se reproduce efectiv. Efect: eroarea e prinsa de `ON ERROR`, executia continua la linia
|
|
||||||
urmatoare (`lnRecno = Recno()`), iar liniile de dupa (`Select * From bckpccrsbnr Into Cursor
|
|
||||||
ccrsbnr`) reconstruiesc singure alias-ul `ccrsbnr` — **auto-vindecare in aceeasi rulare**, `Return
|
|
||||||
2` final e corect si cazul 8 (cascada imediat urmatoare, sursa 2 pe acelasi obiect) confirma ca
|
|
||||||
`ccrsbnr` ramane intr-o stare corecta (Used, fara sa mai loveasca "Eroare interna 2" la
|
|
||||||
`creeaza_backup_cursoare` din apelul urmator). Nu s-a reparat codul — doar raportat, cum s-a cerut.
|
|
||||||
|
|
||||||
## 4. Cazul 6 — defect REAL, nu artefact de test
|
|
||||||
|
|
||||||
**Return obtinut 2, asteptat 1.** Nu e problema harnessului — e un gol real in
|
|
||||||
`citeste_curs_bnr` pentru cazul specific "host complet nerezolvabil" (nu doar HTTP non-200 sau
|
|
||||||
timeout pe conexiune).
|
|
||||||
|
|
||||||
Secventa exacta din log (prima eroare e cea reala, restul e cascada, exact ca in nota de procedura
|
|
||||||
despre `ON ERROR`):
|
|
||||||
|
|
||||||
```
|
|
||||||
[ON ERROR] 1429 OLE IDispatch exception ... WinHttp.WinHttpRequest: The server name or address
|
|
||||||
could not be resolved. in ACTUALIZARE_CURS_BNR.CITESTE_CURS_BNR (linia loHTTP.Send(), onom_curs.vc2:137)
|
|
||||||
[ON ERROR] 1429 ... The data necessary to complete this operation is not yet available. (citirea
|
|
||||||
ulterioara a lui loHTTP.Status, onom_curs.vc2:139)
|
|
||||||
... (cascada: 8x eroare 11 "Function argument value, type, or count is invalid" in bucla
|
|
||||||
Do While de la onom_curs.vc2:151-173, cu lcFisier ramas needefinit)
|
|
||||||
[ON ERROR] 13 Alias 'CCRSBNR' is not found. in REPUNE_BACKUP_CURSOARE linia 6363
|
|
||||||
```
|
|
||||||
|
|
||||||
Explicatie: `loHTTP.Send()` (`onom_curs.vc2:137`) NU e in `TRY/CATCH` si NU verifica intai daca
|
|
||||||
request-ul a esuat la nivel de retea/DNS — arunca direct o exceptie OLE cand hostul nu se rezolva.
|
|
||||||
Fara `TRY/CATCH` in metoda, `ON ERROR` global preia eroarea si, conform semanticii VFP (executia
|
|
||||||
continua la linia URMATOARE, nu la ramura `Else` corecta), programul "sare" peste blocul
|
|
||||||
`If loHTTP.Status = 200 ... Else ... Return 1 ... Endif` fara sa execute nici ramura de succes, nici
|
|
||||||
`Return 1` — continua direct in bucla de cautare `Cube` cu `lcFisier` gol/nedefinit, epuizeaza cele
|
|
||||||
10 incercari (cascada de erori 11), si iese pe ramura "nimic gasit" -> `Return 2`. Rezultatul final
|
|
||||||
e gresit: un host complet indisponibil (DNS esuat) e raportat ca "data lipsa" (2), nu ca
|
|
||||||
"indisponibilitate" (1) — exact confuzia pe care patch-ul trebuia sa o elimine, dar doar pentru
|
|
||||||
raspunsuri HTTP non-200 de la un server care RASPUNDE, nu pentru esecul de rezolvare DNS.
|
|
||||||
|
|
||||||
**Durata: 0.181s** — nu agata deloc (departe de cele 5s/5s/5s/15s din `SetTimeouts`); esecul de
|
|
||||||
rezolvare DNS e instant, nu trece prin timeout-uri.
|
|
||||||
|
|
||||||
Conform instructiunii explicite: **nu am reparat codul**. Raportez cu dovada de mai sus.
|
|
||||||
|
|
||||||
## 5. Cum s-a rulat
|
|
||||||
|
|
||||||
Script: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg` (helper-e proprii:
|
|
||||||
`crea_cursor_gol`, `log_txt`, `log_eroare`, `verifica_caz` — toate in acelasi fisier).
|
|
||||||
|
|
||||||
Precompilare izolata:
|
|
||||||
```
|
|
||||||
powershell -ExecutionPolicy Bypass -File D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1 -Prg D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg
|
|
||||||
```
|
|
||||||
|
|
||||||
Rulare (proces separat, timeout 240s, in PowerShell):
|
|
||||||
```powershell
|
|
||||||
$p = Start-Process -FilePath 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' `
|
|
||||||
-ArgumentList '-A','-T','"D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg"' -PassThru
|
|
||||||
if (-not $p.WaitForExit(240000)) { $p.Kill() }
|
|
||||||
```
|
|
||||||
|
|
||||||
Log complet (marca unica de rulare in prima linie, `SYS(2015)`):
|
|
||||||
`D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_test_curs_bnr_out.txt` — ultima rulare confirmata: mtime
|
|
||||||
17.09.2026 22:26, marca `_7KA1C340N`.
|
|
||||||
|
|
||||||
Retea: toate cele 8 sub-cazuri au ajuns efectiv la `curs.bnr.ro` (sau la hostul invalid pentru
|
|
||||||
cazul 6) — reteaua a functionat, niciun rezultat nu e raportat fals din lipsa de conectivitate.
|
|
||||||
|
|
||||||
## 6. Confirmare: nicio scriere in Oracle
|
|
||||||
|
|
||||||
Scriptul apeleaza EXCLUSIV `loCurs.citeste_curs_bnr(...)` pe obiecte `actualizare_curs_bnr`
|
|
||||||
instantiate direct — niciun apel la `initializeaza()` (care ar citi din Oracle si, la
|
|
||||||
`Reccount=0`, ar declansa cascada de UI) si niciun apel la `scrie_curs_bnr()` (singura metoda a
|
|
||||||
clasei care scrie in `pack_curs.scrie_cotatii_curs`). Singurele interactiuni cu Oracle din intreaga
|
|
||||||
rulare sunt cele din `test_init_env_auto_roafacturare.prg` (conectare + `SELECT`-uri de sesiune),
|
|
||||||
neschimbate fata de orice alt test headless din acest proiect.
|
|
||||||
|
|
||||||
## 7. Fisiere temporare de test, sterse dupa confirmare
|
|
||||||
|
|
||||||
`test_curs_bnr_probe.prg` / `.fxp` si `test_curs_bnr_probe2.prg` / `.fxp` (folosite doar pentru
|
|
||||||
diagnostic, sterse din `COMUN\utile\Teste\` dupa ce artefactul de harness a fost confirmat si
|
|
||||||
reparat in scriptul final). Raman pe disc doar `test_curs_bnr_headless.prg` / `.fxp` si logul
|
|
||||||
`_test_curs_bnr_out.txt`.
|
|
||||||
|
|
||||||
## 8. Rerulare dupa reparatia `Try/Catch` (v4)
|
|
||||||
|
|
||||||
Data/ora: 17.09.2026 22:36. Reparatia din `docs\patch_curs_bnr_arhiva.md` sectiunea 7 (`Try/Catch`
|
|
||||||
in jurul ambelor `loHTTP.Send()` din `citeste_curs_bnr`) e scrisa in binar (write-back confirmat,
|
|
||||||
fidelity-check pe round-trip identic). `.FXP` vechi al testului sters, precompilat din nou, o
|
|
||||||
singura rulare vfp9 (`-A -T`), log nou confirmat pe mtime si marca (`_7KA1CGZRZ`, 22:36:54).
|
|
||||||
|
|
||||||
| # | Caz | Return inainte (sectiunea 2) | Return dupa | Rezultat |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 1-4, 7, 8 | neschimbate | 0 | 0 | PASS (neschimbat) |
|
|
||||||
| 5 | data mult mai veche decat arhiva | 2 | 2 | PASS (neschimbat) |
|
|
||||||
| 6 | host invalid (indisponibilitate) | **2 (FAIL, asteptat 1)** | **1** | **PASS** |
|
|
||||||
|
|
||||||
**Sumar nou: 8 PASS, 0 FAIL** (fata de 7 PASS, 1 FAIL inainte de reparatie). Nicio regresie pe
|
|
||||||
cazurile 1-5, 7, 8. Cazul 6 intoarce acum `1` ("BNR picat") in loc de `2` ("data lipsa"), deci
|
|
||||||
`initializeaza` ofera acum linkul `https://www.cursbnr.ro/` in loc de mesajul gresit "Nu exista
|
|
||||||
cursul BNR pentru data specificata!".
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
# VM 304 — ce este si cum se acceseaza
|
|
||||||
|
|
||||||
## Ce este
|
|
||||||
|
|
||||||
VM 304 = masina virtuala Proxmox cu ID **304**, nume **Win11-Marius**, pe nodul **pvemini**
|
|
||||||
din clusterul Proxmox ROA. Nu e in HA (`noha`).
|
|
||||||
|
|
||||||
Surse:
|
|
||||||
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\docs\oprire-planificata-cluster.md:293`
|
|
||||||
`| 2 | pvemini | VM 304 Win11-Marius | Nu | Shutdown |`
|
|
||||||
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\docs\oprire-planificata-cluster.md:479`
|
|
||||||
`Guest-uri in afara HA: CT 102, CT 301, VM 302, VM 303, **VM 304**, VM 310.`
|
|
||||||
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\scripts\cluster-startup.sh:53` `304 # Win11-Marius`
|
|
||||||
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\scripts\cluster-shutdown.sh:41`
|
|
||||||
`304 # Win11-Marius — desktop, fara dependente`
|
|
||||||
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\scripts\.cluster-state.txt:13` `vm pvemini 304 running noha`
|
|
||||||
|
|
||||||
E clona lui **VM 303 (Win11-Adina)**:
|
|
||||||
- `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:251`
|
|
||||||
`| docs/chei-publice/vm304.pub | ... | angajat, romfast@VM-304 (clona lui 303) |`
|
|
||||||
- `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:256-260` — clona a pornit cu aceeasi
|
|
||||||
cheie privata ca VM 303; a fost regenerata separat (`ssh-keygen -t ed25519 -C "romfast@VM-304"`),
|
|
||||||
cu stergerea keypair-ului mostenit din Bitvise User keypair manager si regenerarea cheilor de
|
|
||||||
host `ssh_host_*`, altfel clona continua sa se autentifice cu identitatea VM 303 in loguri.
|
|
||||||
|
|
||||||
Nu am gasit un nume de retea/hostname DNS sau un IP direct alocat lui VM 304 in documentatia
|
|
||||||
cautata (nu e in tabelul de IP-uri interne `10.0.20.x` de la
|
|
||||||
`E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:388-394`, care listeaza doar nodurile
|
|
||||||
Proxmox). Accesul documentat trece prin host-ul Proxmox, nu prin IP propriu al VM-ului (vezi mai jos).
|
|
||||||
|
|
||||||
## Cum se acceseaza
|
|
||||||
|
|
||||||
**Nu prin RDP** — nu am gasit nicio mentiune de RDP catre VM 304 in fisierele cautate. Accesul
|
|
||||||
documentat e **headless, prin QEMU guest agent**, de pe LXC 171 (sau orice masina cu acces SSH la
|
|
||||||
host-ul Proxmox), catre host-ul nodului `pvemini` la `root@10.0.20.201`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# ping guest agent, ca sa confirmi ca VM-ul raspunde:
|
|
||||||
ssh root@10.0.20.201 "qm agent 304 ping"
|
|
||||||
|
|
||||||
# executie de comanda Windows in interiorul VM 304:
|
|
||||||
ssh root@10.0.20.201 "qm guest exec 304 -- <comanda Windows>"
|
|
||||||
```
|
|
||||||
|
|
||||||
Sursa: `E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:14-19` (documentat generic
|
|
||||||
pentru `<vmid>`, cu exemplul explicit "304 = Win11-Marius" la linia 14).
|
|
||||||
|
|
||||||
Acelasi tipar (guest agent prin `qm agent`/`qm guest exec` de pe host `root@10.0.20.201`) e
|
|
||||||
folosit si pentru celelalte VM-uri Windows non-HA ale clusterului — vezi si
|
|
||||||
`E:\proiecte\ROMFASTSQL\proxmox\cluster\docs\oprire-planificata-cluster.md:15` (guest agent
|
|
||||||
necesar pentru shutdown controlat).
|
|
||||||
|
|
||||||
## Cale de retea catre discul ei
|
|
||||||
|
|
||||||
Nu am gasit o cale UNC (`\\server\share`) documentata catre VM 304. Ce e documentat e o cale
|
|
||||||
**locala, in interiorul VM-ului** — `D:\roa\BITVISE\<client>.tlp` (profilele Bitvise) si
|
|
||||||
`D:\roa\<produs>\...` (working copy ROA, folosita ca sursa pentru scripturi SQL) — accesibila doar
|
|
||||||
prin comenzi rulate cu `qm guest exec`, nu prin share de retea:
|
|
||||||
|
|
||||||
- `E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:17`
|
|
||||||
`# Profilul Bitvise al clientului e in D:\roa\BITVISE\<client>.tlp pe VM.`
|
|
||||||
- `E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:62`
|
|
||||||
`Sursa: D:\roa\<produs>\COMUN\Drepturi utilizatori\drepturi_utilizatori.sql din orice working copy ROA`
|
|
||||||
- `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:371-372`
|
|
||||||
`astea sunt fisierele care pleaca pe VM 303 / VM 304 (pe VM 304 stau in D:\roa\BITVISE\)`
|
|
||||||
|
|
||||||
## Ce are instalat (documentat)
|
|
||||||
|
|
||||||
- **Bitvise SSH Client**, cale `C:\Program Files (x86)\Bitvise SSH Client\sexec.exe`
|
|
||||||
(`E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:19`), cu profile de clienti in
|
|
||||||
`D:\roa\BITVISE\*.tlp` si cheia globala `C:/Users/romfast/.ssh/id_ed25519`.
|
|
||||||
- **O copie/working copy ROA** sub `D:\roa\<produs>\...` (produsul e generic in text, nu apare
|
|
||||||
explicit "ROAFACTURARE" — vezi `drepturi-utilizatori-roa-firme.md:62`).
|
|
||||||
- Nu am gasit mentiune explicita despre VFP 9 sau Oracle client instalate pe VM 304 in fisierele
|
|
||||||
cautate (spre deosebire de VM 302, documentat separat ca `oracle-test`,
|
|
||||||
`E:\proiecte\ROMFASTSQL\proxmox\vm302-oracle-test\`).
|
|
||||||
|
|
||||||
## Credentiale — unde sunt documentate (nu le-am copiat)
|
|
||||||
|
|
||||||
- Cheia publica SSH pentru angajat pe VM 304: `E:\proiecte\ROMFASTSQL\docs\chei-publice\vm304.pub`,
|
|
||||||
amprenta SHA-256 listata la `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:251`.
|
|
||||||
- Cheia globala folosita de `sexec.exe` in `qm guest exec`:
|
|
||||||
`C:/Users/romfast/.ssh/id_ed25519` (in interiorul VM 304 — vezi
|
|
||||||
`drepturi-utilizatori-roa-firme.md:19`).
|
|
||||||
- Lista completa a serverelor pe care sunt copiate cheile: `scripts/bitvise-chei.ps1`, variabila
|
|
||||||
`$Servere` (`E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:264`).
|
|
||||||
- Nu am gasit parola sau alta credentiala pentru autentificare directa (RDP/consola) pe VM 304 in
|
|
||||||
fisierele cautate.
|
|
||||||
|
|
||||||
## Unde am cautat
|
|
||||||
|
|
||||||
1. `D:\ROA\ROAFACTURARE\COMUN\docs\` (grep `304`, `vm304`, `masina virtuala`, `server de test`,
|
|
||||||
`infrastructur`) — potriviri gasite pentru `304` erau toate numere de linie in cod PL/SQL
|
|
||||||
(`PACK_UTILS` linia 304, `PLS-00304`) sau text irelevant, nicio mentiune de VM 304.
|
|
||||||
2. `D:\ROA\COMUNROA\` — grep pe `304` a expirat (timeout la 20s pe arborele intreg, prea mare);
|
|
||||||
nu am reluat cautarea pentru ca raspunsul complet a fost deja gasit la pasul 3, conform
|
|
||||||
instructiunii de oprire la primul raspuns gasit.
|
|
||||||
3. `E:\proiecte\ROMFASTSQL\` — gasit direct: `docs/chei-publice/vm304.pub`,
|
|
||||||
`docs/acces-ssh-chei-angajati.md`, `docs/drepturi-utilizatori-roa-firme.md`,
|
|
||||||
`proxmox/cluster/docs/oprire-planificata-cluster.md`,
|
|
||||||
`proxmox/cluster/scripts/cluster-{startup,shutdown}.{sh,ps1}`,
|
|
||||||
`proxmox/cluster/scripts/.cluster-state.txt`.
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
# Write-back patch curs BNR arhiva (v3) — onom_curs.vc2
|
|
||||||
|
|
||||||
Tinta: `COMUN\clase\onom_curs.vc2`, clasa `actualizare_curs_bnr`. Sursa patch-ului:
|
|
||||||
`docs\patch_curs_bnr_arhiva.md` (v3) si `docs\patch_curs_bnr_arhiva.diff`.
|
|
||||||
|
|
||||||
## 0. Stare de pornire
|
|
||||||
|
|
||||||
- `git status --short clase/onom_curs.vc2` (COMUN) — gol, fara modificari necomise.
|
|
||||||
- `svn status clase/onom_curs.vc2` — `I` (ignorat de SVN, fisier text generat).
|
|
||||||
- Backup inainte de orice modificare, in scratchpad: `onom_curs.vc2.bak`, `onom_curs.vcx.bak`,
|
|
||||||
`onom_curs.VCT.bak`.
|
|
||||||
|
|
||||||
## 1. Metoda de editare
|
|
||||||
|
|
||||||
Editare byte-safe cu script Python (mod binar `rb`/`wb`), fara `Edit`/`sed -i`. Continutul nou
|
|
||||||
(ASCII pur) a fost extras direct din `docs\patch_curs_bnr_arhiva.md` (blocurile `NOU` pentru
|
|
||||||
`citeste_curs_bnr`, indentare identica cu fisierul tinta — offset 0) si reconstruit manual pentru
|
|
||||||
blocul `initializeaza` (indentarea reala din fisier e cu 1 tab mai adanca decat in markdown: linia
|
|
||||||
`Case This.dData < ...` are 7 tab-uri in fisier, nu 6 ca in document — s-a aplicat +1 tab pe toate
|
|
||||||
liniile blocului nou, pastrand structura interna neschimbata).
|
|
||||||
|
|
||||||
## 2. Linii schimbate efectiv (dupa write-back, confirmate cu `git diff`)
|
|
||||||
|
|
||||||
Trei zone, toate in `actualizare_curs_bnr`:
|
|
||||||
|
|
||||||
1. **Proprietati noi** `clinkan` / `clinkarhiva` — inserate in ordine alfabetica in
|
|
||||||
`*<DefinedPropArrayMethod>` (dupa `*p: clink`), in `*<PropValue>` (dupa `clink = ...`) si in
|
|
||||||
`_memberdata` (dupa `<memberdata name="clink".../>`), exact ca in patch.
|
|
||||||
2. **`PROCEDURE citeste_curs_bnr`** (era 37 linii, acum 92) — parametru `tnSursa`, `Do Case` pentru
|
|
||||||
alegerea link-ului (`cLink`/`cLinkArhiva`/`cLinkAn`), cautare inapoi zi cu zi (max 10 incercari),
|
|
||||||
reincercare pe anul precedent pentru `tnSursa=2`, coduri de retur `0`/`1`/`2`.
|
|
||||||
3. **`PROCEDURE initializeaza`**, ramura `nTip=1` de dupa ora 13 (era 4 linii, acum 15) — cascada
|
|
||||||
`citeste_curs_bnr(1)` -> `citeste_curs_bnr(2)` -> mesaje, link nou `https://www.cursbnr.ro/` in
|
|
||||||
loc de `http://www.bnr.ro` doar pe aceasta ramura.
|
|
||||||
|
|
||||||
`git diff --stat` (COMUN): `1 file changed, 79 insertions(+), 7 deletions(-)`. `git diff` integral
|
|
||||||
verificat — apar EXCLUSIV liniile din patch, nimic in alta zona a fisierului (fara diacritice
|
|
||||||
schimbate, fara CRLF/reindentari colaterale).
|
|
||||||
|
|
||||||
## 3. Cens octeti >= 0x80 (diacritice)
|
|
||||||
|
|
||||||
| | inainte | dupa |
|
|
||||||
|---|---|---|
|
|
||||||
| octeti >= 0x80 (total) | 0 | 0 |
|
|
||||||
| CRLF | 1718 | 1790 |
|
|
||||||
| LF izolate (lf - crlf) | 0 | 0 |
|
|
||||||
|
|
||||||
Secventa de octeti inalti e identica (goala in ambele versiuni — fisierul nu contine diacritice in
|
|
||||||
zonele atinse sau in restul fisierului). Zero LF izolate inainte si dupa — toate liniile noi au
|
|
||||||
fost inserate cu `\r\n`, consistent cu restul fisierului. Cresterea de 1790-1718=72 linii CRLF
|
|
||||||
corespunde exact insertiilor nete (2+2+2 proprietati + 11 linii nete in `initializeaza` + 55 linii
|
|
||||||
nete in `citeste_curs_bnr`).
|
|
||||||
|
|
||||||
## 4. Write-back text -> binar
|
|
||||||
|
|
||||||
Comanda:
|
|
||||||
```
|
|
||||||
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\txt2vcx.ps1 -TextFile D:\ROA\ROAFACTURARE\COMUN\clase\onom_curs.vc2 -ProjectRoot D:\ROA\ROAFACTURARE -AllowComun
|
|
||||||
```
|
|
||||||
Iesire:
|
|
||||||
```
|
|
||||||
O,P1,E0,S1,X0,c:\users\mmari\appdata\local\temp\txt2bin_roafacturare\16352-20260917221154630\comun\clase\onom_curs\verify\onom_curs.vc2
|
|
||||||
|
|
||||||
--- Sumar txt2vcx ---
|
|
||||||
OK D:\ROA\ROAFACTURARE\COMUN\clase\onom_curs.vc2
|
|
||||||
|
|
||||||
Toate cele 1 fisier(e) au fost scrise cu succes.
|
|
||||||
```
|
|
||||||
|
|
||||||
`onom_curs.vcx` si `onom_curs.VCT` au mtime nou (22:11:55, imediat inaintea `.vc2` la 22:11:56 —
|
|
||||||
scriere in aceeasi rulare).
|
|
||||||
|
|
||||||
## 5. Fidelity check pe binar (independent de mtime)
|
|
||||||
|
|
||||||
Reconvertit binarul proaspat scris (`onom_curs.vcx`) intr-un cache TEMPORAR separat, cu
|
|
||||||
`vcx2txt.ps1 -Source ... -CacheRoot <scratchpad>\fidelity_check -Force`, si comparat textul
|
|
||||||
regenerat cu textul sursa:
|
|
||||||
|
|
||||||
```
|
|
||||||
cmp fidelity_check\COMUN\clase\onom_curs.vc2 COMUN\clase\onom_curs.vc2
|
|
||||||
-> FIDELITY OK: byte-identical
|
|
||||||
```
|
|
||||||
|
|
||||||
Rezultat: **identic byte cu byte**. Round-trip confirmat.
|
|
||||||
|
|
||||||
## 6. Stare finala
|
|
||||||
|
|
||||||
`git status --short` in COMUN:
|
|
||||||
```
|
|
||||||
M clase/onom_curs.vc2
|
|
||||||
```
|
|
||||||
Doar `.vc2` e urmarit de git (binarele `.vcx`/`.VCT` nu sunt versionate); niciun alt fisier atins.
|
|
||||||
**Niciun commit, niciun `svn commit`, niciun rebuild de EXE.** Modificarea ramane necomisa, in
|
|
||||||
asteptarea aprobarii/probei manuale a lui Marius (pasii de proba sunt in
|
|
||||||
`docs\patch_curs_bnr_arhiva.md`, sectiunea 6).
|
|
||||||
Reference in New Issue
Block a user