# Site de envio de Sell out e Stock — instalação

Os aliados entram com o código de aliado e a senha única, sobem o Excel de sellout ou de estoque e o site confere o arquivo na hora. Quando falta algo que o site sabe completar, ele mostra a sugestão e o aliado decide com OK. Se o arquivo estiver certo, só a versão tratada (completada) fica guardada no servidor. Você baixa os arquivos na área administrativa.

- Aliados (em espanhol): `https://expo.borisbuzo.com.br/`
- Administração (em português): `https://expo.borisbuzo.com.br/admin.php`

O site fica no subdomínio `expo.borisbuzo.com.br`. Se o mesmo servidor tiver outros sites (como `www.borisbuzo.com.br`), eles continuam funcionando como estão. Comece criando o registro do `expo` no DNS (passo 5), porque ele pode levar algumas horas para valer.

Servidor: VPS com **Ubuntu 24.04 LTS** e **Apache 2** (o PHP 8.3 vem do próprio Ubuntu). Não usa banco de dados externo nem serviços de fora: tudo fica numa pasta do servidor.

## O que tem no pacote

```
sellout-latam/
├── public/          ← a ÚNICA pasta visível na internet (DocumentRoot do Apache)
│   ├── index.php    ← página dos aliados
│   ├── admin.php    ← área administrativa
│   └── assets/      ← estilo e scripts
├── app/             ← código do sistema
├── bin/criar-admin.php
├── config/          ← exemplo de configuração e o arquivo do Apache
└── dados/           ← pasta privada: banco, arquivos recebidos, tabela de preços e bases de códigos
```

A pasta `dados/` nunca pode ficar dentro de `public/`. Se a instalação deixar `app/` ou `dados/` visíveis na internet, o site se recusa a funcionar e mostra “Instalação incorreta”.

---

## 1. Instalar os pacotes

Conecte no servidor por SSH e rode:

```bash
sudo apt update
sudo apt install -y apache2 libapache2-mod-php8.3 php8.3-cli php8.3-sqlite3 php8.3-xml php8.3-zip php8.3-mbstring unzip
```

## 2. Copiar o site

Envie o `sellout-latam.zip` para o servidor (WinSCP ou FileZilla em modo SFTP, ou `scp sellout-latam.zip usuario@IP-DO-VPS:~`). Depois:

```bash
cd ~
sudo unzip -o sellout-latam.zip -d /var/www/
sudo chown -R root:root /var/www/sellout-latam
sudo mkdir -p /var/www/sellout-latam/dados
sudo chown -R www-data:www-data /var/www/sellout-latam/dados
sudo chmod 750 /var/www/sellout-latam/dados
```

O código fica com o dono `root` (o Apache só lê). Só a pasta `dados/` é do usuário do Apache (`www-data`), porque é nela que o site grava.

## 3. Configurar o Apache

```bash
sudo cp /var/www/sellout-latam/config/apache-sellout-latam.conf /etc/apache2/sites-available/sellout-latam.conf
sudo a2ensite sellout-latam
sudo apache2ctl configtest
sudo systemctl reload apache2
```

- `configtest` deve responder `Syntax OK`.
- O arquivo já vem com `ServerName expo.borisbuzo.com.br`: o Apache entrega este site só a quem acessa esse endereço. Não é preciso desligar a página padrão do Apache nem mexer nos outros sites do servidor.
- O arquivo já limita os envios a 20 MB e dá ao PHP a memória de que ele precisa (512 MB) para arquivos grandes.

## 4. Criar o administrador

```bash
sudo -u www-data php /var/www/sellout-latam/bin/criar-admin.php boris
```

A senha é pedida duas vezes e não aparece na tela. Ela precisa ter pelo menos 10 caracteres, com letras e números. O mesmo comando troca a senha, se você esquecer. Rode sempre com `sudo -u www-data`: como root, o comando se recusa a rodar, porque o banco ficaria com o dono errado.

## 5. Ativar o HTTPS

Antes, o subdomínio precisa apontar para o VPS. No painel de DNS do domínio `borisbuzo.com.br` (no site onde ele foi registrado ou no Cloudflare, se usar), crie um registro **A** com o nome `expo` e o IP do VPS. A mudança pode levar de alguns minutos a algumas horas para valer. Para conferir, rode no servidor:

