Skip to content

#3821 centralizar geração próximo número para create de matérias legislativas#3822

Open
LeandroJatai wants to merge 16 commits intointerlegis:3.1.xfrom
LeandroJatai:3821_centralizar_geracao_proximo_numero_ml
Open

#3821 centralizar geração próximo número para create de matérias legislativas#3822
LeandroJatai wants to merge 16 commits intointerlegis:3.1.xfrom
LeandroJatai:3821_centralizar_geracao_proximo_numero_ml

Conversation

@LeandroJatai
Copy link
Member

Atualmente a geração de próximo número para matérias é um código replicado em dois pontos:

sapl.materia.forms.ConfirmarProposicaoForm.save()
sapl.materia.views.recuperar_materia()

Um terceiro local se faz necessário, no create da API, em _MateriaLegislativaViewSet, além de outros que possam surgir no futuro.

Este PR centraliza a geração de próximo número em um método estático no model MateriaLegislativa, refatora ConfirmarProposicaoForm e recuperar_materia, além de criar o método create na viewset para que em POSTs seja aplicada a mesma lógica de numeração configurada em Configurações da Aplicação e na sequencia de numeração do tipo de matéria.

Descrição

Issue Relacionada

#3821

Motivação e Contexto

Centraliza a geração de próximo número para matéria legislativa e remove duplicidade de código.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes the logic for generating the next sequential number for legislative matters (MateriaLegislativa) into a single static method get_proximo_numero() in the MateriaLegislativa model. Previously, this logic was duplicated in ConfirmarProposicaoForm.save() and recuperar_materia() view function. The refactoring removes code duplication and adds support for the same numbering logic in the API's create endpoint.

Changes:

  • Added static method get_proximo_numero() to MateriaLegislativa model to centralize number generation logic
  • Refactored recuperar_materia() view and ConfirmarProposicaoForm.save() to use the new centralized method
  • Implemented custom create() method in the API viewset to apply automatic numbering to API-created matters

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
sapl/materia/models.py Added centralized get_proximo_numero() static method that handles all numbering sequence types (A, L, U) and supports preferred numbers
sapl/materia/views.py Refactored recuperar_materia() to use the new centralized method, removing ~40 lines of duplicated logic
sapl/materia/forms.py Refactored ConfirmarProposicaoForm.save() to use the new centralized method, removing ~35 lines of duplicated logic
sapl/api/views_materia.py Added custom create() method to apply automatic numbering when creating MateriaLegislativa via API
sapl/api/serializers.py Added import for MateriaLegislativa model to support API changes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@LeandroJatai LeandroJatai changed the title #3821 centralizar geracao proximo numero ml #3821 centralizar geração próximo número para create de matérias legislativas Jan 28, 2026
@joaohortsenado joaohortsenado self-requested a review March 14, 2026 11:35
Copy link
Contributor

@joaohortsenado joaohortsenado left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LeandroJatai no geral achei a mudança ótima e necessária, sempre que pudermos melhorar a reusabilidade do código é excelente.
Fiz comentários em alguns pontos, @edwardoliveira se você puder dar uma conferida, podem haver casos de tech debt antigo que talvez possamos passar em PR específico.

class Meta:
ordering = ['-ano', 'tipo', 'numero']

@transaction.atomic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O @transaction.atomic não resolve o problema de concorrência. Duas requests simultâneas podem chamar get_proximo_numero(), obter o mesmo número, e só uma delas vai conseguir salvar (ou pior, ambas salvam se não houver unique_together).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1.

Felizmente nós temos unique_together em MateriaLegislativa: https://github.com/interlegis/sapl/blob/3.1.x/sapl/materia/models.py#L310, mas realmente precisamos de um mecanismo para evitar race conditions (select_for_update() ou algo similar).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joaohortsenado as alterações e testes do primeiro commit de hoje foram feitas pelo "claude opus 4.6" tendo como contexto todos os comentários. Após isso chequei todos os comentários cruzando com a implementação.

