Compare commits

...

2 Commits

Author SHA1 Message Date
Echo
db4b57f5a3 Update dashboard, memory, root (+1 ~7 -2) 2026-02-03 17:20:24 +00:00
Echo
f184b2b179 feat(process_bon): script complet procesare bonuri fiscale
- OCR via roa2web API
- SQLite via API (payment_methods, tva_breakdown)
- Oracle: partener, TVA încasare (4426/4428), ID_FDOC=17
- ID_JTVA_COLOANA per cotă TVA
- TAXCODE (TVAI pentru firma TVA încasare)
- Suport multiple cote TVA în același bon
- Plată CARD: fără 401=5311 (se face la extras)
2026-02-03 16:50:48 +00:00
14 changed files with 1856 additions and 529 deletions

View File

@@ -231,6 +231,19 @@ Pe Discord/WhatsApp, folosesc reacții emoji natural:
---
## 🧾 Procesare Bonuri (OBLIGATORIU)
Când Marius trimite un **PDF** și scrie "procesează bon" / "bon" / "adaugă bon":
1. **Găsesc PDF-ul** în media/inbound (ultimul primit)
2. **Rulez dry run:** `python3 tools/process_bon.py <pdf_path>`
3. **Arăt rezultatul:** partener, total, TVA, note contabile
4. **Aștept confirmare:** "ok" / "salvează" / "da"
5. **Salvez:** `python3 tools/process_bon.py <pdf_path> --save`
**Cuvinte cheie:** "procesează bon", "bon fiscal", "adaugă bon", "înregistrează bon"
---
## 🎬 YouTube Notes (OBLIGATORIU)
Când primesc un link YouTube:

View File

@@ -44,6 +44,40 @@
**Pentru orice altă adresă:** Citesc și raportez, aștept aprobare.
### Transcriere Audio/Video (Whisper)
- **Model:** OpenAI Whisper (local, CPU)
- **Dependințe:** PyTorch CPU, ffmpeg (static binary în ~/bin/)
- **Venv:** `~/clawd/venv/`
**Utilizare:**
```bash
cd ~/clawd && source venv/bin/activate
export PATH="$HOME/bin:$PATH"
# Transcriere directă
python3 -c "
import whisper
model = whisper.load_model('base')
result = model.transcribe('/path/to/audio.mp3', language='ro')
print(result['text'])
"
# Download de pe Google Drive + transcriere
gdown 'FILE_ID' -O /tmp/audio.mp3
```
**Modele disponibile:**
- `tiny` - cel mai rapid, mai puțin precis
- `base` - echilibru bun (folosit default)
- `small`, `medium`, `large` - mai precise, mai lente
**Formate suportate:** mp3, mp4, wav, m4a, webm, etc. (orice ffmpeg poate procesa)
**Note:**
- Pe CPU durează mai mult (~1x real-time pentru base)
- Pentru română, base e suficient de precis
- Transcrierea poate avea erori minore de recunoaștere
### Dashboard
- **URL:** https://moltbot.tailf7372d.ts.net/echo/
- **Task add:** `python3 dashboard/update_task.py add in-progress "titlu"`
@@ -97,6 +131,23 @@ memory_get path="memory/file.md" from=1 lines=50
3. Re-indexează DOAR fișierul modificat (incremental)
4. Celelalte fișiere rămân neatinse
### Procesare Bonuri Fiscale (PDF → Oracle)
- **Script:** `python3 tools/process_bon.py <pdf_path> [--save]`
- **Comandă:** Când Marius trimite PDF + "procesează bon" sau "bon fiscal"
- **Dry run:** fără `--save` - arată ce ar salva
- **Salvare:** cu `--save` - salvează efectiv în Oracle
**Flux:**
1. Marius trimite PDF pe WhatsApp/Discord
2. Marius scrie: "procesează bon" / "bon" / "adaugă bon"
3. Echo rulează dry run, arată rezultatul
4. Marius confirmă "ok" / "salvează" → Echo rulează cu --save
**Ce face scriptul:**
- OCR via roa2web API
- SQLite: receipt complet
- Oracle: partener + note contabile (ID_JTVA, TAXCODE, etc.)
### Git
- **Repo:** ~/clawd → gitea.romfast.ro/romfast/clawd
- **Commit:** `python3 tools/git_commit.py --push`

View File