```bash
getent hosts expo.borisbuzo.com.br
```

A resposta precisa mostrar o IP do VPS (com o proxy do Cloudflare ligado, aparece um IP do Cloudflare, e tudo bem). Depois:

```bash
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d expo.borisbuzo.com.br
```

Quando o certbot perguntar, escolha redirecionar o HTTP para o HTTPS. Ele renova o certificado sozinho. Sem HTTPS, as senhas passam pela internet sem proteção, e a área administrativa mostra um alerta vermelho.

## 6. Firewall (recomendado)

```bash
sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'
sudo ufw enable
```

Libere o `OpenSSH` antes do `enable`. Sem ele, você perde o acesso ao servidor.

## 7. Primeiro acesso

1. Abra `https://expo.borisbuzo.com.br/admin.php` e entre com o usuário do passo 4.
2. **Configurações → Senha dos aliados**: defina a senha única. Ela não fica visível em lugar nenhum, então guarde-a.
3. **Configurações → Trocar a tabela**: envie a `Tabela de Preço.xlsm` (também aceita `Tabela de Preco.xlsm`). Em seguida, confira se aparecem cerca de 1.470 códigos com classificação.
4. **Configurações → Bases de códigos**: envie a `Base_ARG.csv` e a `BASE_URU_de_Codigos.xlsx`, uma de cada vez. Com a tabela e as duas bases, devem aparecer 2.057 códigos com classificação.
5. **Configurações → Servidor**: todos os itens devem estar com ✓.
6. **Aliados**: os 8 aliados dos arquivos de exemplo já estão cadastrados. Adicione os novos com o código de aliado, o nome e o país.
7. **Colunas**: decida o que exigir dos aliados (veja “Antes de mandar o link aos aliados”, abaixo).
8. Envie a cada aliado o endereço `https://expo.borisbuzo.com.br/`, o código de aliado dele e a senha.

### Antes de mandar o link aos aliados

O modelo de colunas segue o modelo oficial de sellout da Intelbras:

- as 19 colunas são obrigatórias: CD_ALIADO, TX_ALIADO, PAIS_ALIADO, DISTRITO_ALIADO, REGION_ALIADO, TIPO_CLIENTE, RUT_CLIENTE, NOMBRE_CLIENTE, PAIS_CLIENTE, CIUDAD_CLIENTE, REGION_CLIENTE, CD_ITEM, TX_ITEM, CD_ANO, CD_MES, TX_MES, CANTIDAD, VALOR_UNIT USD e VALOR_TOT USD;
- nenhuma célula pode ficar em branco; só **UNIDADE, SEGMENTO e FAMILIA** podem, porque o site completa;
- em cada linha, o TX_MES precisa ser o mesmo mês do CD_MES (por exemplo, 10 e OCTUBRE).

Os arquivos de exemplo de todos os países foram conferidos com essa regra. Só o estoque da Balanir passa como está:

| Arquivo | País | Faltam colunas | Campos em branco | Outro problema |
|---|---|---|---|---|
| Tele, jul/2026 | Uruguai | DISTRITO_ALIADO, REGION_ALIADO, TX_MES | RUT_CLIENTE em todas as 170 linhas | — |
| Electro Interior, set/2026 | Uruguai | DISTRITO_ALIADO, REGION_ALIADO | RUT_CLIENTE em 157 de 762 linhas | — |
| Balanir, ago/2026 | Uruguai | DISTRITO_ALIADO, REGION_ALIADO, TX_MES | RUT_CLIENTE em todas as 1.295 linhas | — |
| Balanir, estoque jun/2026 | Uruguai | — | — | aceito |
| Fullvision, out/2025 | Argentina | — | PAIS_CLIENTE em 317 de 431 linhas | CD_MES = 11 e TX_MES = OCTUBRE em todas as linhas |
| Fullvision, out/2026 | Argentina | CIUDAD_CLIENTE | — | — |
| Maretec, set/2026 | Argentina | — | — | TX_MES com "0" (ou outro número) em vez do mês |
| Maretec, out/2026 | Argentina | — | CIUDAD_CLIENTE na linha 9 | TX_MES com a quantidade em 148 linhas |
| Lince, out/2026 | Colômbia | CIUDAD_CLIENTE | — | — |
| Macroquil, out/2026 | Equador | CIUDAD_CLIENTE | — | — |
| Brastex, jun/2026 | Venezuela | DISTRITO_ALIADO, REGION_ALIADO | — | — |

