Files
Marius Mutu e257fa5d5f feat(telegram): bot bonuri fiscale — OCR → preview → Oracle write
- US-001: mută queue_client.py în data_entry/services/ocr/
- US-002/003/004: oracle_receipt_writer + oracle_server_id în DB
- US-005: receipt_handlers.py (PDF/photo/callback flow)
- US-006: wire handlers în main.py, per-schema connect, seq_cod.nextval
- US-007: .gitignore secrets/*.oracle_pass
- US-008/009/010: teste unit + integration + E2E
- setup-secrets.sh helper + template
- docs/telegram/README.md actualizat cu arhitectura nouă

Testat E2E pe DB live (MARIUSM_AUTO). COD din seq_cod.nextval.
pypdfium2 fallback pentru PDF decode (fără poppler).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 09:26:58 +00:00

187 lines
9.6 KiB
Plaintext

# Ralph Progress Log
Started: 2026-05-05 23:01
Project: roa2web-telegram-bonuri
---
## Iterație: 2026-05-05 23:15
### Story implementat: US-003 - Adaugă câmpul write_user în OracleServerConfig (tags: backend)
### Status: Complete
### Gates rulate:
- Pytest: PASS (5/5 tests)
- /review (backend): PASS — 2 minor auto-fixes applied
- Clarified misleading comment on write_user (fallback is caller's responsibility, not OracleServerConfig's)
- Renamed duplicate test to better express its purpose (explicit None vs omitted)
### Fișiere atinse:
- backend/config.py — write_user: Optional[str] = None adăugat la OracleServerConfig
- tests/backend/test_oracle_server_config.py — 5 unit tests noi (fișier nou)
### Learnings:
- write_user field este groundwork pentru US-005 (receipt_handlers.py va folosi get_write_user)
- Specialist review a identificat: oracle_receipt_writer.py (US-002, untracked) are probleme de connection pooling și float precision pentru money — de adresat în US-002 retry
### Next:
- US-004: Migrare DB oracle_server_id în telegram_users (independent, priority 40)
- US-007: Verifică .gitignore pentru secrets/*.oracle_pass (independent, priority 70)
---
## Rate limit la iter 8 — sleep 1800
## Iterație: 2026-05-06
### Story implementat: US-007 - Verifică .gitignore pentru secrets/*.oracle_pass (tags: infra)
### Status: Complete
### Gates rulate:
- Smoke test (infra): PASS — toate 3 AC verificate manual
- AC1: .gitignore line 436 `secrets/` este pattern echivalent pentru `secrets/*.oracle_pass`
- AC2: `git check-ignore secrets/test_write.oracle_pass` → exit 0
- AC3: `git ls-files secrets/` → 0 fișiere .oracle_pass tracked
- /review: N/A — nicio modificare de cod necesară (gitignore deja corect)
### Fișiere atinse:
- Niciun fișier modificat — .gitignore era deja corect cu `secrets/` pe linia 436
### Learnings:
- .gitignore deja excludea întregul director `secrets/` (linia 436), deci `*.oracle_pass` era acoperit
- Pattern `secrets/` este mai broad decât `secrets/*.oracle_pass` — acoperă tot directorul
### Next:
- US-002: Extrage oracle_receipt_writer.py (failed, max_retries — needs manual intervention)
- US-004: Migrare DB oracle_server_id în telegram_users (failed, max_retries — needs manual intervention)
- US-005/006/008/009/010: Toate blocked de US-002 și US-004
---
## Intervenție manuală: 2026-05-08
### Stories deblocate: US-002 și US-004 (intervenție manuală Claude Code)
### US-002: Extrage oracle_receipt_writer.py
### Status: Complete
### Fișiere atinse:
- backend/modules/data_entry/services/oracle_receipt_writer.py — creat
- backend/modules/data_entry/services/__init__.py — exportat write_receipt
- backend/scripts/whatsapp_import/process_v2.py — import actualizat
- backend/scripts/whatsapp_import/whatsapp_flow.py — import actualizat
### US-004: Migrare DB oracle_server_id în telegram_users
### Status: Complete (10/10 teste pass)
### Fișiere atinse:
- shared/database/app_db.py — oracle_server_id în CREATE TABLE
- backend/modules/telegram/db/operations.py — server_id în link_user_to_oracle()
- backend/modules/telegram/auth/linking.py — pass/return server_id
- backend/alembic.ini — config Alembic
- backend/migrations/versions/20260505_add_oracle_server_id_to_telegram_users.py — migrare idempotentă
- tests/backend/test_telegram_oracle_server_id.py — 5 teste, toate pass
### Cauza blocajului original:
- aiosqlite, pydantic-settings, httpx nu erau instalate în test env
- patch("backend.modules.telegram.auth.linking...") necesita pre-import cu mock telegram
### Next:
- US-005: receipt_handlers.py (priority 50)
- US-006: bot_main.py wire (priority 60)
- US-008, 009, 010: teste
---
## Intervenție manuală US-005: 2026-05-08
### Story implementat: US-005 - receipt_handlers.py (tags: backend, db)
### Status: Complete (intervenție manuală — gate-urile au depășit 30 turns)
### Gates rulate manual:
- Syntax check: PASS
- AC1 funcții (9/9): PASS
- AC2 constante (5/5): PASS
- AC3 error handling specific: PASS (fără bare except Exception)
- AC4 ORA codes (3/3): PASS
- AC5 password cache: PASS
- AC7 duplicate guard: PASS
- AC8 confidence warning: PASS
- AC9 TTL expired: PASS
- AC10 oracle_pool.get_pool(): PASS
### Fișiere atinse:
- backend/modules/telegram/handlers/receipt_handlers.py — creat (459 linii)
- backend/modules/telegram/handlers/__init__.py — creat
- backend/modules/data_entry/services/oracle_receipt_writer.py — acceptă Connection din pool
- shared/database/oracle_pool.py — adăugat get_pool(server_id)
### Next: US-006 (wire bot_main.py), US-008/009/010 (teste)
---
## Iterație: 2026-05-08
### Story implementat: US-006 - Wire bot_main.py cu handlers și concurrent_updates (tags: backend)
### Status: Complete
### Gates rulate:
- Syntax check (py_compile): PASS pentru bot_main.py și receipt_handlers.py
- AC verification (AST): PASS 8/8
- AC1 .concurrent_updates(True): PASS
- AC2 MessageHandler(Document.PDF | Document.IMAGE): PASS
- AC3 MessageHandler(PHOTO): PASS
- AC4 CallbackQueryHandler pattern=r'^receipt:' ÎNAINTE de catch-all: PASS
- AC5 startup_cleanup() cu glob '/tmp/receipt_*.*' și missing_ok=True: PASS
- AC6 create_telegram_application există + syntax valid: PASS (full import deferat — deps lipsă local)
- /review (backend): PASS
- Handler ordering: corect (receipt CB înregistrat înainte de catch-all)
- concurrent_updates(True) thread-safety: OK — state keyed per-user, PTB rulează single event loop (asyncio, nu threads)
- startup_cleanup blast radius: OK — prefix `receipt_` suficient de specific; single-worker deployment per CLAUDE.md previne race orfani
- Error handling per-fișier OSError + outer broad except în startup(): appropriate (best-effort, nu blochează startup-ul)
### Fișiere atinse:
- backend/modules/telegram/bot_main.py (+57 linii, -2):
- Import `glob` adăugat
- Import receipt handlers (handle_document_message, handle_photo_message, handle_receipt_callback)
- Application.builder() refactored cu .concurrent_updates(True)
- 3 handler-uri noi înregistrate ÎNAINTE de catch-all CallbackQueryHandler
- Funcție nouă `startup_cleanup() -> int` care unlink-uiește orfanii din /tmp/receipt_*.*
- Apel `startup_cleanup()` adăugat la începutul `startup()` cu try/except non-critical
### Learnings:
- python-telegram-bot dispatchează handler-ele în ordinea înregistrării în același group → CallbackQueryHandler cu pattern specific TREBUIE înregistrat înainte de catch-all
- concurrent_updates(True) în PTB rulează update-urile în task-uri asyncio separate pe același event loop (nu threads), deci dict mutations între await-uri sunt sigure dacă cheile nu se suprapun
- startup_cleanup este safe doar în deployment single-worker (vezi ARCHITECTURE-DECISIONS.md `--workers 1`); altfel ar putea unlink-ui un fișier activ al altei instanțe
### Next:
- US-008: Unit tests pentru receipt_handlers (acum unblocked)
- US-009: E2E tests pentru flow Telegram bonuri (acum unblocked)
- US-010: Oracle integration test pentru oracle_receipt_writer (deja unblocked după US-002)
---
## Rate limit la iter 4 — sleep 1800
## Iterație: 2026-05-08
### Story implementat: US-009 - Write E2E tests pentru flow Telegram bonuri (tags: backend)
### Status: Complete
### Gates rulate:
- Pytest (3 E2E + 35 unit = 38 total): PASS în ambele ordini de colectare
- /review (backend): PASS — 3 review fix-uri aplicate înainte de commit:
1. Izolare stubs telegram: NU mai mut `sys.modules['telegram']` când există deja; rebind doar `rh.InlineKeyboardButton/Markup` pe modul (păstrează independența cross-file)
2. Concurrency assertion: înlocuit `elapsed < 0.5` (slabă: serial cu sleep 50ms ≈ 0.4s ar trece) cu in-flight counter `state["max_in_flight"] == 2` care dovedește overlap real
3. Adăugat verificare suffix `.pdf` și prefix `receipt_` pe temp file în testul PDF
### Fișiere atinse:
- tests/e2e/__init__.py (nou)
- tests/e2e/test_receipt_telegram_flow.py (nou, 3 teste E2E):
* test_e2e_pdf_send_preview_confirm_success — happy path PDF: download → OCR → preview → confirm → cod 7777 + temp unlinked
* test_e2e_photo_send_preview_cancel_cleanup — photo: largest resolution → preview → cancel → temp file `.exists() == False`
* test_e2e_concurrent_two_users_get_previews — 2 user_ids simultan, in-flight counter dovedește overlap, elapsed<30s
### AC verification:
- AC1 fișier există: PASS
- AC2 PDF flow (OCR processing → preview → confirm → success): PASS
- AC3 photo flow (cancel + temp deleted): PASS
- AC4 concurrent <30s + overlap real: PASS (max_in_flight=2)
- AC5 pytest PASS: PASS (3/3)
### Learnings:
- Cross-file `sys.modules` mutation creează test-order coupling între unit test și E2E test pe același modul stubbed; soluție: rebind doar atributele pe modulul under-test (`rh.X`) după import, nu modulul stub însuși
- Asserting `elapsed<X` pentru concurency e nesigur (serial poate trece accidental); folosește in-flight counter sau `asyncio.Barrier` care ar deadlock sub serial
- python-telegram-bot stubbed cu `MagicMock` direct ca clasă breakuiește când codul real face `MagicMock([list])` — MagicMock interpretează primul arg pozițional ca spec; soluție: stub-uri proper (clase mici) pentru obiecte construite de cod, nu MagicMock pur
- Reviewer a flagat bug paralel în receipt_handlers.py:447-459 (no cleanup on `oracledb.DatabaseError` / `asyncio.TimeoutError`): pending-state și temp-file rămân până la TTL 600s. Out-of-scope pentru US-009; merită follow-up pentru US-005.
### Next:
- US-010: deja Complete (intervenție manuală anterioară)
- Toate user stories au passes=true — proiectul COMPLET
---