diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index eb6de00a4..82c056ef4 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -45,7 +45,7 @@ from sapl.utils import (autor_label, autor_modal, timing, GoogleRecapthaMixin, get_client_ip) from sapl.utils_template import adicionar_cabecalho_materia -from .models import (AcompanhamentoMateria, Anexada, Autoria, +from .models import (AcompanhamentoMateria, Anexada, Autoria, AutoriaProposicao, DespachoInicial, DocumentoAcessorio, Numeracao, Proposicao, Relatoria, TipoMateriaLegislativa, Tramitacao, UnidadeTramitacao) @@ -1999,6 +1999,19 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): numero_materia_futuro = forms.IntegerField( label='Número (Opcional)', required=False) + coautores = forms.ModelMultipleChoiceField( + label=_('Co-autores'), + required=False, + queryset=Autor.objects.all(), + widget=forms.SelectMultiple(attrs={ + 'class': 'select2-coautores', + 'style': 'width: 100%', + 'data-placeholder': _('Selecione os co-autores...') + }), + help_text=_('Selecione os demais autores deste documento. ' + 'Eles serão adicionados como co-autores ao incorporar a proposição.') + ) + class Meta: model = Proposicao fields = ['tipo', @@ -2044,6 +2057,7 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): dismiss=False), 12)), to_column(('descricao', 12)), to_column(('observacao', 12)), + to_column(('coautores', 12)), ] @@ -2181,6 +2195,13 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): 'ano_materia' ].initial = self.instance.materia_de_vinculo.ano + # Pré-popular co-autores existentes + coautores_pks = list( + self.instance.coautores.values_list('autor_id', flat=True) + ) + if coautores_pks: + self.fields['coautores'].initial = coautores_pks + def clean_texto_original(self): texto_original = self.cleaned_data.get('texto_original', False) @@ -2272,7 +2293,9 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): inst.texto_original.delete() self.gerar_hash(inst, receber_recibo) - return super().save(commit) + result = super().save(commit) + self._salvar_coautores(result) + return result inst.ano = timezone.now().year sequencia_numeracao = BaseAppConfig.attr( @@ -2291,9 +2314,27 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): self.gerar_hash(inst, receber_recibo) inst.save() + self._salvar_coautores(inst) return inst + def _salvar_coautores(self, inst): + """Sincroniza os co-autores selecionados no formulário com AutoriaProposicao.""" + coautores = self.cleaned_data.get('coautores', []) + # Remove co-autores não mais selecionados + inst.coautores.exclude(autor__in=coautores).delete() + # Adiciona novos co-autores + autores_existentes = set( + inst.coautores.values_list('autor_id', flat=True) + ) + for autor in coautores: + if autor.pk not in autores_existentes: + AutoriaProposicao.objects.create( + proposicao=inst, + autor=autor, + primeiro_autor=False + ) + class DevolverProposicaoForm(forms.ModelForm): @@ -2796,6 +2837,21 @@ class ConfirmarProposicaoForm(ProposicaoForm): 'Autoria registrada para (%s)' ) % str(autoria.autor)) + # Transferir co-autores da proposição para Autoria da matéria + for coautoria in proposicao.coautores.all(): + # Não duplicar se o co-autor for o mesmo que o autor principal + if coautoria.autor != proposicao.autor: + Autoria.objects.get_or_create( + autor=coautoria.autor, + materia=materia, + defaults={ + 'primeiro_autor': coautoria.primeiro_autor + } + ) + self.instance.results['messages']['success'].append(_( + 'Co-autoria registrada para (%s)' + ) % str(coautoria.autor)) + # Transferir anexos da proposição para DocumentoAcessorio from sapl.materia.models import AnexoProposicao anexos = AnexoProposicao.objects.filter( diff --git a/sapl/materia/migrations/0095_add_autoria_proposicao.py b/sapl/materia/migrations/0095_add_autoria_proposicao.py new file mode 100644 index 000000000..a125f3721 --- /dev/null +++ b/sapl/materia/migrations/0095_add_autoria_proposicao.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.28 on 2026-05-12 12:00 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0064_appconfig_revisao_setor_legislativo'), + ('materia', '0094_add_anexoproposicao'), + ] + + operations = [ + migrations.CreateModel( + name='AutoriaProposicao', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('primeiro_autor', models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Primeiro Autor')), + ('autor', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='base.Autor', verbose_name='Co-autor')), + ('proposicao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='coautores', to='materia.Proposicao', verbose_name='Proposição')), + ], + options={ + 'verbose_name': 'Co-autoria da Proposição', + 'verbose_name_plural': 'Co-autorias da Proposição', + 'ordering': ('-primeiro_autor', 'autor__nome'), + 'unique_together': {('proposicao', 'autor')}, + }, + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index 5ce5ddf52..7048117fc 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -1158,6 +1158,40 @@ class Proposicao(models.Model): update_fields=update_fields) +class AutoriaProposicao(models.Model): + """ + Modelo para co-autores de uma Proposição. + Permite que o autor da proposição indique múltiplos co-autores + que serão transferidos para a Autoria da matéria ao incorporar. + """ + proposicao = models.ForeignKey( + Proposicao, + on_delete=models.CASCADE, + verbose_name=_('Proposição'), + related_name='coautores' + ) + autor = models.ForeignKey( + Autor, + on_delete=models.PROTECT, + verbose_name=_('Co-autor') + ) + primeiro_autor = models.BooleanField( + verbose_name=_('Primeiro Autor'), + choices=YES_NO_CHOICES, + default=False + ) + + class Meta: + verbose_name = _('Co-autoria da Proposição') + verbose_name_plural = _('Co-autorias da Proposição') + unique_together = (('proposicao', 'autor'),) + ordering = ('-primeiro_autor', 'autor__nome') + + def __str__(self): + return _('Co-autoria: %(autor)s - %(proposicao)s') % { + 'autor': self.autor, 'proposicao': self.proposicao} + + class HistoricoProposicao(models.Model): STATUS_PROPOSICAO = Choices(('E', 'ENVIADA', _('Enviada')), ('R', 'RECEBIDA', _('Recebida')), diff --git a/sapl/templates/materia/proposicao_detail.html b/sapl/templates/materia/proposicao_detail.html index c477ce82e..a84a23e63 100644 --- a/sapl/templates/materia/proposicao_detail.html +++ b/sapl/templates/materia/proposicao_detail.html @@ -460,6 +460,21 @@ {% endif %} + + {% if object.coautores.exists %} +
+
{% trans "Co-autores" %}
+
+ {% for coautoria in object.coautores.all %} +
+ + {{ coautoria.autor.nome }} +
+ {% endfor %} +
+
+ {% endif %} + {% if object.conteudo_gerado_related or object.materia_de_vinculo %}
diff --git a/sapl/templates/materia/proposicao_form.html b/sapl/templates/materia/proposicao_form.html index b947e8364..1300f178a 100644 --- a/sapl/templates/materia/proposicao_form.html +++ b/sapl/templates/materia/proposicao_form.html @@ -2,8 +2,46 @@ {% load i18n %} {% load crispy_forms_tags %} -{% block extra_js %} +{% block extra_css %} + +{% endblock %} +{% block extra_js %} +