@@ -1,5 +1,5 @@
{
"lastUpdated": "2026-02-02T22:27:06.452Z",
"lastUpdated": "2026-02-03T17:20:07.199Z",
"programs": [
"ROACONT",
"ROAGEST",
@@ -21,10 +21,11 @@
"program": "ROACONT",
"owner": "robert",
"priority": "urgent-important",
"status": "todo",
"status": "done",
"created": "2026-02-02T11:25:18.115Z",
"deadline": "2026-02-02",
"updated": "2026-02-02T22:27:06.428Z"
"updated": "2026-02-02T22:27:06.428Z",
"completed": "2026-02-03T17:20:07.195Z"
},
{
"id": "ROA-001",

View File

@@ -1,5 +1,44 @@
# 3 Februarie 2026
## roa2web WhatsApp Import - COMPLET
### Ce s-a realizat:
1. **OCR prin API** - doctr-plus, ~4 sec per bon (nu 30 sec ca PaddleOCR cold start)
2. **Flux complet testat:** PDF WhatsApp → OCR → SQLite → Oracle
3. **Scripturi în repo:** `roa2web/backend/scripts/whatsapp_import/`
4. **Commit:** `1366dbc` pe main
### Flux final:
```
PDF (WhatsApp) → OCR API (~4sec) → SQLite (draft) → Aprobare frontend → Oracle
```
### Probleme rezolvate:
- **Oracle pool "SID not found"** - trebuia restart complet backend (kill -9)
- **Frontend fără server dropdown** - Marius a fixat și făcut commit
- **Server ID** - acum e `central` nu `test`
### Endpoint-uri API folosite:
- `POST /api/auth/login` - cu server_id="central"
- `POST /api/auth/check-identity` - verifică user și returnează servere
- `POST /api/data-entry/ocr/extract` - submit OCR job
- `GET /api/data-entry/ocr/jobs/{id}` - rezultat OCR
- `POST /api/data-entry/receipts/` - creare receipt în SQLite
### Test real efectuat:
- Bon Dedeman (RO10562600) primit pe WhatsApp
- OCR: 5.2 sec, confidence 96%
- Salvat în SQLite: ID=73, status=draft
- Salvat și în Oracle: COD=1140631, luna 01/2026
### Locații importante (claude-agent LXC 171):
- Backend: http://localhost:8000 (sau claude-agent:8000)
- Frontend: http://localhost:3000 (sau claude-agent:3000)
- Scripturi: `/workspace/roa2web/backend/scripts/whatsapp_import/`
- Start: `./start.sh central`
---
## Decizii
- (în așteptare raport dimineață)
@@ -13,6 +52,10 @@
- [ ] A2: Sistemul 5 pași pentru frici (15 min) - de programat
- [ ] Verificare securitate Clawdbot (din insights tehnice)
- [ ] Verificare email replies (flux nefuncțional?)
- [ ] **BON DE SALVAT:** CUI RO11201891, 310.98 RON, 02.02.2026
- PDF: `2831eeeb-f331-4fb1-a7b1-ede1c954eadb.pdf`
- Partener nou - de verificat numele real
- Dry run făcut, așteaptă confirmare
## Insights disponibile (din 2026-02-03.md)
- ⚡ Heartbeat cost optimization - VERIFICAT, monitorizăm

View File

@@ -1,12 +1,13 @@
{
"lastChecks": {
"agents_sync": "2026-02-02",
"email": 1770044415,
"calendar": null,
"git": 1769965200,
"agents_sync": "2026-02-03",
"email": 1770206400,
"calendar": 1770206400,
"git": 1770220800,
"kb_index": 1770022820
},
"notes": {
"2026-02-02": "15:00 UTC - Email OK (nimic nou). Cron jobs funcționale toată ziua."
"2026-02-02": "15:00 UTC - Email OK (nimic nou). Cron jobs funcționale toată ziua.",
"2026-02-03": "12:00 UTC - Calendar: sesiune 15:00 alertată. Emailuri răspuns rapoarte în inbox (deja read)."
}
}

View File

@@ -64,11 +64,11 @@
"project": null,
"subdir": null,
"video": "",
"tldr": "*Surse: Video-uri tehnice Clawdbot/Claude Code*"
"tldr": "*Surse: Video-uri tehnice Clawdbot/Claude Code/Lead Generation*"
},
{
"file": "notes-data/projects/analiza-telegram-whatsapp-roa2web.md",
"title": "Analiză: Telegram/WhatsApp → roa2web → Contabilitate ROA",
"file": "notes-data/projects/roa2web-telegram-import/README.md",
"title": "Proiect: Import Bonuri Fiscale via Telegram/WhatsApp → ROA",
"date": "2026-02-03",
"tags": [],
"domains": [
@@ -78,27 +78,55 @@
"project"
],
"category": "projects",
"project": null,
"project": "roa2web-telegram-import",
"subdir": null,
"video": "",
"tldr": "Integrare canale messaging (Telegram/WhatsApp) cu roa2web pentru import documente în contabilitatea ROA. OCR prin Doctr (cost zero, local). Flux cu aprobare contabil."
"tldr": "Sistem pentru importul bonurilor fiscale de achiziție din Telegram/WhatsApp în contabilitatea ROA. OCR prin Doctr (cost zero, local). Integrare cu Oracle prin proceduri existente (PACK_CONTAFIN)."
},
{
"file": "notes-data/projects/analiza-import-bonuri-pdf.md",
"title": "Analiză: Sistem Import Bonuri PDF via Telegram/Discord/WhatsApp → roa2web",
"file": "notes-data/projects/roa2web-telegram-import/schema-oracle.md",
"title": "Schema Oracle - ROA (MARIUSM_AUTO)",
"date": "2026-02-03",
"tags": [],
"domains": [
"work"
],
"types": [
"project"
],
"domains": [],
"types": [],
"category": "projects",
"project": null,
"project": "roa2web-telegram-import",
"subdir": null,
"video": "",
"tldr": "Sistem pentru importul bonurilor fiscale (PDF/imagine) în ERP ROA prin canale de mesagerie. Utilizatorul trimite poza bonului → OCR extrage datele → roa2web validează și importă în Oracle."
"tldr": "*Schema documentată de Echo • 2026-02-03*"
},
{
"file": "notes-data/projects/roa2web-telegram-import/flux-contabil.md",
"title": "Flux Contabil - Import Bonuri Fiscale Achiziție",
"date": "2026-02-03",
"tags": [],
"domains": [],
"types": [],
"category": "projects",
"project": "roa2web-telegram-import",
"subdir": null,
"video": "",
"tldr": "*Flux documentat de Echo • 2026-02-03*"
},
{
"file": "notes-data/projects/grup-sprijin/biblioteca/meditatie-vizualizare-motivatie.md",
"title": "Meditație: Vizualizare pentru Motivație",
"date": "2026-02-03",
"tags": [
"grup-sprijin"
],
"domains": [
"sprijin"
],
"types": [
"meditatie"
],
"category": "projects",
"project": "grup-sprijin",
"subdir": "biblioteca",
"video": "",
"tldr": "Exercițiu de vizualizare care leagă o acțiune pentru care vrei motivație de o stare de plăcere intensă din trecut. Folosește tehnica \"fissurii în ecran\" pentru a transfera emoțional motivația."
},
{
"file": "notes-data/youtube/2026-02-03_openclaw-72-hours-full-breakdown.md",
@@ -1689,12 +1717,12 @@
}
],
"stats": {
"total": 92,
"total": 94,
"by_domain": {
"work": 27,
"work": 26,
"health": 16,
"growth": 33,
"sprijin": 26,
"sprijin": 27,
"scout": 2
},
"by_category": {
@@ -1702,7 +1730,7 @@
"emails": 1,
"health": 1,
"insights": 8,
"projects": 23,
"projects": 25,
"reflectii": 3,
"retete": 1,
"youtube": 38,

View File

@@ -1,12 +1,12 @@
# Insights - 3 Februarie 2026
**Surse procesate:** 3 note tehnice noi + 1 insight anterior
**Focus:** Optimizări Clawdbot + Multi-agent orchestration
**Actualizat:** 06:00 UTC
**Surse procesate:** 5 note tehnice noi + insights anterioare
**Focus:** Optimizări Clawdbot + Multi-agent + **Lead Generation pentru clienți noi**
**Actualizat:** 17:00 UTC (19:00 București)
---
## 🔗 CONEXIUNE: De la Tool la Companion
## 🔗 CONEXIUNE: De la Tool la Companion → la CLIENȚI NOI
**Pattern descoperit:** Notele tehnice de azi completează un cerc important:
@@ -16,10 +16,17 @@ TOOL (Clawdbot) ← COST OPTIMIZATION ← MULTI-AGENT ← SECURITATE
MARIUS: 80/20 mindset
APLICARE: Delegare eficientă, nu micromanagement
⚡ NOU: Automatizare lead generation =
deblocare "nu caut clienți noi"
```
**Insight cheie seară:** Nota despre "Unlimited Leads with Claude Code" atacă DIRECT provocarea principală a lui Marius: inacțiunea în căutarea clienților noi.
Ironia: Clawdbot (pe care rulăm) e exemplul perfect de "do things, not suggest things" - exact ce Marius vrea de la angajatul nou!
**Și acum:** Poate face lead generation automat, eliminând FRICȚIA care probabil cauzează inacțiunea.
---
## @work - Tehnic și business
@@ -158,6 +165,64 @@ MULTI-AGENT (dorit):
---
### [ ] ⚡ **COLD EMAIL AUTOMATIZAT - Soluția pentru "nu caut clienți noi" (prioritate: ⚡urgent)**
**Context:** Din video-ul "How I Get Unlimited Leads Using Claude Code" - o agenție a construit sistem de lead generation care procesează 272,000 leads/secundă.
**Esența PROFUNDĂ:**
James, care NU știa să codeze, a construit întregul sistem într-o săptămână după ce a învățat Claude Code timp de 3 săptămâni. Nu e vorba de "învață programare", ci de "descrie ce vrei și lasă AI-ul să construiască".
**Conexiune DIRECTĂ cu Marius:**
1. **Credința limitativă:** "Clienți noi = mai multă muncă"
- **Realitate:** Sistemul automatizat ELIMINĂ munca manuală de prospecting
- Cold email la scară = AI face screening-ul, tu vorbești doar cu cei interesați
2. **Inacțiune în căutarea clienților:**
- **Cauza profundă:** Probabil nu e lene, ci FRICȚIE - e obositor să faci manual
- **Soluția 80/20:** Automatizează partea grea (găsit leads, validat emails, trimis), rămâi doar cu conversațiile
3. **Model de business ROA:**
- Clienții lui sunt software houses cu ERP-uri
- Cold email către companii care au nevoie de soluții Oracle/contabilitate
- Google Ads library scraper → găsești companii care rulează ads (= au buget)
**Ce au construit ei (și ce s-ar putea adapta):**
- Lead Processor: validare email în timp real
- Ad Library Scrapers: găsesc companii care rulează ads Google/LinkedIn
- Auto-Refill: când rămâi fără leads, se reîncarcă automat
- Executive Summary: raport AI despre ce funcționează
**Rezultate lor:**
- RB2B: $4M ARR în 4 luni (42% din cold email)
- Fixer AI: $4.3M pipeline anual
- 8.8 milioane emails/lună
**Acțiune concretă propusă:**
**Varianta MINIMĂ (80/20):**
1. Echo creează un scraper simplu care găsește companii românești care:
- Au anunțuri de angajare pentru contabil/economist
- Sau postează despre ERP/software contabilitate pe LinkedIn
2. Lista → manual review de Marius (5 min/zi)
3. Template email personalizat per industrie
4. Trimitere semi-automată
**Efort:** Echo ~4h setup, Marius ~30 min/săptămână review
**Potențial:** 5-10 leads calificate/săptămână
**⚠️ ÎNTREBARE pentru Marius:**
"Dacă ai avea o listă de 10 companii/săptămână care au nevoie DEMONSTRABILĂ de soluții ROA, și un email template gata de trimis, ai acționa?"
Dacă răspunsul e DA → construiesc sistemul.
Dacă răspunsul e "tot nu aș face" → problema nu e lipsa lead-urilor, ci altceva mai profund.
**✅ RECOMAND** - direct pe provocarea principală, efort mic pentru varianta minimă
**Sursă:** [How I Get Unlimited Leads Using Claude Code](files.html#memory/kb/youtube/2026-02-03_unlimited-leads-claude-code-cold-email.md)
---
### [ ] 💡 **DON'T WORK ON APP, WORK ON AGENTS (prioritate: 💡nice)**
**Context:** Citat transformator:
@@ -241,33 +306,47 @@ MULTI-AGENT (dorit):
| Angajat nou | Control vs autonomie | Nivele de trust progresive |
| Clienți noi | Efort vs venit | Automatizează prospectarea |
### Tema: INACȚIUNE CLIENȚI NOI (NOU!)
| Barieră percepută | Realitate | Soluție |
|-------------------|-----------|---------|
| "E prea multă muncă" | Munca manuală da, automatizat nu | Sistem lead gen automat |
| "Nu știu unde să caut" | AI poate găsi semnale (ads, hiring) | Scrapers LinkedIn/Google |
| "Cold email nu funcționează" | La ei: 42% din revenue = cold email | Template + personalizare |
| "Nu am timp" | 30 min/săptămână pentru review | Echo face restul |
---
## ❓ Întrebări pentru Marius
1. **Vrei să verificăm configurația de securitate Clawdbot?** E meta - video-ul e despre tool-ul pe care îl folosim.
1. **CLIENȚI NOI (cea mai importantă):** Dacă ai avea o listă de 10 companii/săptămână care au nevoie demonstrabilă de soluții ROA, și un email template gata de trimis, ai acționa? (Răspunsul sincer la asta ne spune dacă problema e fricțiune sau altceva mai profund)
2. **Pentru angajat: ce nivel de sandbox ar fi confortabil?** Definit, Parametrizat, sau Autonom?
2. **Vrei să verificăm configurația de securitate Clawdbot?** E meta - video-ul e despre tool-ul pe care îl folosim.
3. **Ai experimentat cu "coding overnight"?** Descrii seara, review dimineața?
3. **Pentru angajat: ce nivel de sandbox ar fi confortabil?** Definit, Parametrizat, sau Autonom?
4. **Ce "agenți" (documentație/template) ar putea face training-ul pentru tine?**
4. **Ai experimentat cu "coding overnight"?** Descrii seara, review dimineața?
5. **Ce "agenți" (documentație/template) ar putea face training-ul pentru tine?**
---
## 📊 Statistici procesare
**Surse noi:** 3 note tehnice
**Surse procesate azi:** 5 note tehnice
- OpenClaw 72h Breakdown
- Clawdbot Cost Optimization
- Claude Code Task System
- **NOU:** Unlimited Leads Claude Code Cold Email
- OpenClaw Use Cases Automations
**Insights extrase:** 7 total (+ 1 anterior păstrat)
- ⚡ Urgent: 2 (securitate, cost)
**Insights extrase:** 8 total
- ⚡ Urgent: 3 (securitate, cost, **COLD EMAIL/CLIENȚI NOI**)
- 📌 Important: 4 (delegare, multi-agent, overnight, fundamentele)
- 💡 Nice: 1 (meta-level work)
**Conexiuni cu provocările lui Marius:**
- **Inacțiune clienți noi → 1 insight DIRECT și acționabil**
- Angajat nou și training → 3 insights directe
- Stil 80/20 → 2 insights directe
- Securitate infrastructură → 1 insight
@@ -277,5 +356,6 @@ MULTI-AGENT (dorit):
---
*Actualizat: 2026-02-03 06:00 UTC*
*Surse: Video-uri tehnice Clawdbot/Claude Code*
*Actualizat: 2026-02-03 17:00 UTC (19:00 București)*
*Run: insights-extract seară*
*Surse: Video-uri tehnice Clawdbot/Claude Code/Lead Generation*

View File

@@ -1,226 +0,0 @@
# Analiză: Sistem Import Bonuri PDF via Telegram/Discord/WhatsApp → roa2web
**Data:** 2026-02-03
**Status:** Analiză conceptuală
**Tags:** @work @project
---
## TL;DR
Sistem pentru importul bonurilor fiscale (PDF/imagine) în ERP ROA prin canale de mesagerie. Utilizatorul trimite poza bonului → OCR extrage datele → roa2web validează și importă în Oracle.
---
## 1. Arhitectură Propusă
```
┌─────────────┐ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Telegram │ │ │ │ │ │ │
│ Discord │────▶│ Clawdbot │────▶│ OCR Engine │────▶│ roa2web │
│ WhatsApp │ │ (Router) │ │ (Parsare) │ │ (FastAPI) │
└─────────────┘ └─────────────┘ └──────────────┘ └─────────────┘
│ │ │
│ │ │
▼ ▼ ▼
• Primește fișier • Tesseract/Mistral • Validare date
• Identifică tip • Extrage câmpuri • Import Oracle
• Rutare canal • Structurează JSON • Confirmare user
```
---
## 2. Componente Tehnice
### 2.1 Canale Inbound (Clawdbot)
| Canal | Cum primește | Format acceptat |
|-------|--------------|-----------------|
| Telegram | Bot API / file_id | PDF, JPG, PNG |
| Discord | Attachment | PDF, JPG, PNG |
| WhatsApp | Media message | PDF, JPG, PNG |
**Clawdbot poate:**
- Detecta atașamente pe orice canal
- Descărca fișierul local temporar
- Ruta către procesare OCR
### 2.2 OCR Engine
**Opțiuni (în ordinea complexității):**
| Soluție | Pro | Contra | Efort |
|---------|-----|--------|-------|
| **Tesseract + regex** | Gratuit, local | Acuratețe medie, layout-uri diverse | Mic |
| **Mistral OCR API** | Calitate bună, JSON direct | Cost per request | Mic |
| **Claude Vision** | Excelent la context | Cost mai mare | Mic |
| **Google Document AI** | Enterprise, structurat | Cost, setup complex | Mare |
**Recomandare:** Mistral OCR sau Claude Vision - calitate bună, integrare simplă cu Clawdbot.
### 2.3 Câmpuri de Extras din Bon
```json
{
"tip_document": "bon_fiscal",
"data": "2026-02-03",
"ora": "14:32",
"furnizor": {
"nume": "MEGA IMAGE SRL",
"cui": "RO12345678",
"adresa": "Str. Exemplu nr. 1"
},
"articole": [
{"denumire": "Paine alba", "cantitate": 1, "pret_unitar": 4.50, "valoare": 4.50},
{"denumire": "Lapte 1L", "cantitate": 2, "pret_unitar": 8.00, "valoare": 16.00}
],
"total": 20.50,
"tva": {
"cota_9": 1.50,
"cota_19": 0.50
},
"numar_bon": "0001234",
"casa_marcat": "ABC12345"
}
```
### 2.4 roa2web Integration
**Endpoint propus:** `POST /api/data-entry/import-bon`
```python
# FastAPI endpoint
@router.post("/import-bon")
async def import_bon(
bon_data: BonFiscalSchema,
user_id: str = Depends(get_current_user)
):
# 1. Validare date (CUI valid, sume corecte)
# 2. Mapare furnizor în baza ROA
# 3. Creare document intrare marfă
# 4. Return confirmare + document_id
```
---
## 3. Flux Utilizator
### 3.1 Happy Path
1. **Marius** face poză la bon în magazin
2. **Trimite** pe Telegram la @ROABot (sau grup dedicat)
3. **Clawdbot** detectează imagine, descarcă
4. **OCR** extrage datele, validează structura
5. **Clawdbot** trimite preview: "Am extras: Mega Image, 20.50 RON, 3 articole. Confirm import? ✅/❌"
6. **Marius** confirmă (sau corectează)
7. **roa2web** importă în Oracle
8. **Confirmare**: "✅ Bon importat. Document #12345"
### 3.2 Edge Cases
| Situație | Handling |
|----------|----------|
| Poză neclară | "Nu am putut citi bonul. Retrimite o poză mai clară." |
| Furnizor nou | "Furnizor MEGA IMAGE nu există. Creez? [Da/Nu]" |
| Articol necunoscut | "Articol 'Paine alba' nu are corespondent. Alege: [1] Paine [2] Produse panificatie [3] Skip" |
| PDF protejat | "PDF-ul e protejat. Trimite ca imagine." |
---
## 4. Provocări și Soluții
### 4.1 Calitatea imaginii
- **Problemă:** Poze făcute pe fugă, iluminate prost
- **Soluție:**
- Pre-procesare imagine (contrast, deskew)
- Fallback la input manual dacă OCR confidence < 70%
### 4.2 Varietatea formatelor
- **Problemă:** Fiecare comerciant are alt layout
- **Soluție:**
- LLM (Claude/Mistral) extrage semantic, nu pe poziție
- Template-uri pentru comercianți frecvenți (Mega, Lidl, Kaufland)
### 4.3 Mapare articole
- **Problemă:** "Lapte Zuzu 1L 3.5%" vs cod ROA "LAPTE_001"
- **Soluție:**
- Fuzzy matching pe denumire
- Tabel mapări (bon_text cod_roa)
- Learning: salvează mapările confirmate
### 4.4 Rate limiting canale
- **Problemă:** WhatsApp/Telegram pot limita
- **Soluție:**
- Queue local pentru procesare
- Confirmare async ("Procesez, te anunț în 30 sec")
---
## 5. Plan Implementare
### Faza 1: POC (2-3 zile)
- [ ] Endpoint Clawdbot pentru primire fișier
- [ ] Integrare Mistral OCR / Claude Vision
- [ ] Parsare bon simplu (1 comerciant test)
- [ ] Răspuns pe canal cu datele extrase
### Faza 2: Integrare roa2web (3-5 zile)
- [ ] Endpoint FastAPI `/api/import-bon`
- [ ] Validare și mapare furnizor
- [ ] Creare document Oracle
- [ ] Confirmare utilizator
### Faza 3: Polish (2-3 zile)
- [ ] Handling edge cases
- [ ] Template-uri comercianți mari
- [ ] Tabel mapări articole
- [ ] Logging și statistici
### Faza 4: Extindere (opțional)
- [ ] Suport pentru mai multe tipuri documente (facturi, chitanțe)
- [ ] Batch import (multiple bonuri)
- [ ] Dashboard statistici achiziții
---
## 6. Efort Estimat
| Componentă | Ore | Cine |
|------------|-----|------|
| Clawdbot file handler | 2-4 | Echo/Claude |
| OCR integration | 2-3 | Echo/Claude |
| roa2web endpoint | 4-6 | Marius + Claude |
| Mapări și validări | 4-8 | Marius |
| **Total MVP** | **12-21 ore** | Mix |
**Cost OCR estimat:** ~$0.01-0.05 per bon (Claude Vision sau Mistral)
---
## 7. Beneficii
- **Timp economisit:** 2-5 min/bon 10 sec/bon
- **Erori reduse:** OCR consistent vs. tastare manuală
- **Mobilitate:** Import din teren, nu doar de la birou
- **Trasabilitate:** Fiecare bon are sursa (poza originală)
---
## 8. Următorii Pași
1. **Marius decide:** Merită efortul? Câte bonuri/lună?
2. **Test rapid:** Un bon de la Mega Image Claude Vision vezi output
3. **Prototip:** Clawdbot + Claude Vision răspuns formatat
---
## 9. Întrebări pentru Marius
1. Câte bonuri/documente procesezi lunar manual?
2. Care sunt cei mai frecvenți furnizori?
3. Ce câmpuri sunt obligatorii în ROA pentru intrare marfă?
4. Preferi confirmare înainte de import sau import direct cu corecție ulterioară?
---
*Analiză generată de Echo • 2026-02-03 03:00*

View File

@@ -1,263 +0,0 @@
# Analiză: Telegram/WhatsApp → roa2web → Contabilitate ROA
**Data:** 2026-02-03
**Status:** Analiză tehnică actualizată după review cod
**Tags:** @work @project
---
## TL;DR
Integrare canale messaging (Telegram/WhatsApp) cu roa2web pentru import documente în contabilitatea ROA. OCR prin Doctr (cost zero, local). Flux cu aprobare contabil.
**UPDATE:** După analiza codului, roa2web are deja:
- ✅ OCR complet (Doctr + PaddleOCR + Tesseract)
- ✅ API `/api/data-entry/ocr/extract` funcțional
- ✅ Bot Telegram pentru dashboard/facturi/sold
-**LIPSEȘTE:** Handler fișiere în bot Telegram
-**LIPSEȘTE:** PDF text layer extraction (optimizare pentru Adobe Scan)
---
## 1. Ce Există Deja
### roa2web
- **OCR:** Doctr (local, gratuit, rezultate bune)
- **Storage:** SQLite local (bonuri procesate)
- **API:** FastAPI endpoints
### Clawdbot
- **Canale:** Telegram ✅, WhatsApp ✅, Discord ✅
- **Capabilități:** Primire fișiere, rutare, răspuns
### ROA (ERP)
- **DB:** Oracle
- **Contabilitate:** Module existente pentru intrări/facturi
---
## 2. Ce Lipsește (Gap Analysis)
| Component | Status | Ce trebuie |
|-----------|--------|------------|
| Telegram → roa2web | ❌ | Endpoint webhook sau polling |
| WhatsApp → roa2web | ❌ | Endpoint webhook sau polling |
| roa2web → Oracle | ❌ | API salvare în contabilitate |
| Workflow aprobare | ❌ | UI contabil + status draft/approved |
---
## 3. Arhitectură Propusă
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Telegram │ │ │ │ │ │ │
│ WhatsApp │────▶│ Clawdbot │────▶│ roa2web │────▶│ ROA Oracle │
└─────────────┘ │ (Bridge) │ │ (OCR+API) │ │ (Contab) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
• Primește fișier • Doctr OCR • Salvare draft
• Forward la API • Extrage date • Aprobare contabil
• Răspuns user • Validare • Înregistrare finală
```
---
## 4. Opțiuni Implementare
### Opțiunea A: Clawdbot ca Bridge (RECOMANDAT)
**Flux:**
1. User trimite bon pe Telegram/WhatsApp
2. Clawdbot detectează fișier, îl descarcă
3. Clawdbot trimite fișierul la `roa2web/api/upload`
4. roa2web procesează cu Doctr, returnează JSON
5. Clawdbot trimite răspuns user: "Bon procesat: Mega Image, 45.50 RON"
6. roa2web salvează draft în Oracle (status: pending)
7. Contabil vede în UI, aprobă → status: approved
**Pro:**
- Folosește Clawdbot existent
- Nu modifică roa2web prea mult
- Flexibilitate pe canale
**Contra:**
- Clawdbot trebuie să știe să ruteze fișiere
**Efort:** 8-12 ore
### Opțiunea B: Webhook Direct în roa2web
**Flux:**
1. Telegram/WhatsApp webhook direct la roa2web
2. roa2web procesează și salvează
3. roa2web trimite răspuns înapoi pe canal
**Pro:**
- Mai direct, fără intermediar
**Contra:**
- Trebuie implementat handler pentru fiecare canal
- Duplicare logică cu Clawdbot
**Efort:** 15-20 ore
### Opțiunea C: Shared Folder / Email
**Flux:**
1. User trimite pe canal → Clawdbot salvează în folder
2. roa2web monitorizează folder, procesează automat
**Pro:**
- Simplu, decuplat
**Contra:**
- Fără feedback instant la user
- Polling inefficient
**Efort:** 4-6 ore
---
## 5. Recomandare: Opțiunea A (Clawdbot Bridge)
### 5.1 Ce trebuie în Clawdbot
```python
# Pseudo-cod: handler pentru fișiere pe Telegram/WhatsApp
async def on_file_received(channel, file, user):
# 1. Descarcă fișierul
local_path = await download_file(file)
# 2. Trimite la roa2web
response = await http_post(
"https://roa2web.romfast.ro/api/ocr/process",
files={"file": open(local_path, "rb")},
data={"user_id": user.id, "source": channel}
)
# 3. Răspuns la user
if response.ok:
data = response.json()
await reply(f"✅ Procesat: {data['furnizor']}, {data['total']} RON")
else:
await reply("❌ Eroare procesare. Încearcă din nou.")
```
### 5.2 Ce trebuie în roa2web
**Endpoint nou:** `POST /api/ocr/process`
- Primește fișier (PDF/imagine)
- Procesează cu Doctr (existent)
- Salvează în SQLite (existent) + Oracle (NOU)
- Returnează JSON cu datele extrase
**Endpoint nou:** `POST /api/contabilitate/save-draft`
- Primește datele extrase
- Creează înregistrare draft în Oracle
- Status: pending_approval
**Endpoint nou:** `POST /api/contabilitate/approve/{id}`
- Contabil aprobă
- Status: approved → înregistrare finală
### 5.3 UI Contabil (opțional, faza 2)
- Lista documente pending
- Preview date extrase vs. imagine originală
- Buton Approve / Reject / Edit
---
## 6. Integrare Oracle - Detalii
### Ce tabele în ROA?
**Întrebare pentru Marius:**
- Unde se salvează intrările de marfă/bonuri în Oracle?
- Ce câmpuri sunt obligatorii?
- Există API/proceduri Oracle existente pentru insert?
### Propunere structură draft
```sql
-- Tabel nou pentru documente OCR pending
CREATE TABLE roa_ocr_drafts (
id NUMBER PRIMARY KEY,
source VARCHAR2(20), -- 'telegram', 'whatsapp'
user_id VARCHAR2(50),
file_path VARCHAR2(500),
extracted_data CLOB, -- JSON cu datele OCR
status VARCHAR2(20), -- 'pending', 'approved', 'rejected'
created_at TIMESTAMP,
approved_by VARCHAR2(50),
approved_at TIMESTAMP,
roa_document_id NUMBER -- FK la documentul creat în ROA după aprobare
);
```
---
## 7. Plan Implementare
### Faza 1: Conectare canale (3-4 ore)
- [ ] Clawdbot: handler fișiere Telegram
- [ ] Clawdbot: handler fișiere WhatsApp
- [ ] Forward la roa2web API
### Faza 2: roa2web API (4-6 ore)
- [ ] Endpoint `/api/ocr/process` (extinde existent)
- [ ] Returnare JSON standardizat
- [ ] Logging și error handling
### Faza 3: Salvare Oracle (4-6 ore)
- [ ] Conexiune Oracle din roa2web (cx_Oracle/oracledb)
- [ ] Tabel `roa_ocr_drafts`
- [ ] Endpoint `/api/contabilitate/save-draft`
### Faza 4: Aprobare (2-3 ore)
- [ ] Endpoint `/api/contabilitate/approve`
- [ ] Notificare user după aprobare
### Faza 5: UI Contabil (opțional, 4-8 ore)
- [ ] Pagină listare drafts
- [ ] Aprobare/respingere din UI
---
## 8. Estimare Totală
| Fază | Ore | Cine |
|------|-----|------|
| Conectare canale | 3-4 | Echo (Clawdbot config) |
| roa2web API | 4-6 | Marius + Claude |
| Salvare Oracle | 4-6 | Marius (cunoaște schema) |
| Aprobare | 2-3 | Marius + Claude |
| **Total MVP** | **13-19 ore** | |
| UI Contabil (opț.) | 4-8 | Marius |
**Cost:** 0 RON (Doctr local, fără API externe)
---
## 9. Întrebări pentru Marius
1. **Schema Oracle:** În ce tabel/modul se salvează bonurile în contabilitate?
2. **Câmpuri obligatorii:** Ce date TREBUIE să existe pentru o înregistrare validă?
3. **Contabil:** Cine aprobă? Un singur user sau mai mulți?
4. **Notificări:** Contabilul vrea notificare când vine ceva nou? (email/Telegram?)
5. **roa2web deploy:** Unde rulează? Are acces la Oracle-ul de producție?
---
## 10. Următorul Pas
Când ai 30 min, hai să:
1. Clarificăm schema Oracle pentru bonuri
2. Verificăm dacă roa2web poate conecta la Oracle
3. Facem un test: Telegram → Clawdbot → roa2web → JSON response
---
*Analiză generată de Echo • 2026-02-03*

View File

@@ -0,0 +1,121 @@
# Meditație: Vizualizare pentru Motivație
**Sursă:** MP3 de la Marius (Google Drive)
**Durată:** ~10-12 minute
**Domenii:** @sprijin @growth
**Tip:** meditație ghidată NLP
---
## TL;DR
Exercițiu de vizualizare care leagă o acțiune pentru care vrei motivație de o stare de plăcere intensă din trecut. Folosește tehnica "fissurii în ecran" pentru a transfera emoțional motivația.
---
## Pași
1. **Relaxare** - Găsește o poziție confortabilă, închide ochii
2. **Identifică imaginea de plăcere**
- Caută în memorie o imagine din trecut care îți produce plăcere extremă
- Poate fi un moment, o acțiune, un loc de relaxare
- Orice simți că îți produce plăcere intensă
3. **Vizualizează pe ecranul mental**
- Proiectează imaginea pe un ecran uriaș (ca la cinematograf)
- Vezi-te pe tine în scenă, filmat din lateral
- Fii atent la ce vezi, ce auzi, ce simți
4. **Amplifică starea**
- Imaginează-ți o telecomandă în mână
- Apasă butonul și crește luminozitatea
- Fă scena mai caldă, mai luminoasă
- Simte cum se amplică starea de plăcere
5. **Suprapune ecranul cu acțiunea**
- Între tine și scena cu plăcere, apare un nou ecran
- Pe noul ecran: tu făcând acțiunea pentru care vrei motivație
- Poate fi: să înveți ceva, să faci o acțiune, să lucrezi la dezvoltare personală
6. **Tehnica fissurii** (repetă de 3 ori)
- Creează o fissură în centrul imaginii cu acțiunea
- Lărgește fissura până vezi complet scena de plăcere din spate
- Închide rapid fissura
- Observă cum se schimbă emoția față de acțiune
7. **Proiectează în viitor**
- Lasă să curgă emoția spre viitorul tău
- De-a lungul secundelor, minutelor, orelor, zilelor
- Acțiunea rămâne definitiv legată de starea de plăcere
8. **Revenire** - Deschide ochii când ești gata
---
## Transcriere completă
> O să te rog să te relaxezi și să închizi ochii, să îți găsești o poziție cât mai confortabilă și să identifici în memoria ta, o imagine de undeva din trecut, îndepărtată sau mai apropiată, o imagine care îți place extrem de mult, ceva care te motivează foarte, foarte tare.
>
> Poate să fie absolut orice, poate să fie un anumit element, poate să fie o acțiune, poate să fie un moment în care te relaxezi, orice simți tu că îți produce o plăcere extremă.
>
> Și când ai găsit această imagine, o să te rog să o vizualizezi, să ți-o imaginezi pe ecranul minții tale, cât mai clar, și doar să o privești, și să te vezi și pe tine în acea imagine ca și cum cineva cu un aparat de filmat ar fi filmat din lateral acea scenă.
>
> Și acum ai proiectat-o pe un ecran uriaș, ca al unui cinematograf chiar în mintea ta. Fii acum atent la tot ce vezi în această scenă. Fii acum atent la tot ce auzi, fii atent la tot ce simți.
>
> Și imaginează-ți acum că ții în mână o telecomandă și că apăși pe un buton și intensifici, crești luminozitatea, și scena pe care o vezi în minte, fă-o mai luminoasă, mai caldă, și o să simți acum cum se amplifică chiar și mai mult, starea de plăcere pe care o ai privind acea imagine.
>
> O să te las pentru câteva momente să savurezi această stare de plăcere pe care o ai în timp ce privești această scenă.
>
> Foarte bine. Păstrează acum în minte această scenă și imaginează-ți că între această scenă și tine apare un alt ecran care acoperă complet scena cu starea de plăcere.
>
> Și pe acest ecran care a apărut acum între tine și această scenă inițială, așază o imagine cu tine, făcând acțiunea pentru care ai zis că să te simți mai motivat, să ai mai mult entuziasm, mai multă dorință de a o face.
>
> Poate vrei să înveți ceva, sau poate vrei să faci o anumită acțiune, sau poate chiar vrei să lucrezi pentru dezvoltarea ta personală.
>
> Orice ai dori, alege o imagine care să fie caracteristică pentru acea acțiune și proiecteaz-o pe acest nou ecran.
>
> Când o privești acum în minte această nouă scenă, îți creează poate emoții contrare, pe de o parte te atrage, ai vrea să mergi spre ea și să faci acțiunea, de altă parte ceva nu te atrage suficient de mult, nu te motivează.
>
> Ce o să facem în continuare, este să încărcăm cu motivație chiar și această imagine pe care creierul tău o percepe lipsită de motivație.
>
> Vreau acum să te relaxezi, chiar și mai mult, și să fii atent la cele două ecrane suprapuse din interiorul minții tale, și să îți folosești imaginația și creativitatea, privind aceste ecrane, privind pe ecranul de deasupra.
>
> Și o să te rog acum să creezi cu puterea minții și a imaginației tale o fissură chiar în centrul acestei imagini, chiar în centrul ecranului, fissură pe care să o lărgești, suficient de mult, încât prin ea să se vadă clar și complet, scena din spate, scena cu plăcerea imensă pe care ai descris-o inițial.
>
> Foarte bine, acum închide rapid acea fissură și recreează imaginea inițială, privește acum iarăși scena pentru care vrei să creezi motivație, și observă cum s-a schimbat, ușor, starea de plăcere, pe care o ai în timp ce privești această scenă.
>
> Folosește-ți iarăși imaginația și creează o fissură în centrul ei, în centrul imaginii, suficient de mare încât prin ea să se vadă complet și clar, scena care îți provoca plăcerea și motivația.
>
> Foarte bine, închide iarăși, imaginează-ți fissura și privește iarăși acea scenă, acțiunea pentru care vrei să îți crești motivația și observă cum s-a schimbat emoția.
>
> Folosește-ți încă o dată pentru a treia oară imaginația și creează iarăși o fissură în mijlocul ecranului mental, suficient de mare încât să vezi prin ea, scena care pentru tine este plină de plăcere și motivație.
>
> Foarte bine, închide acum rapid fissura și privește cum această... doua scenă care în acest moment o simți foarte plăcută și motivată.
>
> Creierul tău o percepe ca fiind plină de motivație, pentru mintea ta, pentru creierul tău această imagine, conține, foarte multă plăcere și motivație.
>
> E posibil chiar din acest moment, după ce vei deschide ochii să simți nevoia să faci acea acțiune.
>
> Păstrează pentru câteva momente o stare de relaxare și o să te rog acum să lași să curgă, spre viitorul tău, toată această emoție pe care ai legat-o de această acțiune.
>
> Lasă să curgă această emoție, acest entuziasm, motivația, de-a lungul secundelor tale viitoare, de-a lungul minutelor, de-a lungul orelor, de-a lungul zilelor, de oricât de mult ai nevoie.
>
> Și simte cum această acțiune rămâne acum definitiv legată de această stare de plăcere, de-a lungul întregii tale vieți.
>
> Să fii cine poți deschide ochii.
---
## Când să folosești
- Când ai o acțiune importantă dar nu găsești motivația
- La începutul zilei, pentru a te pregăti mental
- Înainte de task-uri pe care le tot amâni
- În grup sprijin, ca exercițiu de auto-motivare
## Note
- Tehnica e din NLP (Neuro-Linguistic Programming)
- Similar cu "ancorarea" - leagă o stare emoțională de un trigger
- Funcționează mai bine cu practică repetată

View File

@@ -0,0 +1,386 @@
# Proiect: Import Bonuri Fiscale via Telegram/WhatsApp → ROA
**Data:** 2026-02-03
**Status:** Analiză completă
**Tags:** @work @project
---
## TL;DR
Sistem pentru importul bonurilor fiscale de achiziție din Telegram/WhatsApp în contabilitatea ROA. OCR prin Doctr (cost zero, local). Integrare cu Oracle prin proceduri existente (PACK_CONTAFIN).
---
## 1. Obiective
- **Input:** Bonuri fiscale trimise pe Telegram/WhatsApp (PDF/imagine)
- **Procesare:** OCR local (Doctr) - cost zero
- **Output:** Note contabile în Oracle (ACT, BAL, etc.)
- **Aprobare:** Opțional - contabil verifică înainte de salvare finală
---
## 2. Arhitectură
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Telegram │ │ │ │ │ │ │
│ WhatsApp │────▶│ Clawdbot │────▶│ roa2web │────▶│ ROA Oracle │
└─────────────┘ │ (Bridge) │ │ (OCR+API) │ │ (PACK_ │
└─────────────┘ └─────────────┘ │ CONTAFIN) │
│ │ └─────────────┘
▼ ▼ │
• Primește fișier • Doctr OCR • ACT_TEMP
• Forward la API • PDF text layer • ACT, BAL
• Răspuns user • JSON extrase • IREG_PART
```
---
## 3. Componente Existente
### 3.1 roa2web (LXC 171 - claude-agent)
| Component | Status | Locație |
|-----------|--------|---------|
| **OCR Engine** | ✅ Complet | `/workspace/roa2web/backend/modules/data_entry/services/ocr_engine.py` |
| **Doctr** | ✅ Instalat | PyTorch backend, db_resnet50 + crnn_vgg16_bn |
| **PaddleOCR** | ✅ Instalat | Backup engine |
| **API OCR** | ✅ Funcțional | `POST /api/data-entry/ocr/extract` |
| **Job Queue** | ✅ Complet | Async processing cu worker pool |
| **Bot Telegram** | ⚠️ Parțial | Dashboard/sold, NU handler fișiere |
### 3.2 Schema Date OCR (ExtractionData)
```python
ExtractionData:
partner_name: str # nume furnizor
cui: str # CUI furnizor
receipt_date: date # data bon
receipt_number: str # număr bon
receipt_series: str # serie bon
amount: Decimal # total
tva_entries: List[TvaEntry] # TVA pe cote (A=19%, B=9%, etc.)
tva_total: Decimal # total TVA
payment_methods: List # CARD/NUMERAR
address: str # adresă furnizor
confidence_*: float # scoruri încredere
```
### 3.3 Oracle (MARIUSM_AUTO)
**Tabele principale:**
| Tabel | Scop |
|-------|------|
| **ACT** | Note contabile |
| **ACT_TEMP** | Staging pentru note |
| **RUL** | Rulaje gestiuni |
| **STOC** | Solduri stoc |
| **BAL** | Balanță verificare |
| **IREG_PARTENERI** | Evidență parteneri |
| **BALANTA_PARTENERI** | Balanță parteneri |
| **ATAS_ATASAMENTE** | Fișiere atașate (BLOB) |
| **ATAS_REFERINTE** | Legături atașamente |
| **NOM_FDOC** | Nomenclator tipuri documente |
**Package-uri relevante:**
| Package | Scop |
|---------|------|
| **PACK_CONTAFIN** | Salvare note contabile (principal) |
| **PACK_FACTURARE** | Exemple de salvare (pentru vânzări) |
| **PACK_PARTENERI** | Gestiune parteneri |
---
## 4. Formula Contabilă - Bon Fiscal Achiziție
### 4.1 Bon simplu (plătit cash)
```
Cheltuială = Furnizor: 6xx = 401 (valoare fără TVA)
TVA deductibil = Furn: 4426 = 401 (TVA)
Plată cash: 401 = 5311 (total cu TVA)
```
**Exemplu bon benzină 119 RON (100 + 19 TVA):**
```sql
-- Linia 1: Cheltuială combustibil
SCD='6022', SCC='401', SUMA=100.00
-- Linia 2: TVA deductibil
SCD='4426', SCC='401', SUMA=19.00, PROC_TVA=1.19
-- Linia 3: Plată din casă
SCD='401', SCC='5311', SUMA=119.00
```
### 4.2 Bon cu TVA la încasare
```
Cheltuială = Furnizor: 6xx = 401
TVA neexigibil = Furn: 4428 = 401
Plată cash: 401 = 5311
Exigibilizare TVA: 4426 = 4428
```
### 4.3 Conturi cheltuieli uzuale
| Tip | Cont | Descriere |
|-----|------|-----------|
| Combustibil | 6022 | Combustibili |
| Materiale | 6021 | Materiale auxiliare |
| Rechizite | 6021 | Materiale auxiliare |
| Piese | 6024 | Piese schimb |
| Întreținere | 611 | Cheltuieli întreținere |
| Transport | 624 | Cheltuieli transport |
---
## 5. Flux Oracle - Salvare Note Contabile
### 5.1 Proceduri PACK_CONTAFIN
```sql
-- 1. INIȚIALIZARE SESIUNE
PACK_CONTAFIN.INITIALIZEAZA_SCRIERE_ACT_RUL(
TNIDUTIL NUMBER, -- ID utilizator
TDDATAORA DATE, -- Data și ora
TNAN NUMBER, -- An
TNLUNA NUMBER, -- Luna
TNSUPRASCRIERECOD NUMBER, -- 0/1
TNSUPRASCRIEREANLUNA NUMBER, -- 0/1
TNSCRIESTERGE NUMBER, -- 0=scrie, 2=șterge
TNIDSUCURSALA NUMBER -- ID sucursală
);
-- 2. INSERT ÎN ACT_TEMP (pentru fiecare linie)
INSERT INTO ACT_TEMP (
LUNA, AN, COD,
DATAIREG, DATAACT, DATASCAD,
NRACT, SERIE_ACT,
EXPLICATIA,
SCD, ASCD, -- cont debit, analitic
SCC, ASCC, -- cont credit, analitic
SUMA,
PROC_TVA,
ID_FDOC, -- tip document
ID_PARTD, ID_PARTC, -- parteneri
ID_FACT,
ID_VALUTA,
ID_SUCURSALA
) VALUES (...);
-- 3. FINALIZARE (transferă în ACT, actualizează BAL, etc.)
PACK_CONTAFIN.FINALIZEAZA_SCRIERE_ACT_RUL(
TNIDUTIL NUMBER, -- ID utilizator
TNCOD NUMBER, -- Cod document
TNSCRIESTERGE NUMBER, -- 0=scrie
TNMODIFICARENOTA NUMBER, -- 0/1
TNSCRIECUMPVANZ NUMBER, -- 0/1
TCMESAJREFACERENOTA OUT VARCHAR2 -- Mesaj rezultat
);
```
### 5.2 Ce face FINALIZEAZA intern
```sql
-- La linia 8059 în PACK_CONTAFIN:
pack_contafin.SCRIE_IN_ACT(user); -- ACT_TEMP → ACT + BAL
pack_contafin.SCRIE_IN_RUL(user); -- RUL_TEMP → RUL (dacă există)
pack_contafin.SCRIE_IN_RUL_OBINV(user); -- pentru obiecte inventar
```
---
## 6. Structura ACT_TEMP
```sql
ACT_TEMP (
LUNA NUMBER,
AN NUMBER,
COD NUMBER,
DATAIREG DATE,
NRACT NUMBER,
DATAACT DATE,
EXPLICATIA VARCHAR2(100),
SCD VARCHAR2(10), -- Cont DEBIT
ASCD VARCHAR2(20), -- Analitic debit
SCC VARCHAR2(10), -- Cont CREDIT
ASCC VARCHAR2(20), -- Analitic credit
SUMA NUMBER,
PROC_TVA NUMBER, -- Procent TVA (1.19, 1.09, etc.)
ID_FDOC NUMBER, -- Tip document (din NOM_FDOC)
ID_PARTD NUMBER, -- Partner debit
ID_PARTC NUMBER, -- Partner credit
ID_FACT NUMBER, -- ID factură (opțional)
ID_VALUTA NUMBER,
ID_SUCURSALA NUMBER,
SERIE_ACT VARCHAR2(10),
TVA_INCASARE NUMBER, -- 0/1 pentru TVA la încasare
...
)
```
---
## 7. Optimizări Propuse
### 7.1 PDF Text Layer Extraction
Adobe Scan creează PDF-uri cu text layer. Verificăm înainte de OCR:
```python
import pdfplumber
def extract_text_or_ocr(pdf_path):
with pdfplumber.open(pdf_path) as pdf:
text = ""
for page in pdf.pages:
page_text = page.extract_text()
if page_text:
text += page_text
if text.strip():
# PDF are text layer - folosește-l direct
return {"source": "pdf_layer", "text": text}
else:
# Nu are text - fallback la Doctr OCR
return doctr_ocr(pdf_path)
```
**Beneficii:**
- Instant vs. 2-5 sec OCR
- 100% acuratețe vs. ~95% OCR
- Zero procesare GPU
### 7.2 Mapare CUI → Cont
Tabel pentru auto-detectare tip cheltuială:
```python
CUI_CONT_MAP = {
# Benzinării
"RO1234567": "6022", # OMV
"RO2345678": "6022", # Petrom
"RO3456789": "6022", # Mol
# Magazine bricolaj
"RO10562600": "6021", # Dedeman
# Default
"DEFAULT": "6028" # Alte cheltuieli
}
```
---
## 8. Status Implementare (2026-02-03)
| Component | Status | Note |
|-----------|--------|------|
| OCR (Doctr/PaddleOCR) | ✅ Testat | 34 sec per PDF |
| Script OCR standalone | ✅ Funcțional | `/workspace/roa2web-telegram-test/` |
| Python: INSERT ACT_TEMP | ✅ Testat | 3 linii: cheltuială + TVA + plată |
| Mapare CUI → cont | ✅ Bază | 6022=combustibil, 6021=materiale |
| Găsire partener | ✅ Funcțional | Caută în NOM_PARTENERI |
| Conexiune Oracle | ✅ Testat | Server 10.0.20.121 |
| Handler WhatsApp | 🔄 În pregătire | Clawdbot poate primi fișiere |
| CALL PACK_CONTAFIN | ⏳ TODO | Finalizare cu procedurile Oracle |
| UI aprobare | ⏳ TODO | Opțional |
### Scripturi create:
- `/workspace/roa2web-telegram-test/process_and_save.py` - OCR → Oracle
- `/workspace/roa2web-telegram-test/save_to_oracle.py` - test salvare Oracle
### Test efectuat:
```
PDF "benzina 07 aug. 2024.pdf" → OCR → Oracle ACT_TEMP
CUI: RO11201891
Data: 2024-08-01
Total: 263.28 RON
TVA: 42.04 (19%)
Note: 6022=401, 4426=401, 401=5311
Status: DRY RUN OK
```
---
## 9. Plan Implementare
### Faza 1: Handler Telegram (3-4h)
- [ ] MessageHandler pentru imagini/PDF în bot
- [ ] Forward la `/api/data-entry/ocr/extract`
- [ ] Răspuns cu datele extrase (preview)
### Faza 2: PDF Optimization (1-2h)
- [ ] Verificare text layer în PDF
- [ ] Extracție directă dacă există
- [ ] Fallback la Doctr
### Faza 3: Integrare Oracle (5-7h)
- [ ] Conexiune Oracle din roa2web
- [ ] Găsire/creare partener după CUI
- [ ] INSERT în ACT_TEMP
- [ ] Apel PACK_CONTAFIN proceduri
- [ ] Salvare atașament (ATAS_ATASAMENTE)
### Faza 4: WhatsApp Bridge (4-6h)
- [ ] Clawdbot handler pentru fișiere
- [ ] Forward la roa2web API
- [ ] Răspuns pe canal
### Faza 5: Aprobare & Polish (3-5h)
- [ ] Workflow aprobare (opțional)
- [ ] Mapare automată CUI → cont
- [ ] Logging și error handling
---
## 10. Credențiale Test
**Oracle Test Server:**
```
Host: 10.0.20.121
Port: 1521
SID: ROA
User: CONTAFIN_ORACLE
Pass: (în secrets/test.oracle_pass)
Schema: MARIUSM_AUTO
```
**roa2web:**
```
LXC: 171 (claude-agent)
Path: /workspace/roa2web
Start: ./start.sh test
```
---
## 11. Fișiere Proiect
```
roa2web-telegram-import/
├── README.md # Acest fișier
├── schema-oracle.md # Detalii schema Oracle
├── flux-contabil.md # Formule contabile detaliate
├── api-spec.md # Specificații API (de creat)
└── implementation-notes.md # Note implementare (de creat)
```
---
## 12. Întrebări Deschise
1. **Workflow aprobare:** E necesar ca un contabil să aprobe înainte de salvare?
2. **Tip document (ID_FDOC):** Ce valoare pentru bonuri fiscale achiziție?
3. **Analitice:** Sunt necesare analitice pe conturi (ASCD, ASCC)?
4. **Sucursală:** Valoare default pentru ID_SUCURSALA?
5. **Atașamente:** Unde se face legătura ATAS_REFERINTE (ID_ENTITATE = ID_ACT)?
---
*Analiză generată de Echo • 2026-02-03*

View File

@@ -0,0 +1,229 @@
# Flux Contabil - Import Bonuri Fiscale Achiziție
**Actualizat:** 2026-02-03
---
## 1. Formula Contabilă Generală
### Bon fiscal achiziție (plătit cash)
```
┌─────────────────────────────────────────────────────────────┐
│ DEBIT │ CREDIT │ SUMA │ DESCRIERE │
├──────────────────┼─────────────────┼──────────┼─────────────┤
│ 6xx (cheltuială)│ 401 (furnizor) │ fără TVA│ Cheltuială │
│ 4426 (TVA ded.) │ 401 (furnizor) │ TVA │ TVA deduct.│
│ 401 (furnizor) │ 5311 (casa) │ cu TVA │ Plată cash │
└──────────────────┴─────────────────┴──────────┴─────────────┘
```
### Bon fiscal cu TVA la încasare
```
┌─────────────────────────────────────────────────────────────┐
│ DEBIT │ CREDIT │ SUMA │ DESCRIERE │
├──────────────────┼─────────────────┼──────────┼─────────────┤
│ 6xx (cheltuială)│ 401 (furnizor) │ fără TVA│ Cheltuială │
│ 4428 (TVA neex.)│ 401 (furnizor) │ TVA │ TVA neexig.│
│ 401 (furnizor) │ 5311 (casa) │ cu TVA │ Plată cash │
│ 4426 (TVA ded.) │ 4428 (TVA neex)│ TVA │ Exigibiliz.│
└──────────────────┴─────────────────┴──────────┴─────────────┘
```
---
## 2. Conturi Cheltuieli (6xx)
| Cont | Denumire | Utilizare |
|------|----------|-----------|
| 6021 | Cheltuieli cu materialele auxiliare | Rechizite, materiale consumabile |
| 6022 | Cheltuieli privind combustibilii | Benzină, motorină, GPL |
| 6024 | Cheltuieli privind piesele de schimb | Piese auto, componente |
| 6028 | Cheltuieli privind alte materiale consumabile | Diverse materiale |
| 604 | Cheltuieli privind materialele nestocate | Materiale directe în consum |
| 611 | Cheltuieli cu întreținerea și reparațiile | Service, reparații |
| 612 | Cheltuieli cu redevențele | Licențe, abonamente |
| 613 | Cheltuieli cu primele de asigurare | Asigurări |
| 614 | Cheltuieli cu studiile și cercetările | Consultanță |
| 622 | Cheltuieli privind comisioanele | Comisioane bancare |
| 623 | Cheltuieli de protocol, reclamă | Protocol, publicitate |
| 624 | Cheltuieli cu transportul | Transport bunuri/persoane |
| 625 | Cheltuieli cu deplasările | Diurnă, cazare, transport |
| 626 | Cheltuieli poștale și telecomunicații | Telefon, internet |
| 627 | Cheltuieli cu serviciile bancare | Comisioane bancare |
| 628 | Alte cheltuieli cu serviciile | Diverse servicii |
---
## 3. Mapare Automată CUI → Cont
### Benzinării
| CUI | Denumire | Cont |
|-----|----------|------|
| RO1590082 | OMV PETROM | 6022 |
| RO1025tried | ROMPETROL | 6022 |
| RO14991381 | MOL ROMANIA | 6022 |
| RO524186 | LUKOIL | 6022 |
### Magazine Bricolaj
| CUI | Denumire | Cont |
|-----|----------|------|
| RO10562600 | DEDEMAN | 6021/6028 |
| RO6323780 | HORNBACH | 6021/6028 |
| RO14688790 | LEROY MERLIN | 6021/6028 |
### Supermarket-uri
| CUI | Denumire | Cont |
|-----|----------|------|
| RO15tried | MEGA IMAGE | 6028 |
| RO5765323 | KAUFLAND | 6028 |
| RO3891449 | CARREFOUR | 6028 |
| RO6334171 | LIDL | 6028 |
---
## 4. Exemplu Complet - Bon Benzină
**Bon:**
- Furnizor: OMV PETROM (CUI: RO1590082)
- Data: 2026-02-03
- Număr bon: 12345
- Total: 250.00 RON (210.08 + 39.92 TVA 19%)
### 4.1 Date extrase OCR
```json
{
"partner_name": "OMV PETROM MARKETING SRL",
"cui": "RO1590082",
"receipt_date": "2026-02-03",
"receipt_number": "12345",
"amount": 250.00,
"tva_entries": [
{"code": "A", "percent": 19, "amount": 39.92}
],
"tva_total": 39.92,
"payment_methods": [{"method": "NUMERAR", "amount": 250.00}]
}
```
### 4.2 Note Contabile Generate
```sql
-- LINIA 1: Cheltuială combustibil
INSERT INTO ACT_TEMP (
LUNA, AN, COD, DATAIREG, DATAACT, NRACT,
EXPLICATIA,
SCD, SCC, SUMA,
ID_PARTC, ID_FDOC
) VALUES (
2, 2026, :cod, SYSDATE, TO_DATE('2026-02-03', 'YYYY-MM-DD'), 12345,
'Combustibil OMV bon 12345',
'6022', '401', 210.08,
:id_part_omv, :id_fdoc_bon
);
-- LINIA 2: TVA deductibil
INSERT INTO ACT_TEMP (
LUNA, AN, COD, DATAIREG, DATAACT, NRACT,
EXPLICATIA,
SCD, SCC, SUMA, PROC_TVA,
ID_PARTC, ID_FDOC
) VALUES (
2, 2026, :cod, SYSDATE, TO_DATE('2026-02-03', 'YYYY-MM-DD'), 12345,
'TVA combustibil OMV bon 12345',
'4426', '401', 39.92, 1.19,
:id_part_omv, :id_fdoc_bon
);
-- LINIA 3: Plată din casă
INSERT INTO ACT_TEMP (
LUNA, AN, COD, DATAIREG, DATAACT, NRACT,
EXPLICATIA,
SCD, SCC, SUMA,
ID_PARTD, ID_FDOC
) VALUES (
2, 2026, :cod, SYSDATE, TO_DATE('2026-02-03', 'YYYY-MM-DD'), 12345,
'Plata bon OMV 12345',
'401', '5311', 250.00,
:id_part_omv, :id_fdoc_bon
);
```
### 4.3 Flux Complet Python
```python
import oracledb
def import_bon_achizitie(extraction_data, conn):
cursor = conn.cursor()
# 1. Găsește sau creează partenerul
id_part = find_or_create_partner(cursor, extraction_data['cui'], extraction_data['partner_name'])
# 2. Determină contul de cheltuială
cont_cheltuiala = get_cont_by_cui(extraction_data['cui']) # ex: '6022'
# 3. Calculează sumele
total_cu_tva = extraction_data['amount']
tva = extraction_data['tva_total']
total_fara_tva = total_cu_tva - tva
# 4. Inițializare
cursor.callproc('MARIUSM_AUTO.PACK_CONTAFIN.INITIALIZEAZA_SCRIERE_ACT_RUL', [
id_util, # ID utilizator
datetime.now(),
an,
luna,
0, # suprascriere_cod
0, # suprascriere_anluna
0, # scrie_sterge (0=scrie)
id_sucursala
])
# 5. INSERT în ACT_TEMP (cele 3 linii)
# ... INSERT-uri ca mai sus ...
# 6. Finalizare
mesaj = cursor.var(oracledb.STRING)
cursor.callproc('MARIUSM_AUTO.PACK_CONTAFIN.FINALIZEAZA_SCRIERE_ACT_RUL', [
id_util,
cod,
0, # scrie_sterge
0, # modificare_nota
0, # scrie_cump_vanz
mesaj
])
conn.commit()
return mesaj.getvalue()
```
---
## 5. Cote TVA România
| Cotă | Procent | PROC_TVA | Aplicare |
|------|---------|----------|----------|
| Standard | 19% | 1.19 | Majoritatea bunurilor/serviciilor |
| Redus 1 | 9% | 1.09 | Alimente, medicamente, cărți |
| Redus 2 | 5% | 1.05 | Locuințe sociale |
| Zero | 0% | 1.00 | Export, anumite servicii |
---
## 6. Validări Necesare
1. **CUI valid** - verificare checksum
2. **Data bon** - nu în viitor, nu mai veche de 1 an
3. **Sumă** - pozitivă, format corect
4. **TVA** - să corespundă cu cotele legale
5. **Balanță** - SUMA(debit) = SUMA(credit)
---
*Flux documentat de Echo • 2026-02-03*

View File

@@ -0,0 +1,275 @@
# Schema Oracle - ROA (MARIUSM_AUTO)
**Actualizat:** 2026-02-03
---
## Tabele Principale
### ACT - Note Contabile
```sql
ACT (
ID_ACT NUMBER PRIMARY KEY,
LUNA NUMBER,
AN NUMBER,
COD NUMBER, -- Cod document unic
DATAIREG DATE, -- Data înregistrare
NRACT NUMBER, -- Număr act
DATAACT DATE, -- Data act
EXPLICATIA VARCHAR2(100), -- Descriere
SCD VARCHAR2(10), -- Cont DEBIT
ASCD VARCHAR2(20), -- Analitic debit
SCC VARCHAR2(10), -- Cont CREDIT
ASCC VARCHAR2(20), -- Analitic credit
SUMA NUMBER, -- Sumă
ID_FACTD NUMBER, -- Factură debit
ID_FACTC NUMBER, -- Factură credit
PERECHED NUMBER,
PERECHEC NUMBER,
SUMA_VAL NUMBER, -- Sumă valută
CURS NUMBER, -- Curs valutar
DATASCAD DATE, -- Data scadență
ID_UTIL NUMBER, -- Utilizator creare
DATAORA DATE, -- Data/ora creare
ID_UTILS NUMBER, -- Utilizator modificare
DATAORAS DATE, -- Data/ora modificare
TVA_INCASARE NUMBER, -- Flag TVA la încasare
SERIE_ACT VARCHAR2(10), -- Serie act
ID_PART NUMBER, -- ID partener
ID_CTR NUMBER, -- ID contract
CONT VARCHAR2(10),
ACONT VARCHAR2(20),
ID_VALUTA NUMBER,
ID_SUCURSALA NUMBER,
ID_SET NUMBER,
TIP_SAFT NUMBER
)
```
### ACT_TEMP - Staging Note Contabile
Aceeași structură ca ACT, folosită pentru inserare temporară înainte de transfer.
### RUL - Rulaje Gestiuni
```sql
RUL (
ID_RUL NUMBER PRIMARY KEY,
AN NUMBER,
LUNA NUMBER,
COD NUMBER,
NNIR NUMBER, -- Număr NIR
ID_ARTICOL NUMBER,
ID_GESTIUNE NUMBER,
PRET NUMBER,
PRETV NUMBER, -- Preț vânzare
TVA NUMBER,
TVAV NUMBER,
CANT NUMBER, -- Cantitate
CANTE NUMBER,
CONT VARCHAR2(10),
ACONT VARCHAR2(20),
ADAOS NUMBER,
ID_LUCRARE NUMBER,
ID_RESPONSABIL NUMBER,
DATAACT DATE,
PROCENT NUMBER,
CURS NUMBER,
PRETD NUMBER,
ID_UTIL NUMBER,
DATAORA DATE,
ID_SECTIE NUMBER,
PROC_TVA NUMBER,
ID_FACT NUMBER,
STERS NUMBER,
ID_TIP_RULAJ NUMBER, -- 0=intrare/ieșire reală
ID_SET NUMBER,
ID_SUCURSALA NUMBER,
NRACT NUMBER,
SERIE_ACT VARCHAR2(10),
ID_VALUTA NUMBER,
LOT VARCHAR2(50),
ADATA_EXPIRARE DATE,
...
)
```
### NOM_TIP_RULAJ - Tipuri Rulaj
```sql
(0, 'INTRARE/IESIRE REALA')
(1, 'TRANSFER')
(2, 'RULAJ CMP')
(6, 'INVENTARIERE')
```
### STOC - Stocuri
```sql
STOC (
ID_STOC NUMBER PRIMARY KEY,
AN NUMBER,
LUNA NUMBER,
ID_ARTICOL NUMBER,
PRET NUMBER,
PRETV NUMBER,
TVA NUMBER,
TVAV NUMBER,
CANTS NUMBER, -- Cantitate sold
CANT NUMBER,
CANTE NUMBER,
CONT VARCHAR2(10),
ID_GESTIUNE NUMBER,
...
)
```
### BAL - Balanță Verificare
```sql
BAL (
ID_BAL NUMBER PRIMARY KEY,
CONT VARCHAR2(10),
PRECDEB1 NUMBER, -- Precedent debit (an)
PRECCRED1 NUMBER,
PRECDEB NUMBER, -- Precedent debit (lună)
PRECCRED NUMBER,
RULDEB NUMBER, -- Rulaj debit
RULCRED NUMBER, -- Rulaj credit
AN NUMBER,
LUNA NUMBER,
ID_SUCURSALA NUMBER
)
```
### IREG_PARTENERI - Evidență Parteneri
```sql
IREG_PARTENERI (
ID_IREG_PART NUMBER PRIMARY KEY,
AN NUMBER,
LUNA NUMBER,
ID_PART NUMBER,
CONT VARCHAR2(10),
ACONT VARCHAR2(20),
DEBIT NUMBER,
CREDIT NUMBER,
VALDEBIT NUMBER,
VALCREDIT NUMBER,
CURS NUMBER,
ID_FDOC NUMBER, -- Tip document
DATAACT DATE,
DATAIREG DATE,
DATASCAD DATE,
NRACT NUMBER,
ID_RESPONSABIL NUMBER,
PRECDEB NUMBER,
PRECCRED NUMBER,
...
)
```
### ATAS_ATASAMENTE - Fișiere Atașate
```sql
ATAS_ATASAMENTE (
ID_ATAS NUMBER PRIMARY KEY,
NUME_FISIER VARCHAR2(200),
FISIER BLOB, -- Conținut fișier
ID_UTIL NUMBER,
DATAORA DATE,
STERS NUMBER,
ID_UTILS NUMBER,
DATAORAS DATE,
DESCRIERE VARCHAR2(500),
ID_LINK NUMBER,
ID_TIP_ENT_PREL NUMBER
)
```
### ATAS_REFERINTE - Legături Atașamente
```sql
ATAS_REFERINTE (
ID_REFERINTA NUMBER PRIMARY KEY,
ID_ATAS NUMBER, -- FK la ATAS_ATASAMENTE
ID_ENTITATE NUMBER, -- ID din tabela referită
ID_UTIL NUMBER,
DATAORA DATE,
STERS NUMBER,
ID_UTILS NUMBER,
DATAORAS DATE,
ID_PROGRAM NUMBER, -- Identificator tabel/modul
ORDINE NUMBER
)
```
### NOM_FDOC - Nomenclator Tipuri Documente
```sql
NOM_FDOC (
ID_FDOC NUMBER PRIMARY KEY,
FEL_DOCUMENT VARCHAR2(50),
STERS NUMBER,
ID_MOD NUMBER,
INACTIV NUMBER,
TIP_DOC VARCHAR2(5),
PAYMENTCODE VARCHAR2(10)
)
-- Exemple:
(1, 'ABONAMENT')
(3, 'AVANSURI')
(5, 'AVIZ EXP')
(6, 'BILET')
...
```
---
## Package-uri Oracle
### PACK_CONTAFIN (principal)
**Proceduri principale:**
- `INITIALIZEAZA_SCRIERE_ACT_RUL` - Inițializare sesiune
- `SCRIE_IN_ACT` - Transfer ACT_TEMP → ACT
- `SCRIE_IN_BALANTA` - Actualizare balanță
- `SCRIE_IN_RUL` - Transfer RUL_TEMP → RUL
- `SCRIE_IN_STOC` - Actualizare stoc
- `SCRIE_IN_IREG_PARTENERI` - Actualizare evidență parteneri
- `FINALIZEAZA_SCRIERE_ACT_RUL` - Finalizare (apelează toate cele de mai sus)
### PACK_FACTURARE (referință)
**Exemple INSERT ACT_TEMP:**
- Liniile 1000-1200 în package body
- Model pentru structura datelor
- NU pentru achiziții, dar util ca referință
### PACK_PARTENERI
- Gestiune parteneri (furnizori, clienți)
- Căutare/creare partener după CUI
---
## Conexiune Python
```python
import oracledb
conn = oracledb.connect(
user="CONTAFIN_ORACLE",
password="ROMFASTSOFT",
dsn="10.0.20.121:1521/ROA"
)
# Toate operațiile folosesc schema MARIUSM_AUTO
cursor.execute("SELECT * FROM MARIUSM_AUTO.ACT WHERE ROWNUM <= 1")
```
---
*Schema documentată de Echo • 2026-02-03*

588
tools/process_bon.py Normal file
View File

@@ -0,0 +1,588 @@
#!/usr/bin/env python3
"""
Procesare bon fiscal: PDF → OCR API → SQLite API → Oracle
Usage:
python process_bon.py <pdf_path> [--save]
--save Salvează efectiv în Oracle (altfel dry run)
Fluxul:
1. OCR extract via API (http://10.0.20.171:8000/api/data-entry/ocr/extract)
2. Save receipt via API (http://10.0.20.171:8000/api/data-entry/receipts/) - TOATE datele
3. Save to Oracle:
- Verifică/creează partener
- Verifică TVA la încasare (CALENDAR.TVA_INCASARE)
- Generează note contabile corecte
"""
import sys
import json
import time
import argparse
from pathlib import Path
from datetime import datetime
from decimal import Decimal
import requests
import oracledb
# === CONFIG ===
API_BASE = "http://10.0.20.171:8000"
API_USER = "MARIUS M"
API_PASS = "123"
SERVER_ID = "central"
COMPANY_ID = 110 # MARIUSM AUTO
ORACLE_CONFIG = {
"user": "MARIUSM_AUTO",
"password": "ROMFASTSOFT",
"dsn": "10.0.20.121:1521/ROA"
}
# Mapare CUI → cont cheltuială
CUI_TO_CONT = {
"11201891": "6022", # MOL
"1590082": "6022", # OMV Petrom
"14991381": "6022", # Rompetrol
"10562600": "6021", # Dedeman / Five Holding (Brick)
"1879865": "6021", # Five Holding
}
# Mapare cotă TVA → (ID_JTVA baza, ID_JTVA tva, TAXCODE, TAXCODE_TVAI)
# Pentru achiziții interne neexigibile (TVA la încasare)
JTVA_NEEX = {
21: (210, 211, 301104, 301305), # ACH. INT. NEEX. 21%
19: (188, 189, 301101, 301301), # ACH. INT. NEEX. 19%
11: (214, 215, 301105, 301306), # ACH. INT. NEEX. 11%
9: (172, 173, 301102, 301302), # ACH. INT. NEEX. 9%
5: (174, 175, 301103, 301303), # ACH. INT. NEEX. 5%
}
# Pentru achiziții interne normale (fără TVA la încasare)
JTVA_NORMAL = {
21: (208, 209, 301104, 301305), # ACH. INT. 21%
19: (None, None, 301101, 301301),
9: (None, None, 301102, 301302),
}
def get_cont(cui: str) -> str:
"""Mapare CUI → cont cheltuială."""
cui_clean = (cui or "").upper().replace("RO", "").strip()
return CUI_TO_CONT.get(cui_clean, "6028") # 6028 = alte cheltuieli
class APIClient:
"""Client pentru roa2web API."""
def __init__(self, base_url: str):
self.base_url = base_url.rstrip("/")
self.token = None
self.session = requests.Session()
def login(self, username: str, password: str, server_id: str) -> bool:
"""Login și obține token."""
r = self.session.post(
f"{self.base_url}/api/auth/login",
json={"username": username, "password": password, "server_id": server_id}
)
if r.status_code == 200:
data = r.json()
self.token = data.get("access_token")
self.session.headers["Authorization"] = f"Bearer {self.token}"
return True
print(f"Login failed: {r.status_code} - {r.text}")
return False
def ocr_extract(self, file_path: Path) -> dict:
"""Submit OCR job și așteaptă rezultatul."""
# Determine mime type
suffix = file_path.suffix.lower()
if suffix == ".pdf":
mime_type = "application/pdf"
elif suffix in (".jpg", ".jpeg"):
mime_type = "image/jpeg"
elif suffix == ".png":
mime_type = "image/png"
else:
# Try to detect from content
with open(file_path, "rb") as f:
header = f.read(8)
if header[:4] == b'%PDF':
mime_type = "application/pdf"
suffix = ".pdf"
elif header[:3] == b'\xff\xd8\xff':
mime_type = "image/jpeg"
suffix = ".jpg"
elif header[:8] == b'\x89PNG\r\n\x1a\n':
mime_type = "image/png"
suffix = ".png"
else:
mime_type = "application/pdf" # default
suffix = ".pdf"
# Use proper filename with extension
filename = file_path.stem + suffix if not file_path.suffix else file_path.name
# Submit
with open(file_path, "rb") as f:
r = self.session.post(
f"{self.base_url}/api/data-entry/ocr/extract",
files={"file": (filename, f, mime_type)}
)
if r.status_code != 200:
return {"success": False, "error": f"OCR submit failed: {r.text}"}
job_id = r.json().get("job_id")
print(f" OCR job: {job_id}")
# Wait for result (max 60s per request, retry if pending)
for _ in range(4): # Max 4 retries = ~240s total
r = self.session.get(
f"{self.base_url}/api/data-entry/ocr/jobs/{job_id}/wait",
params={"timeout": 60, "wait_for_terminal": "true"},
timeout=70
)
if r.status_code != 200:
return {"success": False, "error": f"OCR wait failed: {r.text}"}
data = r.json()
status = data.get("status")
if status == "completed":
return {"success": True, "result": data.get("result"), "time_ms": data.get("processing_time_ms")}
elif status == "failed":
return {"success": False, "error": data.get("error") or "OCR failed"}
# Still pending/processing - will retry
return {"success": False, "error": "OCR timeout"}
def create_receipt(self, ocr_result: dict, company_id: int) -> dict:
"""Creează receipt în SQLite via API cu TOATE datele."""
# Parse date
date_str = ocr_result.get("receipt_date")
if date_str:
receipt_date = date_str[:10] # YYYY-MM-DD
else:
receipt_date = datetime.now().strftime("%Y-%m-%d")
# Build TVA breakdown from OCR
tva_breakdown = []
for tva_entry in (ocr_result.get("tva_entries") or []):
tva_breakdown.append({
"code": tva_entry.get("code"),
"percent": tva_entry.get("percent"),
"amount": float(tva_entry.get("amount") or 0)
})
# Build payment methods from OCR
payment_methods = []
for pm in (ocr_result.get("payment_methods") or []):
payment_methods.append({
"method": pm.get("method"),
"amount": float(pm.get("amount") or 0)
})
# Determine payment mode
payment_mode = ocr_result.get("suggested_payment_mode") or "casa"
# If has CARD payment, it's "banca"
if any(pm.get("method", "").upper() == "CARD" for pm in payment_methods):
payment_mode = "banca"
elif any(pm.get("method", "").upper() == "NUMERAR" for pm in payment_methods):
payment_mode = "casa"
payload = {
"receipt_type": "bon_fiscal",
"direction": "cheltuiala",
"receipt_number": ocr_result.get("receipt_number"),
"receipt_series": ocr_result.get("receipt_series"),
"receipt_date": receipt_date,
"amount": float(ocr_result.get("amount") or 0),
"partner_name": ocr_result.get("partner_name"),
"cui": ocr_result.get("cui"),
"tva_total": float(ocr_result.get("tva_total") or 0),
"tva_breakdown": tva_breakdown if tva_breakdown else None,
"payment_methods": payment_methods if payment_methods else None,
"payment_mode": payment_mode,
"company_id": company_id,
"vendor_address": ocr_result.get("address"),
"items_count": ocr_result.get("items_count"),
"ocr_raw_text": ocr_result.get("raw_text"),
}
# Remove None values
payload = {k: v for k, v in payload.items() if v is not None}
self.session.headers["X-Selected-Company"] = str(company_id)
r = self.session.post(
f"{self.base_url}/api/data-entry/receipts/",
json=payload
)
if r.status_code in (200, 201):
return {"success": True, "receipt": r.json()}
else:
return {"success": False, "error": f"Create receipt failed: {r.text}"}
def get_or_create_partner(cursor, cui: str, name: str, address: str = None) -> int:
"""Găsește sau creează partener în Oracle. Returnează ID_PART."""
cui_clean = (cui or "").upper().replace("RO", "").strip()
if not cui_clean:
return 0 # No CUI, no partner
# Try to find existing partner
cursor.execute("""
SELECT ID_PART FROM NOM_PARTENERI
WHERE COD_FISCAL = :cui OR COD_FISCAL = :cui2
""", cui=cui_clean, cui2="RO" + cui_clean)
row = cursor.fetchone()
if row:
return row[0] # Found existing partner
# Create new partner
cursor.execute("SELECT SEQ_NOM_PARTENERI.NEXTVAL FROM DUAL")
new_id = cursor.fetchone()[0]
# Clean name
partner_name = (name or f"PARTENER {cui_clean}")[:100]
partner_address = (address or "")[:200]
cursor.execute("""
INSERT INTO NOM_PARTENERI (ID_PART, NUME, COD_FISCAL, ADRESA, STERS, INACTIV)
VALUES (:id_part, :nume, :cui, :adresa, 0, 0)
""", id_part=new_id, nume=partner_name, cui=cui_clean, adresa=partner_address)
print(f" Partener nou creat: ID={new_id}, CUI={cui_clean}, Nume={partner_name}")
return new_id
def check_tva_incasare(cursor, an: int, luna: int) -> bool:
"""Verifică dacă firma e plătitoare de TVA la încasare în perioada dată."""
cursor.execute("""
SELECT NVL(TVA_INCASARE, 0) FROM CALENDAR
WHERE AN = :an AND LUNA = :luna
""", an=an, luna=luna)
row = cursor.fetchone()
return row[0] == 1 if row else False
def save_to_oracle(ocr_result: dict, do_commit: bool = False) -> dict:
"""Salvează nota contabilă în Oracle cu toate regulile."""
conn = oracledb.connect(**ORACLE_CONFIG)
cursor = conn.cursor()
try:
# Parse date
date_str = ocr_result.get("receipt_date")
if date_str:
receipt_date = datetime.strptime(date_str[:10], "%Y-%m-%d").date()
else:
receipt_date = datetime.now().date()
an, luna = receipt_date.year, receipt_date.month
# 1. Get or create partner
id_part = get_or_create_partner(
cursor,
ocr_result.get("cui"),
ocr_result.get("partner_name"),
ocr_result.get("address")
)
print(f" Partner ID: {id_part}")
# 2. Check TVA la încasare
tva_incasare = check_tva_incasare(cursor, an, luna)
cont_tva = "4428" if tva_incasare else "4426"
print(f" TVA la încasare: {'DA (4428)' if tva_incasare else 'NU (4426)'}")
# 3. Determine payment type
payment_methods = ocr_result.get("payment_methods") or []
has_cash = any(pm.get("method", "").upper() == "NUMERAR" for pm in payment_methods)
has_card = any(pm.get("method", "").upper() == "CARD" for pm in payment_methods)
# If no payment info, assume cash
if not payment_methods:
has_cash = True
print(f" Plată: {'NUMERAR' if has_cash else ''}{' + ' if has_cash and has_card else ''}{'CARD' if has_card else ''}")
# 4. Init PACK_CONTAFIN
cursor.callproc('PACK_CONTAFIN.INITIALIZEAZA_SCRIERE_ACT_RUL',
[0, datetime.now(), an, luna, 0, 0, 0, 0])
# 5. Get next COD
cursor.execute(
"SELECT NVL(MAX(COD), 0) + 1 FROM ACT WHERE AN = :an AND LUNA = :luna",
an=an, luna=luna
)
cod = cursor.fetchone()[0]
# 6. Calculate amounts
total = float(ocr_result.get("amount") or 0)
tva = float(ocr_result.get("tva_total") or 0)
fara_tva = total - tva
nract = ocr_result.get("receipt_number", "")
nract = int(nract) if str(nract).isdigit() else 0
cont_cheltuiala = get_cont(ocr_result.get("cui") or "")
expl = f"OCR: {ocr_result.get('partner_name') or 'N/A'}"[:100]
print(f" COD: {cod}, Cont: {cont_cheltuiala}")
print(f" Total: {total}, Bază: {fara_tva}, TVA: {tva}")
# 7. Process TVA entries from OCR (pot fi mai multe cote TVA)
tva_entries = ocr_result.get("tva_entries") or []
# 8. Build accounting lines
lines = []
# Calculate base for each TVA rate
if tva_entries:
# Process each TVA entry separately
for tva_entry in tva_entries:
tva_rate = tva_entry.get("percent") or 21
tva_amount = float(tva_entry.get("amount") or 0)
if tva_amount <= 0:
continue
# Calculate base for this TVA rate
base_amount = tva_amount / (tva_rate / 100)
# Get ID_JTVA_COLOANA and TAXCODE based on TVA rate and TVA la încasare
if tva_incasare:
jtva_data = JTVA_NEEX.get(tva_rate, (210, 211, 301104, 301305))
else:
jtva_data = JTVA_NORMAL.get(tva_rate, (208, 209, 301104, 301305))
jtva_baza, jtva_tva, taxcode_normal, taxcode_tvai = jtva_data
taxcode = taxcode_tvai if tva_incasare else taxcode_normal
print(f" TVA {tva_rate}%: baza={base_amount:.2f}, tva={tva_amount:.2f}, JTVA=({jtva_baza},{jtva_tva}), TAXCODE={taxcode}")
# Linia cheltuială pentru această cotă
lines.append({
"scd": cont_cheltuiala, "scc": "401",
"suma": base_amount, "expl": expl,
"id_partc": id_part, "id_partd": 0,
"id_jtva": jtva_baza,
"taxcode": taxcode
})
# Linia TVA pentru această cotă
proc_tva = 1 + tva_rate / 100 # 1.21, 1.19, etc.
lines.append({
"scd": cont_tva, "scc": "401",
"suma": tva_amount, "expl": f"TVA {tva_rate}% {expl}"[:100],
"id_partc": id_part, "id_partd": 0,
"proc_tva": proc_tva,
"id_jtva": jtva_tva,
"taxcode": taxcode
})
else:
# Fallback: use total amounts if no tva_entries
if fara_tva > 0:
tva_rate = round(tva / fara_tva * 100) if fara_tva > 0 else 21
else:
tva_rate = 21
if tva_incasare:
jtva_data = JTVA_NEEX.get(tva_rate, (210, 211, 301104, 301305))
else:
jtva_data = JTVA_NORMAL.get(tva_rate, (208, 209, 301104, 301305))
jtva_baza, jtva_tva, taxcode_normal, taxcode_tvai = jtva_data
taxcode = taxcode_tvai if tva_incasare else taxcode_normal
print(f" TVA {tva_rate}% (estimat): JTVA=({jtva_baza},{jtva_tva}), TAXCODE={taxcode}")
lines.append({
"scd": cont_cheltuiala, "scc": "401",
"suma": fara_tva, "expl": expl,
"id_partc": id_part, "id_partd": 0,
"id_jtva": jtva_baza,
"taxcode": taxcode
})
if tva > 0:
proc_tva = 1 + tva_rate / 100
lines.append({
"scd": cont_tva, "scc": "401",
"suma": tva, "expl": f"TVA {tva_rate}% {expl}"[:100],
"id_partc": id_part, "id_partd": 0,
"proc_tva": proc_tva,
"id_jtva": jtva_tva,
"taxcode": taxcode
})
# Linia plată din casă (DOAR dacă plată numerar)
if has_cash and not has_card:
lines.append({
"scd": "401", "scc": "5311",
"suma": total, "expl": f"Plata {expl}"[:100],
"id_partc": 0, "id_partd": id_part,
"id_jtva": None, # Nu are JTVA pentru plată
"taxcode": None
})
# Dacă plată CARD - nu se face nota 401=5311 (se face la extras bancar)
# ID_FDOC = 17 pentru BON FISCAL
id_fdoc = 17
# 9. Insert lines
for line in lines:
proc_tva = line.get("proc_tva") or 0 # Default 0 for non-TVA lines
id_jtva = line.get("id_jtva") # Poate fi None pentru plăți
taxcode = line.get("taxcode") # Poate fi None pentru plăți
cursor.execute("""
INSERT INTO ACT_TEMP (
LUNA, AN, COD, DATAIREG, DATAACT, NRACT,
EXPLICATIA, SCD, SCC, SUMA, PROC_TVA,
ID_PARTC, ID_PARTD, ID_FDOC, ID_JTVA_COLOANA, TAXCODE, ID_UTIL, DATAORA
) VALUES (
:luna, :an, :cod, TRUNC(SYSDATE), :dataact, :nract,
:expl, :scd, :scc, :suma, :proc_tva,
:id_partc, :id_partd, :id_fdoc, :id_jtva, :taxcode, 0, SYSDATE
)
""",
luna=luna, an=an, cod=cod, dataact=receipt_date, nract=nract,
expl=line["expl"], scd=line["scd"], scc=line["scc"],
suma=line["suma"], proc_tva=proc_tva,
id_partc=line["id_partc"], id_partd=line["id_partd"],
id_fdoc=id_fdoc, id_jtva=id_jtva, taxcode=taxcode
)
jtva_info = f" [JTVA={id_jtva}]" if id_jtva else ""
taxcode_info = f" [TAX={taxcode}]" if taxcode else ""
print(f" {line['scd']} = {line['scc']}: {line['suma']:.2f}{jtva_info}{taxcode_info}")
# 9. Finalize
mesaj = cursor.var(oracledb.STRING, 4000)
cursor.callproc('PACK_CONTAFIN.FINALIZEAZA_SCRIERE_ACT_RUL',
[0, cod, 0, 0, 0, mesaj])
if do_commit:
conn.commit()
return {"success": True, "cod": cod, "luna": luna, "an": an, "saved": True,
"id_part": id_part, "tva_incasare": tva_incasare}
else:
conn.rollback()
return {"success": True, "cod": cod, "luna": luna, "an": an, "saved": False,
"id_part": id_part, "tva_incasare": tva_incasare}
except Exception as e:
conn.rollback()
import traceback
return {"success": False, "error": str(e), "traceback": traceback.format_exc()}
finally:
cursor.close()
conn.close()
def process_bon(file_path: Path, do_save: bool = False, company_id: int = COMPANY_ID,
api_user: str = API_USER, api_pass: str = API_PASS):
"""Procesează un bon fiscal: OCR → SQLite → Oracle."""
print("=" * 60)
print(f"📄 Procesez: {file_path.name}")
print("=" * 60)
# 1. Login
print("\n🔑 Login API...")
client = APIClient(API_BASE)
if not client.login(api_user, api_pass, SERVER_ID):
print("❌ Login failed!")
return None
print(" ✅ OK")
# 2. OCR
print("\n🔍 OCR extract...")
ocr_result = client.ocr_extract(file_path)
if not ocr_result["success"]:
print(f"{ocr_result['error']}")
return None
ocr = ocr_result["result"]
print(f" ✅ OK ({ocr_result.get('time_ms', '?')}ms)")
print(f" CUI: {ocr.get('cui')}")
print(f" Partner: {ocr.get('partner_name')}")
print(f" Data: {ocr.get('receipt_date')}")
print(f" Total: {ocr.get('amount')} RON")
print(f" TVA: {ocr.get('tva_total')} RON")
# Show payment methods
payment_methods = ocr.get("payment_methods") or []
if payment_methods:
pm_str = ", ".join(f"{pm.get('method')}: {pm.get('amount')}" for pm in payment_methods)
print(f" Plăți: {pm_str}")
# Show TVA breakdown
tva_entries = ocr.get("tva_entries") or []
if tva_entries:
tva_str = ", ".join(f"{t.get('code')}({t.get('percent')}%): {t.get('amount')}" for t in tva_entries)
print(f" TVA detaliat: {tva_str}")
# 3. SQLite (via API) - cu TOATE datele
print("\n💾 Save SQLite (via API)...")
sqlite_result = client.create_receipt(ocr, company_id)
if not sqlite_result["success"]:
print(f"{sqlite_result['error']}")
return None
receipt = sqlite_result["receipt"]
print(f" ✅ Receipt ID: {receipt.get('id')}")
print(f" Payment mode: {receipt.get('payment_mode')}")
# 4. Oracle (direct)
mode = "SAVE" if do_save else "DRY RUN"
print(f"\n🗄️ Save Oracle ({mode})...")
oracle_result = save_to_oracle(ocr, do_commit=do_save)
if oracle_result["success"]:
if oracle_result["saved"]:
print(f" ✅ SALVAT: COD={oracle_result['cod']}, {oracle_result['luna']:02d}/{oracle_result['an']}")
else:
print(f" ⚠️ DRY RUN: ar fi COD={oracle_result['cod']}")
else:
print(f"{oracle_result.get('error')}")
if oracle_result.get("traceback"):
print(oracle_result["traceback"])
print("\n" + "=" * 60)
return {
"ocr": ocr,
"sqlite_receipt_id": receipt.get("id"),
"oracle": oracle_result
}
def main():
parser = argparse.ArgumentParser(description="Procesare bon fiscal: OCR → SQLite → Oracle")
parser.add_argument("file", help="Path către PDF sau imagine")
parser.add_argument("--save", action="store_true", help="Salvează efectiv în Oracle")
parser.add_argument("--company", type=int, default=COMPANY_ID, help="Company ID")
parser.add_argument("--user", default=API_USER, help="API username")
parser.add_argument("--password", default=API_PASS, help="API password")
args = parser.parse_args()
file_path = Path(args.file)
if not file_path.exists():
print(f"❌ File not found: {file_path}")
sys.exit(1)
result = process_bon(file_path, do_save=args.save, company_id=args.company,
api_user=args.user, api_pass=args.password)
if result:
print("\n✅ Done!")
else:
print("\n❌ Failed!")
sys.exit(1)
if __name__ == "__main__":
main()