Arquitetura e análise técnica de assinaturas
Referência baseada no código local em 08/10/2026. A análise é estática, complementada pela compilação das telas usadas nos prints. Não houve execução contra banco, Agenda, API de produção ou processador de pagamentos.
Componentes e entidades
| Camada | Evidência no repositório | Responsabilidade |
|---|---|---|
| Frontend | pepper-front/src/app/subscription/ | Lista, cadastro, edição, ficha, serviços e filtros locais. |
| Formulários | src/utils/Swipper.ts e páginas new/edit | Serialização de controles alterados entre etapas. |
| API | pepper-api/src/domains/Subscription/controller.js | CRUD, agregações e atualização dos jobs. |
| Modelo | subscription.model.js no mesmo domínio | Cliente/produto únicos, quantidade, valor, parcelas, desconto, datas, estado, pagamento e link. |
| Validação e acesso | validations.js, routes.js, permission.js, policies.js | Validação Joi, permissões e políticas do recurso. |
| Lista por empresa | pepper-api/src/domains/Business/controller.js e routes.js | Endpoint usado pela lista do frontend. |
| Recorrência | pepper-api/src/domains/Subscription/schedule.js | Job mensal, pedidos e itens. |
| Execução | pepper-api/src/scheduler/ e src/server.js | Agenda e habilitação do scheduler. |
| Pedidos | pepper-api/src/domains/Order/ | Reprocessamento, visualização e operação financeira do pedido. |
| Notificações | pepper-api/src/push_notifications/watchers/subscription.js | Watcher com incompatibilidades de destinatário/rota. |
Subscription referencia business, customer, product e opcionalmente owner. status admite paid/notPaid, sem default; payment admite cash/creditCard/debitCard. Esses dois campos não são editados pelo fluxo atual. O cadastro não preenche owner. Os arquivos alternativos SubscriptionProduct encontrados no projeto não constituem o fluxo ativo de múltiplos produtos deste módulo.
Rotas e contratos
No front, a ficha e a edição usam subscription/:id/view e subscription/:id/edit: os links podem ser compartilhados e recarregados. As URLs antigas, sem id, redirecionam (revisão de 08/11/2026).
Rotas abaixo relativas a /api/v1:
| Método e rota | Uso | Acesso observado |
|---|---|---|
POST /subscription | Criar | Token e subscription.write; exige perfil colaborador no header requesting. |
GET /subscription | Listar por contexto de colaborador | Token e subscription.read. |
GET /business/:businessId/subscriptions | Lista usada pelo frontend | Token; sem guard de permissão específico nessa rota. |
GET /business/:id/subscriptions | Listar da empresa; aceita customer (id) para filtrar no servidor | Token. |
GET /subscription/:id | Consultar ficha | Token e subscription.read. |
PUT /subscription/:id | Editar e alterar agendamento | Token e subscription.write. |
DELETE /subscription/:id | Remover documento | subscription.delete; falta auth.checkToken na cadeia dessa rota. |
POST /order/:orderId/reprocess | Reaplicar assinaturas elegíveis | Token e order.write. |
A ausência de middleware de token no DELETE é uma inconsistência; o guard depende de res.locals.user e pode falhar. A revisão não demonstra exploração sem autenticação. As políticas de Subscription estão vazias; buscas por id não registram uma verificação adicional de vínculo com a empresa do solicitante. O endpoint empresarial também precisa de uma autorização explícita para o business solicitado.
A criação exige cliente, produto, quantidade, valor e data de venda. Não aceita estado, pagamento ou desconto. A edição aceita desconto 0–100 e campos opcionais, mas billDate usa uma validação genérica e não exige inteiro entre 1 e 31. link é string sem validação de URI. Não foram encontradas validações de quantidade positiva, compatibilidade de cliente/produto com a empresa ou tipo do produto nessas operações.
Agendamento e geração
Ao atualizar, o controller cancela/recria o job updateOrderForSubscriptionAtDay quando recebe um billDate verdadeiro diferente do anterior. Usa cron mensal 0 0 DIA * *; se for o dia atual, programa execução imediata. Se billDate estiver ausente/falso, cancela o job. Se for igual ao anterior, não verifica a existência do job.
O job consulta a assinatura, remove-se se ela não existir ou se dueDate tiver passado, e normaliza a data atual para o início do dia. Faz upsert de Order por cliente/empresa/vencimento e softDelete != true. Faz upsert de OrderProduct por pedido/produto com quantidade da assinatura e preço atual do catálogo, usando fallback do valor quando não há preço. Um acordo elegível de valor fixo pode definir billedValue.
O código trata colisão 11000 no pedido e o modelo possui índice para a chave diária. Isso evidencia intenção de evitar duplicação, sem comprovar que o índice esteja instalado nem que concorrência tenha sido validada em produção. Várias assinaturas de um mesmo produto atualizam a mesma chave de item.
Falhas e prioridades de correção
| Prioridade | Evidência | Correção proposta |
|---|---|---|
| Alta | Políticas vazias e consultas por id/empresa sem autorização de recurso observada | Validar tenant, colaborador, cliente e produto em todas as operações; alinhar middleware de token no DELETE. |
| Alta | else if (!values.billDate) cancela job em patch sem esse campo | Distinguir campo omitido de remoção explícita; reconciliar jobs ausentes mesmo quando o dia não mudou. |
| Alta | Agenda cancel/create/save não recebe a sessão da transação da API | Tornar a atualização recuperável e consistente após commit/rollback; considerar fila/outbox e reconciliação. |
| Alta | Edição recalcula Valor na inicialização e geração usa preço atual | Definir política de preço contratado, preservar valores e aplicar desconto de forma consistente. |
| Alta | API aceita billDate genérico e quantidades sem mínimo | Validar inteiro 1–31, quantidade/valor e relações entre os recursos. |
| Média | dueDate fixo, parcelas ignoradas, ausência de fuso explícito | Separar vigência de próximo ciclo, definir calendário/fuso e limite de ciclos. |
| Média | Upsert de item por pedido/produto | Definir soma ou vínculo por assinatura para evitar sobrescrita entre contratos. |
| Média | Paginação fixa em página 1, seletores até 100 e filtros sem conexão | Implementar consulta paginada e filtros efetivos; corrigir métricas da lista. |
| Média | Desconto percentual exibido como BRL e link com nomes divergentes | Uniformizar unidade e rótulos, explicando a função do link. |
| Média | Watcher usa Customer id em User.findById, observa active ausente do modelo e rota /subscriptions/id | Resolver usuário do cliente, alinhar estados/rotas e verificar entrega. |
O reprocessamento do pedido busca todas as assinaturas elegíveis por cliente/empresa/vigência e atualiza itens; não remove itens obsoletos. O botão Ver assinatura depende da elegibilidade atual, sem um vínculo persistente de origem. Pagamentos de Order não atualizam Subscription automaticamente.
Na lista, productValue é obtido de productObj.value, enquanto o preço do modelo é price; a ficha usa productPrice corretamente. A lista aplica fallback do link do produto e o detalhe não. A leitura de assinatura inexistente usa um erro com nome de cliente. Rotas duplicadas no frontend e mensagens de sucesso trocadas também devem ser alinhadas.
Validação recomendada após correções
Cubra autorização entre empresas; patches com billDate omitido, removido, igual e diferente; recuperação de job ausente; rollback; exclusão; dia atual e meses curtos; fuso; fim de vigência; alteração do catálogo; descontos; assinaturas duplicadas; reprocessamento; índices e execução concorrente. Verifique separadamente qualquer integração de pagamento e notificações.
As imagens são geradas por scripts/capture-subscriptions.cjs e scripts/subscriptions-preview/main.ts.template, importando componentes reais com HTTP local simulado. POST/PUT/DELETE estão bloqueados nas fixtures. O gerador não confirma formulários nem exclusões.