v0.75.0
AFService Map v0.75.0 — ACL do financeiro e painel Superadmin
Backup/ponto de rollback: tag v0.74.0. Trabalho feito diretamente por
mim (Claude), a partir de uma especificação de perfis de acesso e telas
de Superadmin que o usuário mandou. Com migration
(apps.billing.0002_gateway_configuration).
O que já estava pronto (verificado antes de escrever qualquer código)
Antes de implementar, conferi apps/billing/views.py (da v0.74.0) contra
a especificação recebida. A regra de acesso pros perfis de empresa já
estava completa:
_require_billing_company/_editable_companyjá restringiam o
financeiro aCompanyMembership.role == EDIT— perfil VIEW já era
bloqueado por completo, redirecionado com mensagem, sem exceção.- Essa regra já não dependia do tipo da empresa (Provedor/Projetista) —
um EDIT de qualquer tipo vê o próprio financeiro, um VIEW de qualquer
tipo não vê nenhum. Não criei papéis novos ("ISP Editor", "Projetista
View" etc.) porque oCompanyMembership.Rolegenérico já combinado
comCompany.company_typeentrega exatamente essa regra sem duplicar
enum.
O que realmente faltava, e é o conteúdo desta versão:
1. Superadmin acessa o financeiro de qualquer empresa
_require_billing_company (renomeada pra _require_list_company/
_customer_for_request) antes bloqueava superusuário por completo,
mandando "acessar como usuário da empresa" — o que não fazia sentido
pra Superadmin, que não tem (nem deveria ter) vínculo de equipe em
nenhuma empresa.
Agora:
- Lista e criação de cliente: superusuário resolve a empresa via
?empresa=<id>na query string — vindo do link "Financeiro →" que
cada linha da tabela de empresas ganhou na Visão da plataforma. - Detalhe, edição e pagamento (rotas que já têm um
pkde cliente):
superusuário não precisa nem do parâmetro — o cliente é buscado pelo
pke a empresa dele é lida diretamente do próprio registro. - Usuário comum nunca tem
?empresa=respeitado — a resolução dele
continua 100% baseada no próprio vínculoCompanyMembership, a query
string é ignorada em todo o resto do módulo. Não abre brecha de
autorização entre empresas. - Cliente de outra empresa continua dando 404 pra usuário comum (nunca
200 com "acesso negado") — não revela que o registro existe em outro
lugar, mesmo padrão de segurança já usado no resto do sistema.
2. Cadastro de empresa pelo Superadmin, fora do Django Admin
Busquei em todo apps/core por Company.objects.create antes de
escrever qualquer coisa: não existia nenhum fluxo em-app pra criar
uma empresa do zero. company_onboarding (o único fluxo parecido) exige
que a CompanyMembership já exista — é o próprio usuário da empresa
preenchendo os dados dela depois de já ter sido criada (hoje, só via
/admin/).
Tela nova em painel/plataforma/empresas/novo/
(SuperadminCompanyForm, apps/core/forms.py): cria a empresa e o
primeiro usuário responsável numa única transação —
- Dados da empresa (nome, documento, contato, endereço) — mesmos campos
deCompanyOnboardingForm. - Tipo (Provedor/Projetista) e modo de operação (ERP/manual)
definidos na hora, não depois. - Usuário responsável (nome, usuário de acesso, senha — mesma validação
de força de senha e unicidade deCompanyTeamMemberForm) com nível
de acesso (VIEW/EDIT) escolhido já no cadastro. sluggerado automaticamente a partir do nome (antes só dava pra
preencher à mão no admin).
3. Configuração de gateway de pagamento centralizada
Novo model CompanyPaymentGatewayConfiguration
(apps/billing/models.py): uma credencial por empresa, provedor
(nenhum/Efí/Inter/Mercado Pago — reaproveita o enum GatewayProvider já
existente desde a v0.74.0), modo sandbox, ativo/inativo.
A credencial em si (client_id/client_secret/access_token — três
campos genéricos, cobrem a maioria dos gateways) é serializada em JSON e
criptografada com SecretCipher antes de ir pro banco — mesmo padrão já
usado pra senha de SMTP em CompanyEmailConfiguration.password_encrypted.
Editar a credencial sem preencher um campo mantém o valor já salvo (não
sobrescreve com vazio), mesmo comportamento do formulário de e-mail.
Tela nova em painel/plataforma/empresas/<id>/gateway/, só Superadmin,
com link "Gateway →" ao lado de "Financeiro →" na tabela de empresas.
Nenhuma chamada real a gateway acontece a partir daqui — isso é só
armazenamento e gestão de credencial. A integração de verdade (gerar
boleto/PIX cobrável, confirmar pagamento) continua fora de escopo, mesma
decisão já tomada na v0.74.0 pros campos gateway_provider/
gateway_charge_id da fatura — evita ter que redesenhar o schema de
novo quando isso entrar.
Verificação feita
python -m py_compileem todos os arquivos Python tocados/novos.- Migration escrita à mão (sem GDAL neste ambiente de preparação),
revisada campo a campo contramodels.py. - Geração de slug (
slugify+ sufixo numérico em colisão) — validei a
lógica do laço de sufixo com um script Python direto: nome único,
colisão simples, colisão dupla, nome vazio — 4 asserções, todas
passaram. - Chaves do
static/css/app.cssbalanceadas (278/278). {% if %}/{% endif %}/{% for %}/{% endfor %}/{% block %}/
{% endblock %}balanceados em todos os templates tocados e novos.- Revisão manual de cada view de financeiro confirmando que a resolução
de empresa do usuário comum nunca lê a query string (request.GET) —
só o superusuário tem esse caminho, evitando qualquer brecha de
autorização cruzada entre empresas.
manage.py check, manage.py migrate, manage.py test e o teste
real dependem de banco, GDAL e navegador — não rodam neste ambiente de
preparação.
Atualização
Execute apply no servidor — esta versão tem migration
(apps.billing.0002_gateway_configuration), roda automaticamente.
Depois, um Ctrl+F5 no navegador.
Teste sugerido: como Superadmin, cadastrar uma empresa nova pela Visão
da plataforma definindo tipo e nível de acesso do responsável; logar
como esse usuário e confirmar que o financeiro da própria empresa
aparece; criar um segundo usuário VIEW na mesma empresa e confirmar que
/painel/financeiro/ bloqueia ele; como Superadmin, acessar o
financeiro de qualquer empresa pela Visão da plataforma e salvar uma
credencial de gateway de teste.