# /expense - Mapeamento de Categorias e Dados Técnicos

## Categorias Activas (consolidacao 12-02-2026)

| ID | Nome | Fornecedores típicos |
|----|------|---------------------|
| 1 | Telecomunicacoes | MEO |
| 4 | Alojamento web (Hosting) | Hetzner, CWP |
| 6 | Servicos Externos | Make/Celonis, Gamma |
| 14 | Subscricoes e Servicos Digitais | YouTube Premium, BdThemes |
| 28 | Licencas Software | Canva, Cursor, Descript, GitHub, Softaculous |
| 30 | Servicos Cloud e Infraestrutura | Google One, Google Workspace, ElasticEmail |
| 37 | Dominios | PTisp, Namecheap |
| 38 | Servicos IA e APIs | Anthropic, OpenRouter, CapSolver, Replicate |

Existem ~30 categorias na BD mas apenas 8 são usadas regularmente. SEMPRE usar `get_expense_categories()` para lista completa.

---

## Mapeamento Fornecedor → Categoria

| Fornecedor | Categoria ID | Nome |
|-----------|-------------|------|
| Anthropic, OpenRouter, CapSolver, Replicate | 38 | Servicos IA e APIs |
| Canva, Cursor, Descript, GitHub, Softaculous, CWP | 28 | Licencas Software |
| Google One, Google Workspace, ElasticEmail | 30 | Servicos Cloud e Infraestrutura |
| Hetzner | 4 | Alojamento web (Hosting) |
| MEO | 1 | Telecomunicacoes |
| PTisp, Namecheap | 37 | Dominios |
| Make/Celonis, Gamma | 6 | Servicos Externos |
| YouTube Premium, BdThemes | 14 | Subscricoes e Servicos Digitais |

---

## Mapeamento Email → Categoria

| Dominio Email | Categoria ID | Fornecedor |
|---------------|--------------|------------|
| anthropic.com | 38 | Anthropic |
| openrouter.ai | 38 | OpenRouter |
| payproglobal.com | 38 | CapSolver |
| replicate.com | 38 | Replicate |
| cursor.com, anysphere.dev | 28 | Cursor |
| canva.com | 28 | Canva |
| descript.com | 28 | Descript |
| github.com | 28 | GitHub |
| softaculous.com | 28 | Softaculous |
| centos-webpanel.com | 28 | CWP |
| elasticemail.com | 30 | ElasticEmail |
| google.com (One/Workspace) | 30 | Google Cloud |
| hetzner.com | 4 | Hetzner |
| meoempresas.pt | 1 | MEO |
| namecheap.com | 37 | Namecheap |
| ptisp.pt | 37 | PTisp |
| make.com, celonis.com | 6 | Make/Celonis |
| gamma.app | 6 | Gamma |

> `moloni.com` NAO incluido - é plataforma de facturação.

---

## Mapeamento Padrão PDF → Categoria

| Padrão no PDF | Fornecedor | Categoria ID |
|---------------|-----------|--------------|
| `Gondooffice`, `Cubic Choices` | Gondooffice | 21 Contabilidade |
| `Autoridade Tributaria`, `emiteDoc`, `AT -` | AT | 15 Planos Prestacionais |
| `Staples`, `STP_ECOFACTURA` | Staples | 3 Material Escritorio |
| `MEO`, `meoempresas` | MEO | 1 Telecomunicacoes |
| `TOConline`, `Recibo de Vencimento` | Salario | 22 Salarios |
| `Seguranca Social` | SS | 25 Contribuicoes SS |
| `Cursor`, `Anysphere` | Cursor | 28 Licencas Software |
| `Anthropic` | Anthropic | 38 Servicos IA e APIs |
| `Hetzner` | Hetzner | 4 Alojamento web |
| `ElasticEmail` | ElasticEmail | 30 Servicos Cloud |

---

## Campos create_expense

| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| category_id | number | Sim | ID da categoria |
| amount | number | Sim | Valor da despesa |
| date | string | Sim | Data (YYYY-MM-DD) |
| note | string | Sim | Descrição (incluir ticket ID) |
| reference | string | Sim | Número fatura/recibo |
| tax | number | Sim | ID da taxa (1 = IVA 23%) |
| currency | number | Sim | EUR = **3**, USD = 2 |
| send_invoice_to_customer | number | Sim | Sempre **0** |
| client_id | number | Não | Cliente associado |
| project_id | number | Não | Projecto associado |
| billable | boolean | Não | Facturável (default: false) |

---

## Moedas

| ID | Nome | Símbolo | Default |
|----|------|---------|---------|
| 2 | USD | $ | Não |
| 3 | EUR | € | Sim |

> **CRITICO:** EUR = **3**, USD = **2**. `currency = 1` não existe e causa despesas invisíveis nos relatórios.

---

## Taxas de Imposto

| ID | Nome | Taxa |
|----|------|------|
| 1 | IVA Tx Normal | 23% |

---

## Comandos SQL

```sql
-- 1. VERIFICAR DUPLICADOS (EXECUTAR PRIMEIRO)
SELECT id, amount, date, note, reference_no FROM tblexpenses
WHERE reference_no LIKE '%{receipt_number}%'
   OR note LIKE '%ticket #{ticket_id}%'

-- 2. Verificar anexos do ticket
SELECT id, file_name, filetype FROM tblticket_attachments WHERE ticketid = {ID}

-- 3. Actualizar nome fornecedor (obrigatorio apos criar)
UPDATE tblexpenses SET expense_name = '{Fornecedor}' WHERE id = {expense_id}

-- 4. Registar anexo na despesa
INSERT INTO tblfiles (rel_id, rel_type, file_name, filetype, visible_to_customer, staffid, contact_id, dateadded)
VALUES ({expense_id}, 'expense', '{filename}', 'application/pdf', 0, 1, 0, NOW())
```

---

## Extracção de Dados de PDFs

**Método 1: Script Python (recomendado)**
```bash
python3 /home/ealmeida/scripts/extract_invoice_data.py /path/to/invoice.pdf
```
Retorna JSON com: vendor, cat_id, total, date, invoice, currency, text

**Método 2: pdfplumber directo**
```bash
python3 -c "
import pdfplumber
with pdfplumber.open('/path/to/file.pdf') as pdf:
    for p in pdf.pages:
        t = p.extract_text()
        if t: print(t)
" | grep -iE "total|iva|€"
```

**Método 3: pdftotext (fallback)**
```bash
pdftotext /path/to/file.pdf - | head -80
```

---

## Paths de Ficheiros

- Anexos ticket: `/uploads/ticket_attachments/{ticketid}/`
- Anexos despesa: `/uploads/expenses/{expenseid}/`
- Arquivo mensal: `/media/ealmeida/Dados/GDrive/Cloud/ADM_Descomplicar/Financeiro/Contabilidade/YYYY/NN-NomeMes/`
- Credenciais BD: `/home/ealmeida/desk.descomplicar.pt/application/config/app-config.php`

---

## Tickets a Saltar

| Tipo | Descrição | Acção |
|------|-----------|-------|
| Payment Receipt | Recibo de pagamento (não é fatura) | SALTAR |
| Duplicado | Mesmo nº fatura já processado | SALTAR |
| Sem anexo PDF | Apenas notificação por email | SALTAR |

---

## Campos para Rastreio de Duplicados

| Campo | Conteúdo | Exemplo |
|-------|----------|---------|
| reference | Número do recibo | `2810-3712-9577` |
| note | Incluir ticket ID | `Anthropic Max (ticket #9648)` |
