# F7 — Conversões / Vendas (produtos mais vendidos)

## Status

- Situação: **Planejada (não implementada)**
- Objetivo: saber **quais produtos geraram venda** e **quanto de comissão**, cruzando com os cliques (F6) para medir CTR → CR → receita.
- Pré-requisito: F6 (tracking) concluída — sem `tracked_link`/hash, não dá para ligar venda ao clique/oferta que originou.

---

## Realidade de cada plataforma

Nem toda plataforma expõe dados de venda via API pública. Este é o ponto mais importante antes de desenhar qualquer tela:

| Plataforma | API pública de vendas? | Caminho real |
|---|---|---|
| **Amazon Associates** | Não existe API pública de relatórios de venda | **Import CSV** do Earnings Report do painel Associates (mensal ou sob demanda). Relatório vem com `ASIN`, `ASINSubtag`, data, receita, comissão |
| **Mercado Livre Afiliados** | Existe API de reports para parceiros liberados. Não é auto-serviço para afiliado comum | Caminho A (API): depende de liberação Meli; Caminho B (fallback): **import CSV** do portal de afiliados `mercadolivre.com.br/afiliados` |
| **Shopee** | Congelada (F5), fora de escopo | — |

Conclusão: o mecanismo principal de entrada de dados vai ser **upload de CSV**. API vem depois (se liberada) como "fast path".

---

## Como ligar clique a venda

O elo é um **identificador que a gente passa no link** e que volta no relatório de vendas da plataforma:

### Amazon — `ascsubtag`

A Amazon permite o parâmetro `ascsubtag` no link, que volta no relatório de vendas da SubTag:

```
https://www.amazon.com.br/dp/B0XYZ?tag=seutag-20&ascsubtag={HASH_DO_TRACKED_LINK}
```

No CSV de Earnings vem a coluna `Tracking ID` com o hash → casa direto com `tracked_links.hash`.

**Mudança necessária em F1/F6:** ao montar o link real de afiliado, adicionar `&ascsubtag={tracked_link.hash}`.

### Mercado Livre — `matt_tool` customId

O ML usa o `customId` no parâmetro `matt_tool`. Por padrão a gente usa a tag `pechinchinhaboa`, mas dá para adicionar um segundo nível:

```
https://meli.la/XXXXX?matt_tool=pechinchinhaboa&matt_word={HASH_DO_TRACKED_LINK}
```

O ML aceita `matt_word` como campanha, que aparece no relatório. Precisa validar no painel se o dashboard de afiliado do ML expõe essa coluna no CSV.

**Alternativa mais simples:** se `matt_word` não funcionar, gerar **um link afiliado diferente por oferta** (via `generateAffiliateLink` que já existe), e guardar a URL completa em `tracked_link.destination_url`. Daí o ML sabe discriminar por URL encurtada. O CSV do ML lista URL por URL.

---

## Modelagem de dados

### Nova tabela `affiliate_sales`

Uma linha por item vendido no CSV importado.

```sql
CREATE TABLE affiliate_sales (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    platform VARCHAR(32) NOT NULL,               -- 'amazon' | 'mercadolivre'
    external_order_id VARCHAR(100) NULL,         -- ID do pedido no relatório
    external_item_id VARCHAR(100) NULL,          -- ASIN / MLB
    subtag VARCHAR(32) NULL,                     -- hash do tracked_link que originou (se vier)
    tracked_link_id BIGINT UNSIGNED NULL,        -- FK resolvido após import (via subtag)
    product_title VARCHAR(500) NULL,
    quantity INT UNSIGNED NOT NULL DEFAULT 1,
    revenue DECIMAL(10,2) NULL,                  -- valor vendido (bruto)
    commission DECIMAL(10,2) NULL,               -- comissão gerada
    currency CHAR(3) NOT NULL DEFAULT 'BRL',
    sold_at DATE NULL,
    imported_at TIMESTAMP NOT NULL,
    import_batch_id BIGINT UNSIGNED NOT NULL,    -- FK para sales_imports
    raw_row JSON NULL,                           -- linha original do CSV, para auditoria
    INDEX idx_platform_date (platform, sold_at),
    INDEX idx_subtag (subtag),
    INDEX idx_tracked (tracked_link_id),
    INDEX idx_batch (import_batch_id),
    FOREIGN KEY (tracked_link_id) REFERENCES tracked_links(id) ON DELETE SET NULL,
    FOREIGN KEY (import_batch_id) REFERENCES sales_imports(id) ON DELETE CASCADE
);
```

