feat(fullstack): Add application form status, add submissions of forms, update DB schema

This commit is contained in:
2025-08-02 18:00:59 +02:00
parent f9851f01d9
commit a5eae07eaf
13 changed files with 278 additions and 91 deletions

View File

@@ -2,12 +2,15 @@ package com.betriebsratkanzlei.legalconsenthub.application_form
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSection
import com.betriebsratkanzlei.legalconsenthub.user.User
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus
import jakarta.persistence.AttributeOverride
import jakarta.persistence.AttributeOverrides
import jakarta.persistence.CascadeType
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.EntityListeners
import jakarta.persistence.Enumerated
import jakarta.persistence.EnumType
import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id
import jakarta.persistence.OneToMany
@@ -36,6 +39,10 @@ class ApplicationForm(
var organizationId: String = "",
@Enumerated(EnumType.STRING)
@Column(nullable = false)
var status: ApplicationFormStatus = ApplicationFormStatus.DRAFT,
@Embedded
@AttributeOverrides(
AttributeOverride(name = "id", column = Column(name = "created_by_id", nullable = false)),

View File

@@ -77,4 +77,12 @@ class ApplicationFormController(
applicationFormService.deleteApplicationFormByID(id)
return ResponseEntity.noContent().build()
}
override fun submitApplicationForm(id: UUID): ResponseEntity<ApplicationFormDto> {
return ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto(
applicationFormService.submitApplicationForm(id)
)
)
}
}

View File

@@ -22,7 +22,8 @@ class ApplicationFormMapper(private val formElementSectionMapper: FormElementSec
createdBy = userMapper.toUserDto(applicationForm.createdBy),
lastModifiedBy = userMapper.toUserDto(applicationForm.lastModifiedBy),
createdAt = applicationForm.createdAt ?: LocalDateTime.now(),
modifiedAt = applicationForm.modifiedAt ?: LocalDateTime.now()
modifiedAt = applicationForm.modifiedAt ?: LocalDateTime.now(),
status = applicationForm.status
)
}
@@ -33,6 +34,7 @@ class ApplicationFormMapper(private val formElementSectionMapper: FormElementSec
formElementSections = applicationForm.formElementSections.map { formElementSectionMapper.toFormElementSection(it) }.toMutableList(),
isTemplate = applicationForm.isTemplate,
organizationId = applicationForm.organizationId,
status = applicationForm.status,
createdBy = userMapper.toUser(applicationForm.createdBy),
lastModifiedBy = userMapper.toUser(applicationForm.lastModifiedBy),
createdAt = applicationForm.createdAt,
@@ -50,6 +52,7 @@ class ApplicationFormMapper(private val formElementSectionMapper: FormElementSec
name = createApplicationFormDto.name,
isTemplate = createApplicationFormDto.isTemplate,
organizationId = createApplicationFormDto.organizationId ?: "",
status = createApplicationFormDto.status ?: com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus.DRAFT,
createdBy = createdBy,
lastModifiedBy = lastModifiedBy,
)

View File

@@ -1,10 +1,12 @@
package com.betriebsratkanzlei.legalconsenthub.application_form
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormInvalidStateException
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotCreatedException
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotDeletedException
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotFoundException
import com.betriebsratkanzlei.legalconsenthub.error.ApplicationFormNotUpdatedException
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
@@ -59,4 +61,25 @@ class ApplicationFormService(
throw ApplicationFormNotDeletedException(e)
}
}
fun submitApplicationForm(id: UUID): ApplicationForm {
val applicationForm = getApplicationFormById(id)
if (applicationForm.status != ApplicationFormStatus.DRAFT) {
throw ApplicationFormInvalidStateException(
applicationFormId = id,
currentState = applicationForm.status,
expectedState = ApplicationFormStatus.DRAFT,
operation = "submit"
)
}
applicationForm.status = ApplicationFormStatus.SUBMITTED
return try {
applicationFormRepository.save(applicationForm)
} catch (e: Exception) {
throw ApplicationFormNotUpdatedException(e, id)
}
}
}

View File

@@ -0,0 +1,11 @@
package com.betriebsratkanzlei.legalconsenthub.error
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormStatus
import java.util.UUID
class ApplicationFormInvalidStateException(
val applicationFormId: UUID,
val currentState: ApplicationFormStatus,
val expectedState: ApplicationFormStatus,
val operation: String
) : RuntimeException("Cannot $operation application form with ID $applicationFormId. Current state: $currentState, expected state: $expectedState")

View File

@@ -31,6 +31,22 @@ class ExceptionHandler {
)
}
@ResponseBody
@ExceptionHandler(ApplicationFormInvalidStateException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun handleInvalidStateError(e: ApplicationFormInvalidStateException): ResponseEntity<ProblemDetails> {
logger.warn(e.message, e)
return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_PROBLEM_JSON)
.body(
ProblemDetails(
title = "Invalid State",
status = HttpStatus.BAD_REQUEST.value(),
type = URI.create("about:blank"),
detail = e.message ?: "Operation not allowed in current state"
)
)
}
@ResponseBody
@ExceptionHandler(
ApplicationFormNotCreatedException::class,