Duas formas de seguir:

- **Exigir o modelo completo** (como está): cada aliado vê, ao enviar, a lista exata do que falta e em quais linhas, e corrige o próprio arquivo.
- **Aceitar enquanto eles se ajustam**: em **Colunas → Sellout**, desmarque **Obrigatória** nas colunas que ainda não vêm (DISTRITO_ALIADO, REGION_ALIADO, CIUDAD_CLIENTE e TX_MES) e marque **Pode ficar em branco** em RUT_CLIENTE e PAIS_CLIENTE. Salve. Quando um arquivo não traz o TX_MES, o site o acrescenta no arquivo pronto, com o mês por extenso.

Mesmo aceitando colunas a menos, o TX_MES é conferido sempre que vem no arquivo: Fullvision out/2025 e Maretec set/2026 e out/2026 continuam recusados até serem corrigidos.

Linhas sem dados de venda (sem cliente, código, quantidade nem valor), como um número esquecido no TX_MES abaixo da tabela, são ignoradas. O aliado vê um aviso com o número dessas linhas.

### O que o site sugere completar

Quando falta uma coluna ou há células vazias, o site sugere o que completar, como o dashboard faz na janela de campos em branco. Vale para o sellout e para o estoque:

| O que falta | Sugestão |
|---|---|
| Células vazias numa coluna em que 90% ou mais das células preenchidas têm o mesmo valor | Esse valor (a regra do dashboard). Exemplo: PAIS_CLIENTE vazio e todo o resto ARGENTINA |
| TX_MES (coluna ou células) | O mês escrito, a partir do CD_MES; e o CD_MES vazio, a partir do TX_MES |
| VALOR TOT USD ou VALOR UNIT USD (coluna ou células) | A conta: quantidade × VALOR UNIT USD, ou VALOR TOT USD ÷ quantidade |
| CD_ALIADO | O código de quem entrou |
| TX_ALIADO e PAIS_ALIADO | O nome e o país do aliado em **Aliados** (mantenha esse cadastro em dia) |
| A coluna UNIDADE, SEGMENTO ou FAMILIA | A coluna entra e é completada pela tabela de preços e pelas bases |

Não há sugestão para código e descrição do item, cliente, RUT nem quantidades, nem para colunas sem fonte (DISTRITO_ALIADO, REGION_ALIADO e CIUDAD_CLIENTE): esses o aliado corrige no arquivo.

O aliado vê cada sugestão debaixo do erro, com um botão OK. Quando tudo o que falta tem sugestão, o botão **Completar y enviar** fica liberado depois do OK em todas. O site então completa o arquivo e o guarda. Se houver também um erro que só o aliado pode corrigir, as sugestões aparecem só como informação: ele corrige o arquivo, envia de novo e aí dá o OK. Na lista de **Arquivos recebidos**, cada envio mostra o que foi completado (“completou: …”).

Nos exemplos, o site sugere o TX_MES (Tele e Balanir) e «ARGENTINA» no PAIS_CLIENTE (Fullvision out/2025). Esses arquivos, porém, têm outros problemas que só o aliado corrige, como as colunas que faltam, o RUT_CLIENTE vazio e o TX_MES diferente do CD_MES.

## Tabela de preços e bases de códigos

Quando UNIDADE, SEGMENTO ou FAMILIA vêm em branco, o site busca o código do produto (CD_ITEM), nesta ordem:

1. **Tabela de preços**: abas Tabela, Base_anterior e Lançamentos, nessa ordem.
2. **Bases de códigos** (`Base_ARG.csv`, `BASE_URU_de_Codigos.xlsx` ou outras): completam o código que falta na tabela e também o campo que a tabela deixa em branco. Valem em ordem alfabética do nome do arquivo (hoje, Argentina antes de Uruguai), e cada campo vem da primeira que o tiver.