### Nova tabela `sales_imports`

Um registro por upload de CSV (auditoria + rollback).

```sql
CREATE TABLE sales_imports (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT UNSIGNED NULL,
    platform VARCHAR(32) NOT NULL,
    file_name VARCHAR(255) NOT NULL,
    file_hash VARCHAR(64) NOT NULL UNIQUE,       -- SHA256 do arquivo; previne reimportar o mesmo CSV
    rows_total INT UNSIGNED NOT NULL DEFAULT 0,
    rows_imported INT UNSIGNED NOT NULL DEFAULT 0,
    rows_skipped INT UNSIGNED NOT NULL DEFAULT 0,
    status ENUM('pending', 'processing', 'success', 'failure') NOT NULL,
    error_message TEXT NULL,
    created_at TIMESTAMP NULL,
    completed_at TIMESTAMP NULL,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
);
```

Regras:
- `file_hash` impede reimportar o mesmo CSV por acidente
- Se deu ruim, dá para apagar um `sales_imports` e o `ON DELETE CASCADE` limpa as linhas relacionadas

---

## Mecanismos de entrada

### 1. Upload manual de CSV (mecanismo principal — dia 1)

Tela: `/admin/sales/import`

Fluxo:
1. Usuário escolhe plataforma (Amazon / Mercado Livre)
2. Faz upload do CSV exportado do painel de afiliados
3. Sistema valida hash → se já foi importado, avisa e cancela
4. Sistema faz parse conforme layout da plataforma
5. Para cada linha:
   - Tenta casar `subtag` com `tracked_links.hash` → se achar, preenche `tracked_link_id`
   - Cria `affiliate_sales`
6. Mostra resumo: X importadas, Y duplicadas, Z não casaram com clique

**Parsers a implementar:**
- `App\Services\Sales\AmazonEarningsCsvParser`
- `App\Services\Sales\MercadoLivreAffiliateCsvParser`

Cada parser recebe o CSV e retorna array de `AffiliateSale` já mapeado. Testes unitários com CSVs de exemplo no `tests/fixtures/`.

### 2. Sincronização por API (evolução futura — se a plataforma permitir)

Comando: `php artisan sales:sync {platform}`

- **Amazon**: não tem. Fica só o import manual
- **Mercado Livre**: se/quando a API de reports for liberada, criar `MercadoLivreReportsService::fetchSales($from, $to)` e chamar daqui
- **Gatilho**: schedule diário madrugada, se configurado

---

## Admin web

### `/admin/sales/import` — Upload

Formulário simples:
- Dropdown de plataforma
- Campo de upload (aceita `.csv` e `.xlsx`)
- Botão "Importar"
- Histórico de imports logo abaixo (últimos 10, com status, contadores, botão "reverter")

### `/admin/sales/top` — Produtos mais vendidos

Cards:
- Receita total no período
- Comissão total no período
- Conversão geral: cliques / vendas (cruza com F6)
- AOV (ticket médio)

Tabela ordenada por comissão DESC (ou receita, seletor):

| Coluna | Detalhe |
|---|---|
| # | Ranking |
| Produto | miniatura + título |
| Plataforma | amazon / mercadolivre |
| Cliques (F6) | contador no período |
| Vendas | quantidade |
| Receita | soma |
| Comissão | soma |
| CR | vendas / cliques (%) |

Filtros: período, plataforma, busca por título.

### `/admin/sales/unmatched` — Vendas sem clique associado

Lista vendas onde `tracked_link_id IS NULL` (subtag não casou). Útil para:
- Diagnosticar se o `ascsubtag` está sendo mandado corretamente
- Vendas vindas de canais externos não rastreados
- Permitir associar manualmente (se o usuário souber de onde veio)

---

## Relatório consolidado no Dashboard

Reforma leve em `/admin` (já reformado na F1, agora ganha mais cards):

- Receita total (últimos 30d)
- Comissão total (últimos 30d)
- Top 5 produtos por comissão (lista compacta)

---

## Permissões

- `sales.view` — ver relatórios
- `sales.import` — fazer upload de CSV
- `sales.manage` — reverter imports, associar vendas manualmente

---

## Comandos Artisan

- `php artisan sales:import {platform} {file}` — import via CLI (para cron ou script)
- `php artisan sales:rematch-tracking` — tenta casar novamente `affiliate_sales` órfãs (sem `tracked_link_id`) usando `subtag` — útil se um tracked_link foi criado depois do import por qualquer motivo
- `php artisan sales:prune --days=730` — apaga vendas com mais de 2 anos (configurável)

