diff --git a/cosiap_api/dynamic_forms/models.py b/cosiap_api/dynamic_forms/models.py index 2711f42f7062245ee6fec5882e20d45f57916d70..c4f8bf1ae0d674c0d6c9e03ed4e520876f69d5aa 100644 --- a/cosiap_api/dynamic_forms/models.py +++ b/cosiap_api/dynamic_forms/models.py @@ -167,13 +167,11 @@ class Respuesta(models.Model): objects = InheritanceManager() def __str__(self): - return f"Respuesta {type(self)} - Elemento: {self.elemento} - Solicitud: {self.solicitud_id}" + return f"Respuesta {type(self)} - Elemento: {self.elemento} - Solicitante: {self.solicitud.solicitante_id}" def save(self, *args, **kwargs): if self._state.adding: - # Solo realiza la verificación si estás creando una respuesta nueva - if self.elemento.seccion.tipo == 'unico': - # Verificar si ya existe una respuesta para esta combinación + if self.elemento.seccioneselementos_set.filter(seccion__tipo='unico').exists(): if Respuesta.objects.filter(elemento=self.elemento, solicitud=self.solicitud).exists(): raise IntegrityError('Ya existe una respuesta para este elemento y solicitud') super().save(*args, **kwargs) @@ -402,4 +400,4 @@ class RDocumento(Respuesta): respuesta = self.valor obligatorio = self.elemento.obligatorio if (not respuesta) and obligatorio: - raise ValidationError("Este campo es Obligatorio.") \ No newline at end of file + raise ValidationError("Este campo es Obligatorio.") diff --git a/cosiap_api/dynamic_tables/DynamicTable.py b/cosiap_api/dynamic_tables/DynamicTable.py index b4a5d3d1cc8984c74675d941a4f91af92fd21da1..4cb371625e51b74c82125ed81a359fb3936ad333 100644 --- a/cosiap_api/dynamic_tables/DynamicTable.py +++ b/cosiap_api/dynamic_tables/DynamicTable.py @@ -527,4 +527,36 @@ class DynamicTable(serializers.ModelSerializer): if not success: raise ValidationError(errors) # devolvemos un diccionario de errores surgidos durante las validaciones - return success \ No newline at end of file + return success + + + def update_registers(self, update_registers, model): + ''' + Actualiza múltiples campos de múltiples registros del modelo específicado + + :param update_registers: Diccionario en el formato {"id_registro": {"column1": "new_value", "column2": "new_value"}} + :return: True si todos los registros fueron actualizados de manera exitosa. False de lo contrario + ''' + all_errors = {} + overall_success = True + + for pk, field_updates in update_registers.items(): + try: + instance = model.objects.get(pk=int(pk)) + # Intentar actualizar los campos de la instancia + try: + success = self.update_fields(instance, field_updates) + + except ValidationError as e: + all_errors[pk] = str(e) # Devolvemos el error de validación como string + overall_success = False + + except Exception as e: + all_errors[pk] = str(e) + overall_success = False + + if not overall_success: + raise ValidationError(all_errors) # Devolver un diccionario de errores surgidos durante las actualizaciones + + return overall_success + diff --git a/cosiap_api/dynamic_tables/views.py b/cosiap_api/dynamic_tables/views.py index 67f6b1c01fca6080e10fdbdbfec7ceac31858cc8..ce823114c003d68fdd124a49a1e7b0dd5227028f 100644 --- a/cosiap_api/dynamic_tables/views.py +++ b/cosiap_api/dynamic_tables/views.py @@ -104,43 +104,65 @@ class DynamicTableAPIView(BasePermissionAPIView): - def put(self, request, pk, *args, **kwargs): + def put(self, request, pk=None, *args, **kwargs): ''' Permite a un admin modificar manualmente varias columnas de una fila en la tabla dinámica. ''' response_data = {} + instance = None # Obtener la instancia del modelo por el id proporcionado en la URL o un 404 en caso de no existir - instance = get_object_or_404(self.model_class, pk=pk) + if pk is not None: + instance = get_object_or_404(self.model_class, pk=pk) + + # Obtener datos de actualización del request field_updates = request.data.get('field_updates', {}) + register_updates = request.data.get('register_updates', {}) - # extraemos la configuración, o si no fue enviada asignamos la predeterminada + # Extraer la configuración, o si no fue enviada asignamos la predeterminada configuracion = self.get_configuracion_reporte(request) serializer = DynamicTable(instance=configuracion) - # si tenemos un columns all if self.columns == "__all__": - # debemos recuperar las columnas disponibles según la configuración self.columns = list(serializer.get_available_columns(configuracion).keys()) - - # Verificamos que los campos estén disponibles en la tabla y que sean editables - for column_name in field_updates: - if column_name not in self.columns or column_name in self.non_editable_fields: - Mensaje.error(response_data, f"Modificación no permitida en la columna '{column_name}'.") - return Response(response_data, status=status.HTTP_400_BAD_REQUEST) - try: - serializer = DynamicTable(instance=configuracion) - # Actualizar los valores de los campos y guardar la instancia - success = serializer.update_fields(instance, field_updates) - - if success: - Mensaje.success(response_data, 'Campos actualizados con éxito.') - return Response(response_data, status=status.HTTP_200_OK) - else: - Mensaje.error(response_data,'Ocurrió un error al actualizar algunos campos.') - return Response(response_data, status=status.HTTP_400_BAD_REQUEST) - except ValidationError as e: - return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) + if field_updates and instance: + # Caso donde se proporcionan actualizaciones individuales para un registro + for column_name in field_updates: + if column_name not in self.columns or column_name in self.non_editable_fields: + Mensaje.error(response_data, f"Modificación no permitida en la columna '{column_name}'.") + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + + try: + # Actualizar los valores de los campos del registro y guardar la instancia + success = serializer.update_fields(instance, field_updates) + + if success: + Mensaje.success(response_data, 'Campos actualizados con éxito.') + return Response(response_data, status=status.HTTP_200_OK) + else: + Mensaje.error(response_data, 'Ocurrió un error al actualizar algunos campos.') + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + except ValidationError as e: + return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) + + elif register_updates: + # Caso donde se proporcionan actualizaciones para múltiples registros + try: + success = serializer.update_registers(register_updates, self.model_class) + if success: + Mensaje.success(response_data, 'Registros actualizados con éxito.') + return Response(response_data, status=status.HTTP_200_OK) + else: + Mensaje.error(response_data, 'Ocurrió un error al actualizar algunos registros.') + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + except ValidationError as e: + print(str(e)) + return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) + + else: + Mensaje.error(response_data, 'No se proporcionaron datos de actualización.') + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + def delete(self, request, pk, *args, **kwargs): diff --git a/cosiap_api/modalidades/migrations/0002_dynamic_form_modalidades.py b/cosiap_api/modalidades/migrations/0002_dynamic_form_modalidades.py new file mode 100644 index 0000000000000000000000000000000000000000..a8fd08838fd23cc952a8694356dc85df578e10f2 --- /dev/null +++ b/cosiap_api/modalidades/migrations/0002_dynamic_form_modalidades.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.7 on 2024-07-29 17:17 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic_forms', '0002_creacion_modelos_formularios_dinamicos_02'), + ('modalidades', '0001_creacion_inicial_modulos_dynamic_formats__modalidades__solicitudes'), + ] + + operations = [ + migrations.AddField( + model_name='modalidad', + name='dynamic_form', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='dynamic_forms.dynamicform', verbose_name='Dynamic Form'), + ), + ] diff --git a/cosiap_api/modalidades/migrations/0002_modalidad_dynamic_form.py b/cosiap_api/modalidades/migrations/0002_modalidad_dynamic_form.py new file mode 100644 index 0000000000000000000000000000000000000000..76a4d1e6cd9b021526fce327d2beb3709e22e60b --- /dev/null +++ b/cosiap_api/modalidades/migrations/0002_modalidad_dynamic_form.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.7 on 2024-08-08 16:51 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic_forms', '0006_merge_migrations'), + ('modalidades', '0001_creacion_inicial_modulos_dynamic_formats__modalidades__solicitudes'), + ] + + operations = [ + migrations.AddField( + model_name='modalidad', + name='dynamic_form', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='dynamic_forms.dynamicform', verbose_name='Formulario'), + ), + ] diff --git a/cosiap_api/solicitudes/tests.py b/cosiap_api/solicitudes/tests.py index a36e951b32d9b27faa4771c8c0db7c3795c00b3b..8d2ea92ef9f4cccc31aec0101f0921676bb93cc9 100644 --- a/cosiap_api/solicitudes/tests.py +++ b/cosiap_api/solicitudes/tests.py @@ -335,4 +335,31 @@ class SolicitudTests(TestCase): self.assertIn('nombre', solicitante_data) self.assertEqual(solicitante_data['nombre'], self.solicitante.nombre) - \ No newline at end of file + def test_put_actualizacion_registro_multiple(self): + """ + Probar la actualización de múltiples registros con una solicitud PUT + """ + url = reverse('solicitudes:solicitudes') + + data = { + "register_updates": { + str(self.solicitud1.pk): { + "monto_solicitado": "10000", + "observacion": "Actualización múltiple - Solicitud 1" + }, + str(self.solicitud2.pk): { + "monto_solicitado": "10000", + "observacion": "Actualización múltiple - Solicitud 2" + } + } + } + response = self.client.put(url, data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.solicitud1.refresh_from_db() + self.solicitud2.refresh_from_db() + self.assertEqual(self.solicitud1.monto_solicitado, 10000.0) + self.assertEqual(self.solicitud1.observacion, "Actualización múltiple - Solicitud 1") + self.assertEqual(self.solicitud2.monto_solicitado, 10000.0) + self.assertEqual(self.solicitud2.observacion, "Actualización múltiple - Solicitud 2") + self.assertIn('Registros actualizados con éxito.', response.data['messages']['success']) +