Uma base pode ser .csv (com `;` ou `,`) ou Excel. Ela precisa de um cabeçalho com a coluna do código (CD_ITEM ou COD ITEM) e as colunas UNIDADE, SEGMENTO e FAMILIA; as outras colunas são ignoradas.

Duas formas de colocar a tabela e as bases:

- **Pela área administrativa** (mais simples): Configurações → Trocar a tabela, e Configurações → Bases de códigos. O site confere se consegue ler o arquivo antes de usar. Uma base enviada com o mesmo nome de outra substitui a antiga, e cada base tem o botão Excluir.
- **Pelo SFTP**: copie a tabela para `/var/www/sellout-latam/dados/` e as bases para `/var/www/sellout-latam/dados/bases/`. Depois devolva a pasta ao usuário do Apache:
  ```bash
  sudo chown -R www-data:www-data /var/www/sellout-latam/dados
  ```

**Nunca coloque esses arquivos dentro de `public/`.** Ali, qualquer pessoa poderia baixá-los.

Quando algum arquivo muda, tudo é relido sozinho no próximo envio (leva uns 2 segundos).

Os códigos que não estão em nenhuma das fontes aparecem em **Arquivos recebidos → Códigos sem classificação**, com a lista para baixar em .xlsx.

Nas bases de exemplo, a do Uruguai usa alguns nomes em espanhol ou diferentes da tabela, como ALARMAS, REDES EMPRESARIALES e CCTV IP. Eles só aparecem nos códigos que não estão na tabela nem na base da Argentina. Para padronizar, corrija a base e envie de novo.

## Onde ficam os arquivos

`/var/www/sellout-latam/dados/arquivos/AAAA-MM/`, um arquivo por envio:

- `sellout_<código>_<mês>_<data-hora>_<id>.xlsx` (ou `estoque_…`): o arquivo tratado, que vai para o dashboard. Nele:
  - as colunas têm o nome padrão e os números ficam como números;
  - os textos ficam em maiúsculas e sem acento;
  - o TX_MES fica por extenso em português (OUTUBRO);
  - os países ficam em português (URUGUAY vira URUGUAI, PARAGUAY vira PARAGUAI, ECUADOR vira EQUADOR, como no dashboard);
  - o que o aliado aceitou completar já vem preenchido;
  - Unidade, Segmento e Família vêm completados.

O arquivo do jeito que o aliado mandou não fica guardado. Envios feitos com a versão anterior do site podem ter também um `_original.xlsx`; ele continua disponível no link **Original** desses envios.

Pela área administrativa, os arquivos saem com nomes amigáveis, como `Sellout_2026-10_1014066_FULLVISION.xlsx`, ou todos de uma vez no .zip dos vigentes. Se o aliado reenviar o mesmo tipo e mês, o envio novo passa a ser o vigente e o anterior fica como “substituído”, sem ser apagado.

## Cópia de segurança

Tudo o que importa está em `dados/`. Uma cópia diária às 3 h, guardando as 30 últimas:

```bash
sudo mkdir -p /root/backup-sellout
echo '0 3 * * * root tar czf /root/backup-sellout/dados-$(date +\%F).tgz -C /var/www/sellout-latam dados && find /root/backup-sellout -name "dados-*.tgz" -mtime +30 -delete' | sudo tee /etc/cron.d/backup-sellout
```

De vez em quando, baixe uma dessas cópias para fora do VPS.

## Atualizar para uma versão nova do site

Substitua só `app/`, `public/`, `bin/` e `config/`. **Não apague nem substitua `dados/`.** O banco se atualiza sozinho no primeiro acesso depois da troca, sem perder os aliados, os envios nem as colunas que você já ajustou.

Esta versão:

- leva o modelo de sellout ao modelo oficial: acrescenta DISTRITO_ALIADO, REGION_ALIADO e CIUDAD_CLIENTE e torna o TX_MES obrigatório. As outras colunas ficam como você deixou. Veja “Antes de mandar o link aos aliados” para liberar o que ainda não quiser exigir;
- guarda só o arquivo tratado e registra o que foi completado com o OK do aliado (o banco passa para a versão 4).