---

## Configuração

### `config/sales.php` (novo)

```php
return [
    'retention_days'      => (int) env('SALES_RETENTION_DAYS', 730),
    'csv_max_size_mb'     => (int) env('SALES_CSV_MAX_SIZE_MB', 20),
    'allow_mismatched_subtag' => true,  // importa mesmo sem casar subtag
    'amazon' => [
        'expected_columns' => [
            'Date Shipped', 'ASIN', 'Title', 'Tracking ID',
            'Items Shipped', 'Revenue', 'Advertising Fees',
        ],
    ],
    'mercadolivre' => [
        'expected_columns' => [
            'Data', 'Produto', 'MLB', 'Quantidade',
            'Receita', 'Comissao', 'Tag',
        ],
    ],
];
```

Colunas exatas precisam ser confirmadas com um CSV real de cada plataforma na hora da implementação.

---

## Critérios de aceite

### Import
- Upload de CSV Amazon → linhas importam, batem `Tracking ID` com `tracked_links.hash`, `affiliate_sales` populada
- Upload de CSV ML → mesmo comportamento
- Upload do mesmo arquivo duas vezes → segunda vez recusa por hash duplicado
- CSV malformado → erro claro na tela, nenhum dado salvo

### Relatórios
- `/admin/sales/top` lista top produtos com cliques + vendas + CR
- Dashboard mostra receita e comissão do período
- Tela `/admin/sales/unmatched` lista as vendas sem clique

### Integração com F6
- Link Amazon publicado pela F1 inclui `&ascsubtag={hash}`
- Link ML publicado pela F1 usa a estratégia combinada com tracked_link
- Quando uma venda é importada, o clique que originou ela aparece em `tracked_links.click_count`

---

## Checklist técnico

### Banco e modelos
- [ ] Migration `create_sales_imports_table`
- [ ] Migration `create_affiliate_sales_table`
- [ ] Model `SalesImport`
- [ ] Model `AffiliateSale`

### Pipeline
- [ ] Em F6: ao gerar `tracked_link` para Amazon, incluir `ascsubtag={hash}` na `destination_url`
- [ ] Em F6: decidir e implementar a estratégia ML (validar com o painel real)

### Parsers
- [ ] `Services\Sales\AmazonEarningsCsvParser` + testes
- [ ] `Services\Sales\MercadoLivreAffiliateCsvParser` + testes
- [ ] Detecção de separador (`,` vs `;`) e encoding (UTF-8 / Latin-1)

### Admin
- [ ] Permissões `sales.*`
- [ ] Controller `Admin\SalesImportController`
- [ ] Controller `Admin\SalesReportController`
- [ ] Views de import, top, unmatched
- [ ] Menu lateral: seção "Vendas"
- [ ] Cards novos no dashboard

### Comandos
- [ ] `sales:import`, `sales:rematch-tracking`, `sales:prune`
- [ ] Schedule de prune

### Validação
- [ ] Importar CSV Amazon real da conta de teste
- [ ] Clicar num link do canal teste, esperar venda aparecer no próximo relatório
- [ ] Confirmar que `tracked_link_id` fica preenchido via `subtag`

---

## Decisões em aberto

1. **Formato dos CSVs** — cada plataforma muda coluna de tempos em tempos. Precisa documentar o layout aceito no momento da implementação e adicionar validação com mensagem clara quando mudar
2. **Importação parcial** — se uma linha do CSV estiver mal formada, pula e segue, ou aborta tudo? Sugestão: pula e registra em `rows_skipped`
3. **Previsão de comissão vs comissão confirmada** — Amazon tem dois estágios (pedido, entrega confirmada, janela de devolução). Relatório "Earnings" traz apenas os confirmados? Validar na hora
4. **Multi-conta Amazon / ML** — se o usuário tiver mais de uma tag/credencial, o CSV mistura tudo ou precisa importar separado? Sugestão inicial: uma conta por plataforma

---

## Relação com outras frentes

- **Depende de F6** — sem `tracked_link.hash`, não existe `ascsubtag` para casar no CSV
- **Depende de F1** — sem publicação automática, o volume de dados fica pequeno demais para valer análise
- **Evolui para F4 (multi-tenant)** — cada conta vai ter seus próprios imports e relatórios isolados
