--- name: expense description: > Gestão de despesas Desk CRM v1.3. Registar, categorizar e analisar despesas com verificação obrigatória de categorias existentes. Processa despesas de tickets de contabilidade com anexos PDF. Conversão USD→EUR automática. NUNCA cria categoria duplicada. Use when "despesa", "expense", "gasto", "custo", "categoria despesa", "registar despesa", "expense report", "processar tickets contabilidade". author: Descomplicar® Crescimento Digital version: 1.5.0 quality_score: 88 user_invocable: true category: finance tags: [expense, despesas, finance, categories, desk-crm, costs, reporting, tickets, receipts] desk_task: null desk_project: 65 allowed-tools: Read, mcp__desk-crm-v3 mcps: desk-crm-v3 dependencies: mcps: [desk-crm-v3] triggers: - "User wants to create expense" - "User mentions 'despesa', 'expense', 'gasto', 'custo'" - "User wants to categorize expenses" - "User asks about expense categories" - "User needs expense report or analysis" - "User wants to process expenses from tickets" - "User mentions 'tickets contabilidade', 'recibos', 'receipts'" anti_patterns: critical: - "NEVER create category without checking if exists" - "NEVER assume category doesn't exist" - "ALWAYS list categories before creating new one" categories: - "Creating duplicate categories" - "Using similar name instead of existing category" - "Not checking existing categories first" technical: - "Creating expense without category_id" - "Using category name instead of ID" performance: baseline_duration_ms: 60000 target_duration_ms: 30000 last_run_duration_ms: null success_rate: 0.95 --- # /expense - Gestão de Despesas Skill para gestão de despesas com verificação obrigatória de categorias existentes. ## Metadata - **Version**: 1.3.0 - **Author**: Descomplicar - **Date**: 2026-02-05 - **Status**: Active --- ## Quando Usar - Registar nova despesa - Criar/gerir categorias de despesas - Consultar despesas por período/categoria - Associar despesas a projectos/clientes - Análise e relatórios de despesas - **Processar despesas de tickets de contabilidade (departamento 3)** - Importar recibos de serviços (Anthropic, Hetzner, etc.) ## Quando NÃO Usar - Para facturas a clientes (usar /invoice) - Para orçamentos (usar /orcamento) - Para pagamentos recebidos (usar /crm) --- ## REGRA CRÍTICA: CATEGORIAS > **PROIBIDO criar categoria sem verificar se já existe.** Esta regra é INVIOLÁVEL. SEMPRE: ``` 1. Listar categorias: get_expense_categories(with_stats=true) 2. Pesquisar por nome similar na lista 3. SE encontrar match ou similar: - USAR a categoria existente - NÃO criar duplicado 4. SE realmente não existe: - PERGUNTAR: "Não encontrei categoria para X. Criar nova?" 5. SÓ CRIAR após confirmação explícita ``` **Violação desta regra causa duplicados que prejudicam relatórios financeiros.** --- ## Categorias Existentes (Referência) | ID | Nome | Uso | |----|------|-----| | 1 | Telecomunicações | Net+móvel | | 2 | Serviços Bancários | Comissões, taxas | | 3 | Material de Escritório | Consumíveis | | 4 | FB e Google ADS | Publicidade | | 5 | E-mail MKT | ElasticEmail | | 6 | Seguros | Apólices | | 7 | Equipamento Informático e Audiovisual | Hardware | | 8 | Remunerações | Salários pagos | | 9 | Plugins e Recursos de Design | FULL, Freepik, etc | | 10 | Alojamento de Servidores | Hetzner | | 11 | Software Faturação | Moloni | | 12 | Softwares Gestão | Everhour | | 13 | Registo de Domínios | Ptisp, NameCheap | | 14 | Impostos | SS, IRS, IVA | | 15 | Planos Prestacionais | Pagamentos acordados | | 16 | Manutenção e Suporte | Serviços técnicos | | 17 | Outras | Não classificadas | | 18 | Ofertas a Clientes | Brindes | | 19 | Plataformas Armazenamento | Vimeo, MyAirBridge | | 21 | Contabilidade | GONDOOFFICE | | 22 | Salários e Vencimentos | Recibos vencimento | | 23 | IRC - Imposto sobre Rendimento | Pagamentos IRC | | 24 | IRS - Imposto sobre Rendimento | Retenções IRS | | 25 | Contribuições Segurança Social | SS mensal | | 26 | Hosting e Servidores | Alojamento web | | 28 | Licenças Software | Painéis controlo | | 29 | Reembolsos e Créditos | Valores recebidos | | 38 | Serviços IA e APIs | Anthropic, OpenAI, CapSolver | **NOTA:** Esta lista pode estar desactualizada. SEMPRE usar `get_expense_categories()` para lista actual. --- ## Protocolo ### 1. Registar Despesa ``` 1. OBRIGATÓRIO: get_expense_categories(with_stats=true) 2. Identificar categoria correcta na lista 3. SE categoria não existe: PERGUNTAR antes de criar 4. SE dados vêm de PDFs: ler CADA ficheiro individualmente com pdftotext - NUNCA copiar valor de um PDF para outro, mesmo com nomes semelhantes - Cada documento tem o seu valor próprio 5. Recolher dados: - category_id (obrigatório) - amount (obrigatório - valor REAL extraído do documento) - date (obrigatório, YYYY-MM-DD) - note (obrigatório, descrição) - client_id (opcional) - project_id (opcional) - billable (opcional, default false) - tax (opcional) 6. create_expense com dados validados 7. Confirmar criação ao utilizador ``` ### 2. Criar Categoria (APENAS se necessário) ``` 1. get_expense_categories() - listar todas 2. Verificar se existe categoria similar 3. SE existe similar: USAR ESSA, não criar 4. SE não existe nenhuma similar: - Perguntar confirmação ao utilizador - Aguardar resposta explícita 5. SÓ APÓS confirmação: criar via SQL ou interface ``` ### 3. Consultar Despesas ``` 1. get_expenses com filtros: - category (ID da categoria) - date_from / date_to (período) - client_id (cliente específico) - project_id (projecto específico) - limit (número resultados) 2. Apresentar resumo ao utilizador ``` ### 4. Análise de Despesas ``` 1. expense_analytics com parâmetros: - period: "month", "quarter", "year" - breakdown_by: "category", "client", "project" 2. Apresentar insights: - Total por categoria - Tendências - Categorias mais usadas ``` ### 5. Processar Despesas de Tickets (Contabilidade) > **REGRA 1:** Processar tickets UM A UM com confirmação do utilizador (ou em lote se solicitado). > **REGRA 2:** Verificar SEMPRE se despesa já foi lançada (prevenir duplicados). > **REGRA 3:** NÃO adicionar respostas aos tickets - apenas criar despesa e anexos. > **REGRA 4:** Converter SEMPRE USD para EUR antes de criar despesa. > **REGRA 5:** Saltar tickets que são apenas recibos de pagamento (não são facturas). ``` 1. Obter ticket: get_ticket(ticket_id) 2. Verificar anexos: SELECT * FROM tblticket_attachments WHERE ticketid = X 3. SE não tem PDFs: SALTAR ticket (apenas notificação) 4. VERIFICAR DUPLICADOS (OBRIGATÓRIO): SELECT id, amount, date, note FROM tblexpenses WHERE reference_no LIKE '%{receipt_number}%' OR note LIKE '%ticket #{ticket_id}%' SE encontrar: PARAR e informar "Despesa já existe: #ID" 5. Extrair dados: - Do HTML do ticket (valor, data, referência) - OU usar script Python: python3 /home/ealmeida/scripts/extract_invoice_data.py {pdf_path} 6. APRESENTAR resumo ao utilizador e AGUARDAR confirmação 7. Após confirmação: a. create_expense(category_id, amount, date, note, tax=1, reference) b. UPDATE tblexpenses SET expense_name = '{Fornecedor}' WHERE id = {id} c. mkdir -p uploads/expenses/{expenseid} d. cp uploads/ticket_attachments/{ticketid}/*.pdf uploads/expenses/{expenseid}/ e. chown -R ealmeida:ealmeida uploads/expenses/{expenseid} f. INSERT INTO tblfiles (rel_id, rel_type, file_name, filetype, ...) 8. Confirmar ao utilizador e passar ao próximo ``` #### Script Python para Extracção de PDFs Localização: `/home/ealmeida/scripts/extract_invoice_data.py` ```bash python3 /home/ealmeida/scripts/extract_invoice_data.py /path/to/invoice.pdf ``` Retorna JSON com: vendor, cat_id, total, date, invoice, currency, text #### Conversão USD → EUR > **OBRIGATÓRIO:** Todas as despesas devem ser registadas em EUR. Taxa aproximada: **1 USD ≈ 0.92 EUR** (verificar taxa actual se necessário) Exemplos: - $19.99 USD → €18.39 EUR - $100.00 USD → €92.00 EUR ```sql -- Após criar despesa em USD, actualizar para EUR UPDATE tblexpenses SET amount = {valor_eur}, currency = 2 WHERE id = {expense_id}; ``` > **CRÍTICO:** O ID da moeda EUR é **2** (não 1). Usar currency=1 faz despesas não aparecerem nos relatórios. #### Tickets a Saltar | Tipo | Descrição | Acção | |------|-----------|-------| | Payment Receipt | Recibo de pagamento (não é fatura) | SALTAR - não criar despesa | | Duplicado | Mesmo nº fatura já processado | SALTAR - informar utilizador | | Sem anexo PDF | Apenas notificação por email | SALTAR - dados insuficientes | Exemplos de tickets a saltar: - "Invoice Payment Confirmation" (é recibo, não fatura) - Ticket com mesmo invoice_number de outro já processado #### Credenciais Base de Dados Localização: `/home/ealmeida/desk.descomplicar.pt/application/config/app-config.php` ```bash grep -E "APP_DB" /home/ealmeida/desk.descomplicar.pt/application/config/app-config.php ``` #### Processamento em Lote Se utilizador pedir "criar todas" ou "processar em lote": 1. Obter todos os tickets pendentes em paralelo 2. Identificar duplicados e tickets a saltar 3. Criar despesas em paralelo via `create_expense` 4. Actualizar `expense_name` e `reference_no` em batch via SQL 5. Copiar PDFs em batch 6. Registar ficheiros em batch via SQL INSERT múltiplo #### Mapeamento Email → Categoria | Domínio Email | Categoria ID | Nome | |---------------|--------------|------| | anthropic.com | 38 | Serviços IA e APIs | | openai.com | 38 | Serviços IA e APIs | | payproglobal.com | 38 | Serviços IA e APIs (CapSolver) | | elasticemail.com | 5 | E-mail MKT | | hetzner.com | 10 | Alojamento de Servidores | | meoempresas.pt | 1 | Telecomunicações | | centos-webpanel.com | 28 | Licenças Software | | canva.com | 9 | Plugins e Recursos de Design | | freepik.com | 9 | Plugins e Recursos de Design | | namecheap.com | 13 | Registo de Domínios | | ptisp.pt | 13 | Registo de Domínios | | vimeo.com | 19 | Plataformas Armazenamento | | everhour.com | 12 | Softwares Gestão | | cursor.com | 38 | Serviços IA e APIs (Cursor) | > **NOTA:** `moloni.com` NÃO incluído - é plataforma de facturação, emails podem ser de qualquer fornecedor. Verificar conteúdo do PDF para identificar o fornecedor real. #### Mapeamento Fornecedor (PDF) → Categoria Para documentos financeiros transferidos (não tickets), identificar fornecedor pelo conteúdo do PDF: | Padrão no PDF | Fornecedor | Categoria ID | Nome | |---------------|-----------|--------------|------| | `Gondooffice`, `Cubic Choices`, `GONDOOFFICE` | Gondooffice (Cubic Choices) | 21 | Contabilidade | | `Autoridade Tributária`, `emiteDoc`, `AT -` | AT - Autoridade Tributária | 15 | Planos Prestacionais | | `Staples`, `STAPLES`, `STP_ECOFACTURA` | Staples | 3 | Material de Escritório | | `MEO`, `Serviços _ Compras`, `meoempresas` | MEO Empresas | 1 | Telecomunicações | | `TOConline`, `metta`, `Recibo de Vencimento` | Salário/Vencimento | 22 | Salários e Vencimentos | | `NOS Comunicações`, `NOS ` | NOS | 1 | Telecomunicações | | `Vodafone` | Vodafone | 1 | Telecomunicações | | `EDP`, `E-REDES` | EDP | 17 | Outras | | `Segurança Social`, `Seg. Social` | Segurança Social | 25 | Contribuições SS | | `Cursor`, `Anysphere`, `cursor.com` | Cursor (Anysphere) | 38 | Serviços IA e APIs | #### 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 ``` **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 ``` > **NOTA:** Se PDF não extrair texto (imagem), usar dados do HTML do ticket email. #### 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 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, dateadded) VALUES ({expense_id}, 'expense', '{filename}', 'application/pdf', 0, 1, NOW()) ``` #### 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)` | #### Paths de Ficheiros - Anexos ticket: `/uploads/ticket_attachments/{ticketid}/` - Anexos despesa: `/uploads/expenses/{expenseid}/` --- ## Comandos | Comando | Descrição | |---------|-----------| | `/expense create` | Registar nova despesa | | `/expense list` | Listar despesas recentes | | `/expense categories` | Listar categorias disponíveis | | `/expense report [período]` | Relatório de despesas | | `/expense search [termo]` | Pesquisar despesas | --- ## MCP Tools ### Despesas - `get_expenses` - Listar despesas com filtros - `create_expense` - Criar nova despesa - `update_expense` - Actualizar despesa - `delete_expense` - Eliminar despesa - `bill_expense_to_customer` - Facturar ao cliente ### Categorias - `get_expense_categories` - Listar categorias (usar with_stats=true) ### Análise - `expense_analytics` - Métricas e análise --- ## Campos create_expense | Campo | Tipo | Obrigatório | Descrição | |-------|------|-------------|-----------| | category_id | number | ✓ | ID da categoria | | amount | number | ✓ | Valor da despesa | | date | string | ✓ | Data (YYYY-MM-DD) | | note | string | ✓ | Descrição (incluir ticket ID) | | reference | string | ✓ | Número fatura/recibo (para duplicados) | | tax | number | ✓ | **ID da taxa** (1 = IVA 23%) | | client_id | number | - | Cliente associado | | project_id | number | - | Projecto associado | | billable | boolean | - | Facturável (default: false) | | tax2 | number | - | Segunda taxa imposto | | currency | number | - | ID moeda (**EUR = 2**) | | payment_mode | string | - | Método pagamento | ### Campo expense_name (via SQL) > **IMPORTANTE:** Após criar despesa, actualizar `expense_name` com nome do fornecedor. ```sql UPDATE tblexpenses SET expense_name = '{Fornecedor}' WHERE id = {expense_id}; ``` ### Taxas de Imposto (tbltaxes) | ID | Nome | Taxa | |----|------|------| | 1 | IVA Tx Normal | 23% | ### Moedas (tblcurrencies) | ID | Nome | Símbolo | Default | |----|------|---------|---------| | 2 | EUR | € | Sim | > **CRÍTICO:** Usar sempre `currency = 2` para EUR. O ID 1 não existe e causa despesas invisíveis nos relatórios. --- ## Anti-Patterns (NUNCA fazer) ### Categorias 1. **NUNCA** criar categoria sem listar existentes primeiro 2. **NUNCA** assumir que categoria não existe 3. **NUNCA** criar categoria com nome similar a existente ### Despesas 4. Criar despesa sem category_id 5. Usar nome da categoria em vez do ID 6. Não validar data (formato YYYY-MM-DD) 7. Criar despesa sem note/descrição 8. **NUNCA** criar despesa sem verificar duplicados primeiro 9. **NUNCA** processar ticket sem verificar se já foi lançado ### Valores de PDFs 10. **NUNCA** assumir que ficheiros com nomes semelhantes têm o mesmo valor 11. **SEMPRE** ler CADA PDF individualmente para extrair o valor real 12. Exemplo real: `emiteDoc.pdf` a `emiteDoc (12).pdf` tinham valores de 25,80€ a 100,53€ - todos diferentes ### Tickets 13. Processar ticket sem verificar anexos PDF 14. Assumir que ticket com recibo = despesa não lançada --- ## Exemplos ### Exemplo 1: Registar despesa com categoria existente ``` User: Registar despesa de 50€ de telecomunicações → get_expense_categories() → Encontra ID 1 = Telecomunicações → create_expense(category_id=1, amount=50, date="2026-02-05", note="Telecomunicações") → "Despesa #X criada: 50€ em Telecomunicações" ``` ### Exemplo 2: Despesa com categoria a verificar ``` User: Registar despesa de formação 200€ → get_expense_categories() → NÃO encontra "Formação" → "Não encontrei categoria 'Formação'. Usar 'Outras' (ID 17) ou criar nova categoria?" → User: Usar Outras → create_expense(category_id=17, amount=200, date="2026-02-05", note="Formação profissional") ``` ### Exemplo 3: Despesa facturável a cliente ``` User: Registar 150€ de domínio para cliente TechStart → get_expense_categories() → ID 13 = Registo de Domínios → search_customers("TechStart") → client_id=42 → create_expense(category_id=13, amount=150, date="2026-02-05", note="Domínio techstart.pt", client_id=42, billable=true) → "Despesa facturável criada para TechStart" ``` --- ## Checklist Pré-Operação - [ ] Listar categorias existentes - [ ] Validar categoria correcta - [ ] Confirmar dados com utilizador - [ ] Usar formato data YYYY-MM-DD - [ ] Incluir nota descritiva --- ## Base de Dados - **Tabela categorias:** `tblexpenses_categories` - **Tabela despesas:** `tblexpenses` - **Campo categoria:** `category` (ID da categoria) --- ## Changelog ### v1.5.0 (2026-02-05) - Adicionado mapeamento **Cursor (Anysphere)** → categoria 38 (Serviços IA e APIs) - Email: `cursor.com` - PDF patterns: `Cursor`, `Anysphere`, `cursor.com` ### v1.4.0 (2026-02-05) - **CORRECÇÃO CRÍTICA:** ID moeda EUR = **2** (não 1) - Despesas com currency=1 não aparecem em relatórios - Corrigidas 22 despesas existentes via `UPDATE tblexpenses SET currency = 2 WHERE currency = 1` - Documentada tabela tblcurrencies com IDs correctos ### v1.3.0 (2026-02-05) - **Conversão USD → EUR obrigatória** (taxa ~0.92, $19.99 → €18.39) - **Processamento em lote** quando utilizador pede "criar todas" - Tickets a saltar documentados (payment receipts, duplicados, sem PDF) - Localização credenciais BD: `app-config.php` - Regras 4 e 5 adicionadas ao protocolo de tickets - 9 despesas processadas em sessão de teste: €857.73 total ### v1.2.0 (2026-02-05) - Campo `expense_name` obrigatório com nome do fornecedor (via SQL UPDATE) - Campo `tax` é ID da taxa de imposto (1 = IVA 23%), não percentagem - Coluna BD é `reference_no`, não `reference` - Script Python para extracção de PDFs: `/home/ealmeida/scripts/extract_invoice_data.py` - 3 métodos de extracção PDF documentados (script, pdfplumber, pdftotext) - Workflow actualizado: processar UM A UM com confirmação - Removido: adicionar respostas aos tickets (apenas criar despesa + anexos) - SQL commands actualizados com expense_name ### v1.1.0 (2026-02-05) - Processamento de despesas a partir de tickets de contabilidade - Mapeamento automático email domain → categoria - Workflow completo com verificação de anexos PDF - Cópia automática de anexos ticket → despesa - Registo de ficheiros em tblfiles ### v1.0.0 (2026-02-05) - Versão inicial - Regra crítica de verificação de categorias - Lista de categorias de referência - Fluxos para criar/consultar despesas - Integração com MCP desk-crm-v3 - Anti-patterns documentados - Criada após limpeza de 9 categorias duplicadas --- **Criado:** 2026-02-05 **Actualizado:** 2026-02-05 **Motivo:** Prevenção de categorias duplicadas + processamento de tickets