```bash
cd ~ && unzip -o sellout-latam-novo.zip -x 'sellout-latam/dados/*' -d /tmp/novo
sudo cp -r /tmp/novo/sellout-latam/{app,public,bin,config} /var/www/sellout-latam/
sudo chown -R root:root /var/www/sellout-latam/{app,public,bin,config}
```

## Mudar limites

Copie `config/local.php.exemplo` para `config/local.php` e deixe só o que muda. Os padrões são:

- 20 MB por arquivo;
- 100 mil linhas por arquivo;
- sessão do aliado: 2 horas sem uso; do administrador: 4 horas;
- bloqueio de 15 minutos depois de 5 senhas erradas.

Se aumentar o tamanho máximo, aumente também `upload_max_filesize`, `post_max_size` e `LimitRequestBody` no arquivo do Apache e rode `sudo systemctl reload apache2`.

## Segurança: o que já está feito

- Nenhuma senha fica no código nem nas páginas. O banco guarda só o hash (bcrypt), que não dá para desfazer.
- O código, o banco e os arquivos ficam fora da pasta pública. Os downloads passam pela área administrativa, que exige login.
- Depois de 5 senhas erradas, o código de aliado (ou o usuário administrador) fica bloqueado por 15 minutos. Um mesmo endereço de internet é bloqueado depois de 15 erros.
- A sessão expira por tempo sem uso, e o cookie só serve para este site (HttpOnly, SameSite=Strict e Secure com HTTPS).
- Todo formulário tem proteção contra envio forjado (token CSRF), e a política de segurança de conteúdo (CSP) do navegador só aceita scripts do próprio site.
- Trocar a senha dos aliados desconecta quem estava dentro. Desativar um aliado impede que ele entre.

Opcional: liberar a área administrativa só para o seu endereço de internet. Para isso, acrescente ao arquivo do Apache, dentro do `<VirtualHost>`:

```apache
<Files "admin.php">
    Require ip 200.100.50.25
</Files>
```

Troque o IP pelo seu. Com o certbot, a mesma linha também precisa entrar em `sellout-latam-le-ssl.conf`.

Opcional: esconder a versão do Apache nos cabeçalhos. Em `/etc/apache2/conf-available/security.conf`, use `ServerTokens Prod` e `ServerSignature Off`.

## Problemas comuns

| O que aparece | O que fazer |
|---|---|
| O endereço `expo.borisbuzo.com.br` não abre, ou abre outra página | Confira o registro A do `expo` no DNS (passo 5) e se o site está ativo (`sudo a2ensite sellout-latam` e `sudo systemctl reload apache2`). |
| O certbot diz que não conseguiu validar o domínio | O DNS ainda não aponta para o VPS, ou a porta 80 está fechada. Espere o DNS valer (`getent hosts expo.borisbuzo.com.br`) e confira o firewall (passo 6). |
| “Instalação incorreta” ou “Sitio en mantenimiento” | O `DocumentRoot` não aponta para `/var/www/sellout-latam/public`. Refaça o passo 3. |
| “Nenhum administrador foi criado ainda” | Rode o passo 4. |
| Alerta de que o servidor não tem permissão para ler a tabela ou uma base | Rode o `chown`/`chmod` que o próprio alerta mostra. |
| Base de códigos recusada | Confira se a primeira linha tem CD_ITEM (ou COD ITEM) e UNIDADE, SEGMENTO e FAMILIA. |
| O aliado não vê o botão “Completar y enviar” | Algum erro não tem sugestão (por exemplo, RUT_CLIENTE vazio ou uma coluna sem fonte). Ele corrige o arquivo e envia de novo; aí dá o OK no que o site sugerir. |
| Aliado não consegue entrar | Confira se ele está **Ativo** em Aliados e se a senha dos aliados foi definida. Depois de 5 erros, ele espera 15 minutos. |
| Arquivo grande falha no envio | Veja os limites em Configurações → Servidor e na seção “Mudar limites”. |
| Erro inesperado | Veja o log: `sudo tail -50 /var/log/apache2/sellout-latam-error.log` |
