Pular para o conteúdo principal

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​

CamadaEvidência no repositórioResponsabilidade
Frontendpepper-front/src/app/subscription/Lista, cadastro, edição, ficha, serviços e filtros locais.
Formuláriossrc/utils/Swipper.ts e páginas new/editSerialização de controles alterados entre etapas.
APIpepper-api/src/domains/Subscription/controller.jsCRUD, agregações e atualização dos jobs.
Modelosubscription.model.js no mesmo domínioCliente/produto únicos, quantidade, valor, parcelas, desconto, datas, estado, pagamento e link.
Validação e acessovalidations.js, routes.js, permission.js, policies.jsValidação Joi, permissões e políticas do recurso.
Lista por empresapepper-api/src/domains/Business/controller.js e routes.jsEndpoint usado pela lista do frontend.
Recorrênciapepper-api/src/domains/Subscription/schedule.jsJob mensal, pedidos e itens.
Execuçãopepper-api/src/scheduler/ e src/server.jsAgenda e habilitação do scheduler.
Pedidospepper-api/src/domains/Order/Reprocessamento, visualização e operação financeira do pedido.
Notificaçõespepper-api/src/push_notifications/watchers/subscription.jsWatcher 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 rotaUsoAcesso observado
POST /subscriptionCriarToken e subscription.write; exige perfil colaborador no header requesting.
GET /subscriptionListar por contexto de colaboradorToken e subscription.read.
GET /business/:businessId/subscriptionsLista usada pelo frontendToken; sem guard de permissão específico nessa rota.
GET /business/:id/subscriptionsListar da empresa; aceita customer (id) para filtrar no servidorToken.
GET /subscription/:idConsultar fichaToken e subscription.read.
PUT /subscription/:idEditar e alterar agendamentoToken e subscription.write.
DELETE /subscription/:idRemover documentosubscription.delete; falta auth.checkToken na cadeia dessa rota.
POST /order/:orderId/reprocessReaplicar assinaturas elegíveisToken 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​

PrioridadeEvidênciaCorreção proposta
AltaPolíticas vazias e consultas por id/empresa sem autorização de recurso observadaValidar tenant, colaborador, cliente e produto em todas as operações; alinhar middleware de token no DELETE.
Altaelse if (!values.billDate) cancela job em patch sem esse campoDistinguir campo omitido de remoção explícita; reconciliar jobs ausentes mesmo quando o dia não mudou.
AltaAgenda cancel/create/save não recebe a sessão da transação da APITornar a atualização recuperável e consistente após commit/rollback; considerar fila/outbox e reconciliação.
AltaEdição recalcula Valor na inicialização e geração usa preço atualDefinir política de preço contratado, preservar valores e aplicar desconto de forma consistente.
AltaAPI aceita billDate genérico e quantidades sem mínimoValidar inteiro 1–31, quantidade/valor e relações entre os recursos.
MédiadueDate fixo, parcelas ignoradas, ausência de fuso explícitoSeparar vigência de próximo ciclo, definir calendário/fuso e limite de ciclos.
MédiaUpsert de item por pedido/produtoDefinir soma ou vínculo por assinatura para evitar sobrescrita entre contratos.
MédiaPaginação fixa em página 1, seletores até 100 e filtros sem conexãoImplementar consulta paginada e filtros efetivos; corrigir métricas da lista.
MédiaDesconto percentual exibido como BRL e link com nomes divergentesUniformizar unidade e rótulos, explicando a função do link.
MédiaWatcher usa Customer id em User.findById, observa active ausente do modelo e rota /subscriptions/idResolver 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.