numero = {'numero__max': 0}
elif numeracao == 'U': # Único/Universal
numero = MateriaLegislativa.objects.filter(
tipo=tipo).aggregate(Max('numero'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aqui está sendo feito um SELECT MAX(numero) sem select_for_update(), então o número gerado pode já estar em uso quando osave()ocorrer.
Minha sugestão é usar select_for_update() nas queries dentro de get_proximo_numero quando chamado dentro de uma transaction, ou adicionar retry logic.

numero_preferido_int = None

# Verifica se o número preferido está disponível
if numero_preferido_int is not None and not MateriaLegislativa.objects.filter(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Se o numero_preferido já está em uso, o método silenciosamente ignora e retorna o próximo sequencial. Isso pode ser confuso na API: o cliente envia numero=5 e recebe a matéria criada com numero=47 sem nenhum aviso.
Acho que poderia retornar um erro ou ao menos um aviso quando o número preferido não pode ser usado.... o que acha?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. IMHO, um erro seria melhor.

numero, ano = MateriaLegislativa.get_proximo_numero(
tipo=tipo,
ano=ano,
numero_preferido=numero
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O campo numero do payload é passado como numero_preferido. Isso significa que na API nunca é possível forçar um número específico, ele sempre pode ser sobrescrito. Se o cliente envia numero=5 e ele já existe, recebe outro número sem erro. Isso quebra a expectativa de uma API REST onde o cliente espera que os dados enviados sejam respeitados ou rejeitados.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. IMO, deveria haver uma rejeição se numero_preferido informado mas já existir.


@transaction.atomic
def create(self, request, *args, **kwargs):
data = deepcopy(request.data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O deepcopy pode ter comportamento inesperado dependendo do content type. Um simples request.data.copy() (ou dict(request.data)) é mais idiomático para django rest framework

@@ -340,53 +340,15 @@ def get(self, request, *args, **kwargs):

@permission_required('materia.detail_materialegislativa')
def recuperar_materia(request):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O código original retornava vazio quando tipo ou ano estavam ausentes. A nova versão não tem essa guarda. Se ano não vier no GET, get_proximo_numero usa o ano atual, o que muda o comportamento da view. Pode ser intencional, mas vale verificar se o frontend depende do comportamento anterior. (@edwardoliveira se puder opinar sobre isso, você tem mais conhecimento que eu... talvez nao seja um problema, o que acha?)

Returns:
tuple[int, int]: Uma tupla contendo (numero, ano) da matéria.
"""
from django.db.models import Max
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O Max pode ser importado no topo do arquivo sem problema. Para os outros, o import local é aceitável, mas vale documentar o motivo com um comentário (provavelmente para evitar imports circulares??)

class Meta:
ordering = ['-ano', 'tipo', 'numero']

@transaction.atomic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1.

Felizmente nós temos unique_together em MateriaLegislativa: https://github.com/interlegis/sapl/blob/3.1.x/sapl/materia/models.py#L310, mas realmente precisamos de um mecanismo para evitar race conditions (select_for_update() ou algo similar).

update_fields=update_fields)

@staticmethod
def get_proximo_numero(tipo, ano=None, numero_preferido=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: renomear para numero_candidato

numero_preferido_int = None

# Verifica se o número preferido está disponível
if numero_preferido_int is not None and not MateriaLegislativa.objects.filter(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. IMHO, um erro seria melhor.

numero, ano = MateriaLegislativa.get_proximo_numero(
tipo=tipo,
ano=ano,
numero_preferido=numero
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. IMO, deveria haver uma rejeição se numero_preferido informado mas já existir.

LeandroJatai and others added 9 commits March 16, 2026 07:42
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@LeandroJatai LeandroJatai force-pushed the 3821_centralizar_geracao_proximo_numero_ml branch from f96a98f to a9f66a9 Compare March 16, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants