# 🎯 Sistema Completo de Campanhas (Módulo Merchant)

## 📋 Visão Geral

Sistema completo de criação e gerenciamento de campanhas de acumulação/resgate de pontos para merchants, com validações rigorosas, controle de plafond, prémios, elegibilidades e comunicação.

## 🗄️ Estrutura do Banco de Dados

### Tabela `campaigns` (Atualizada)

#### Identificação
- `campaign_number`: VARCHAR(50) UNIQUE - Número automático (ex: CAMP-20241104-0001)
- `campaign_name`: VARCHAR(80) - Nome da campanha (obrigatório, único por merchant no período)
- `sponsor_name`: VARCHAR(80) - Nome do sponsor (opcional - NUNCA obrigatório)

#### Pontos (Plafond)
- `total_points_limit`: INTEGER - Plafond global (≥ 100, opcional)
- `points_accumulated`: INTEGER DEFAULT 0 - Pontos acumulados até agora

#### Validade
- `valid_from`: TIMESTAMP - Data/hora de início (obrigatório)
- `valid_until`: TIMESTAMP - Data/hora de fim (obrigatório, > valid_from)
- `redemption_deadline`: DATE - Prazo de resgate (pode ser > valid_until, mas ≥ valid_from)

#### Prémio
- `reward_description`: VARCHAR(120) - Descrição do prémio
- `reward_value_mt`: NUMERIC(14,2) DEFAULT 0 - Valor económico em MT
- `reward_points_cost`: INTEGER - Pontos necessários para resgate (1 unidade)
- `reward_stock`: INTEGER DEFAULT 0 - Stock disponível
- `reward_stock_redeemed`: INTEGER DEFAULT 0 - Stock resgatado

#### Estado (Ciclo de Vida)
- `status`: VARCHAR(20) DEFAULT 'Rascunho' - Status: Rascunho, Activo, Parado, Cancelado, Concluído, Expirado

#### Regras de Acumulação
- `accumulation_rate`: NUMERIC(10,4) - Taxa (ex: 0.1 = 1 MT = 10 pts)
- `daily_limit_per_client`: INTEGER - Limite diário por cliente
- `transaction_limit`: INTEGER - Limite por transação
- `campaign_limit_per_client`: INTEGER - Limite total por cliente na campanha

#### Elegibilidades
- `new_customers_only`: BOOLEAN DEFAULT FALSE - Apenas clientes novos
- `vip_only`: BOOLEAN DEFAULT FALSE - Apenas segmento VIP
- `allowed_locations`: JSONB - IDs de localizações permitidas
- `allowed_payment_methods`: JSONB - Métodos de pagamento permitidos

#### Comunicação
- `notify_push`: BOOLEAN DEFAULT FALSE - Notificações push
- `notify_sms`: BOOLEAN DEFAULT FALSE - SMS
- `notify_email`: BOOLEAN DEFAULT FALSE - E-mail
- `notify_whatsapp`: BOOLEAN DEFAULT FALSE - WhatsApp
- `communication_budget`: NUMERIC(10,2) DEFAULT 0 - Orçamento de comunicação
- `communication_credits_used`: NUMERIC(10,2) DEFAULT 0 - Créditos utilizados

#### Outros
- `points_expiry_days`: INTEGER - Dias para expiração dos pontos
- `notes`: TEXT - Observações/notas

### Tabelas Auxiliares

#### `campaign_points_accumulation`
Rastreia pontos acumulados por cliente na campanha:
- `campaign_id`, `user_id`, `establishment_id`
- `points_accumulated` - Total acumulado
- `points_daily` - Pontos do dia
- `last_daily_reset` - Último reset diário

#### `campaign_rewards_redemptions`
Registra resgates de prémios:
- `campaign_id`, `user_id`
- `points_used` - Pontos utilizados
- `reward_description`, `reward_value_mt`
- `status` - pending, completed, cancelled
- `redeemed_at`, `completed_at`

## 🔐 Validações Implementadas

### 1. Dados Obrigatórios
- ✅ `campaign_name`: 1-80 caracteres
- ✅ `sponsor_name`: 1-80 caracteres
- ✅ `establishment_id`: Deve existir e estar ativo
- ✅ `valid_from`: Data válida
- ✅ `valid_until`: Data válida, > valid_from

### 2. Plafond
- ✅ `total_points_limit`: Se definido, deve ser ≥ 100
- ✅ Auto-conclusão quando `points_accumulated >= total_points_limit`

### 3. Validade e Prazo
- ✅ `valid_until` > `valid_from`
- ✅ `redemption_deadline` ≥ `valid_from`
- ✅ `redemption_deadline` pode ser > `valid_until`

### 4. Prémio
- ✅ `reward_description`: 1-120 caracteres
- ✅ `reward_value_mt`: ≥ 0
- ✅ `reward_points_cost`: > 0 (se definido)
- ✅ `reward_stock`: ≥ 0 (inteiro)
- ✅ Resgates bloqueados se `reward_stock_redeemed >= reward_stock`

### 5. Unicidade
- ✅ Nome da campanha único por merchant no período de validade

### 6. Status e Transições
- ✅ Transições válidas:
  - `Rascunho` → `Activo`, `Cancelado`
  - `Activo` → `Parado`, `Cancelado`, `Concluído`, `Expirado`
  - `Parado` → `Activo`, `Cancelado`, `Concluído`, `Expirado`
  - `Cancelado`, `Concluído`, `Expirado` → (não pode sair)

### 7. Limites de Acumulação
- ✅ `daily_limit_per_client`: > 0 (se definido)
- ✅ `transaction_limit`: > 0 (se definido)
- ✅ `campaign_limit_per_client`: > 0 (se definido)

## 📡 API Endpoints

### Criar Campanha Completa
```
POST /api/campaigns
Body: {
    "establishment_id": 5,
    "campaign_name": "Campanha Yyyy",
    "sponsor_name": "empresa Yyyy",
    "total_points_limit": 1600,  // Opcional
    "valid_from": "2024-11-05T00:00:00Z",
    "valid_until": "2024-12-31T23:59:59Z",
    "redemption_deadline": "2026-12-31",
    "reward_description": "1 bilhete de cinema",
    "reward_value_mt": 150.00,
    "reward_points_cost": 100,  // 1 MT = 10 pts
    "reward_stock": 50,
    "status": "Rascunho",
    "accumulation_rate": 0.1,  // 1 MT = 10 pts
    "daily_limit_per_client": 200,
    "transaction_limit": 100,
    "campaign_limit_per_client": 800,
    "new_customers_only": false,
    "vip_only": false,
    "allowed_locations": [1, 2, 3],
    "allowed_payment_methods": ["card", "mobile_money"],
    "notify_push": true,
    "notify_sms": false,
    "notify_email": true,
    "notify_whatsapp": false,
    "communication_budget": 500.00,
    "points_expiry_days": 90,
    "notes": "Campanha de teste"
}
```

### Mudar Status da Campanha
```
POST /api/campaigns/:id/status
Body: {
    "status": "Activo"  // ou "Parado", "Cancelado"
}
```

### Buscar Campanha
```
GET /api/campaigns/:id
```

### Atualizar Campanha
```
PUT /api/campaigns/:id
Body: {
    "campaign_name": "Novo nome",
    "reward_stock": 60,
    "status": "Activo"
}
```

## 🔄 Fluxo de Status

1. **Rascunho** → Merchant cria campanha
2. **Activo** → Merchant ativa (validações automáticas)
3. **Parado** → Merchant pausa temporariamente
4. **Activo** → Merchant retoma
5. **Concluído** → Sistema marca automaticamente quando:
   - Plafond atingido (`points_accumulated >= total_points_limit`)
   - Stock esgotado (`reward_stock_redeemed >= reward_stock`)
6. **Expirado** → Sistema marca automaticamente quando `valid_until < NOW()`
7. **Cancelado** → Merchant ou admin cancela manualmente

## 📊 Funcionalidades Automáticas

### Auto-Conclusão
- Quando `points_accumulated >= total_points_limit` → Status = `Concluída`
- Quando `reward_stock_redeemed >= reward_stock` → Status = `Parado` (se estava Activo)
- Quando `valid_until < NOW()` → Status = `Expirado` (se estava Activo ou Rascunho)

### Número Automático
- Formato: `CAMP-YYYYMMDD-XXXX`
- Exemplo: `CAMP-20241104-0001`
- Gerado automaticamente na criação

### Validação de Unicidade
- Nome da campanha único por merchant no período de validade
- Impede campanhas com mesmo nome sobrepostas

## 📝 Arquivos Criados/Atualizados

1. **`deploy/create-campaigns-full-schema.sql`**: Script SQL para criar estrutura completa
2. **`utils/campaignValidator.js`**: Validações completas de campanhas
3. **`utils/campaignSchema.js`**: Schemas Joi para validação
4. **`models/CampaignsModel.js`**: Método `createFull` para criar campanhas completas
5. **`controllers/campaignsController.js`**: Controller atualizado com todas as validações
6. **`routes/campaignsRoutes.js`**: Rota para mudança de status

## 🚀 Próximos Passos

1. **Executar script SQL**:
   ```bash
   sudo -u postgres psql -d maktubia_points -f deploy/create-campaigns-full-schema.sql
   ```

2. **Testar criação de campanha** com todos os campos

3. **Implementar sistema de acumulação de pontos** (quando cliente faz compra)

4. **Implementar sistema de resgate de prémios**

5. **Implementar sistema de comunicação** (notificações push, SMS, email, WhatsApp)

6. **Criar dashboard para merchants** visualizarem estatísticas das campanhas

## 📌 Notas Importantes

- O sistema mantém compatibilidade com campos legados (`type`, `description`, `active`)
- Validações são executadas tanto no schema Joi quanto no validator customizado
- Status automático é atualizado via trigger no banco de dados
- Merchants só podem editar campanhas que criaram
- Admin pode editar qualquer campanha
- Nome da campanha deve ser único por merchant no período de validade





