Repository: urlaubsverwaltung/urlaubsverwaltung (java)

Issue:
Deleting replacements: Reference about application is lost
<!--

Yay, thanks for your contribution. Your awesome 🥰

Please check the issue tracker first. Maybe someone else has reported the same ugly bug.
Or just file an issue, I'm just a hint not your boss.

Please fill the sections below and delete everything you don't consider valuable currently.

-->

**Describe the bug**
When deleting a replacement in an existing application, the reference to the application is lost and the form switches to a create form instead of a edit form (Button changes from "Edit application" to "New application").

**To Reproduce**
Steps to reproduce the behavior:
1. Go to an existing application with replacements
2. Click on "Remove replacement"
3. Scroll down to the save button
4. Here you can see that the button is labeled with "New application"
5. Click the button
6. An error appears stating that you have already submitted an application in this period.

**Expected behavior**
After removing a replacement, it should be possible to save the application.


Pull request:
4380 reference lost application edit
closes #4380

Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewController.java b/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewController.java
index 9c7bcd55f..fdf4e9d5d 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewController.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewController.java
@@ -150,7 +150,7 @@ class ApplicationForLeaveFormViewController implements HasLaunchpad {
         return "application/application_form";
     }
 
-    @PostMapping(value = {"/application/new", "/application/{applicationId}"}, params = "add-holiday-replacement")
+    @PostMapping(value = {"/application/new", "/application/{applicationId}/edit"}, params = "add-holiday-replacement")
     public String addHolidayReplacement(ApplicationForLeaveForm applicationForLeaveForm, Model model, Locale locale) {
 
         final Person signedInUser = personService.getSignedInUser();
@@ -223,13 +223,13 @@ class ApplicationForLeaveFormViewController implements HasLaunchpad {
         if (applicationForLeaveId == null) {
             model.addAttribute("deleteButtonFormActionValue", "/web/application/new");
         } else {
-            model.addAttribute("deleteButtonFormActionValue", "/web/application/" + applicationForLeaveId);
+            model.addAttribute("deleteButtonFormActionValue", "/web/application/" + applicationForLeaveId + "/edit");
         }
 
         return "application/application-form :: replacement-item";
     }
 
-    @PostMapping(value = {"/application/new", "/application/{applicationId}"}, params = "remove-holiday-replacement")
+    @PostMapping(value = {"/application/new", "/application/{applicationId}/edit"}, params = "remove-holiday-replacement")
     public String removeHolidayReplacement(@ModelAttribute("applicationForLeaveForm") ApplicationForLeaveForm applicationForLeaveForm,
                                            @RequestParam(name = "remove-holiday-replacement") Long personIdToRemove,
                                            Model model, Locale locale) {
diff --git a/src/main/resources/templates/application/application_form.html b/src/main/resources/templates/application/application_form.html
index a3b0d0c22..a2e3f5280 100644
--- a/src/main/resources/templates/application/application_form.html
+++ b/src/main/resources/templates/application/application_form.html
@@ -97,6 +97,9 @@
                   </div>
                 </div>
                 <th:block th:if="${not canAddApplicationForLeaveForAnotherUser}">
+                  <th:block th:if="${applicationForLeaveForm.id != null}">
+                    <input type="hidden" th:field="*{id}" />
+                  </th:block>
                   <input type="hidden" name="person" th:value="${person.id}" />
                 </th:block>
               </div>
@@ -452,7 +455,7 @@
                                   name="remove-holiday-replacement"
                                   th:value="${holidayReplacement.person.id}"
                                   formmethod="post"
-                                  th:formaction="${applicationForLeaveForm.id == null ? '/web/application/new' : '/web/application/__${applicationForLeaveForm.id}__'}"
+                                  th:formaction="${applicationForLeaveForm.id == null ? '/web/application/new' : '/web/application/__${applicationForLeaveForm.id}__/edit'}"
                                 >
                                   <span
                                     class="tw-flex tw-items-center tw-text-sm tw-text-black tw-text-opacity-50 hover:tw-text-opacity-100 focus:tw-text-opacity-100 dark:tw-text-zinc-200 tw-transition-colors"


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewControllerTest.java b/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewControllerTest.java
index 5785fcc34..29559f9f2 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewControllerTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/application/application/ApplicationForLeaveFormViewControllerTest.java
@@ -883,7 +883,7 @@ class ApplicationForLeaveFormViewControllerTest {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"/web/application/new", "/web/application/21"})
+    @ValueSource(strings = {"/web/application/new", "/web/application/21/edit"})
     void ensureReplacementAddingForOtherPersonIsNotAllowedWhenMyRoleIsUser(String url) {
         final Person signedInUser = new Person();
         signedInUser.setId(42L);
@@ -896,15 +896,13 @@ class ApplicationForLeaveFormViewControllerTest {
 
         when(personService.getPersonByID(1L)).thenReturn(Optional.of(new Person()));
 
-        assertThatThrownBy(() -> {
-            perform(post(url)
-                .param("holidayReplacementToAdd", "1")
-                .param("person", "1337")
-                .param("vacationType.id", "1")
-                .param("holidayReplacements[0].person.id", "42")
-                .param("holidayReplacements[1].person.id", "1337")
-                .param("add-holiday-replacement", ""));
-        }).hasCauseInstanceOf(AccessDeniedException.class);
+        assertThatThrownBy(() -> perform(post(url)
+            .param("holidayReplacementToAdd", "1")
+            .param("person", "1337")
+            .param("vacationType.id", "1")
+            .param("holidayReplacements[0].person.id", "42")
+            .param("holidayReplacements[1].person.id", "1337")
+            .param("add-holiday-replacement", ""))).hasCauseInstanceOf(AccessDeniedException.class);
     }
 
     @Test
@@ -999,7 +997,7 @@ class ApplicationForLeaveFormViewControllerTest {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"/web/application/new", "/web/application/21"})
+    @ValueSource(strings = {"/web/application/new", "/web/application/21/edit"})
     void ensureAddingReplacementRemovesItFromSelectables(String url) throws Exception {
 
         final Locale locale = GERMAN;
@@ -1104,12 +1102,12 @@ class ApplicationForLeaveFormViewControllerTest {
                 hasProperty("note", nullValue())
             )))
             .andExpect(model().attribute("index", is(2)))
-            .andExpect(model().attribute("deleteButtonFormActionValue", is("/web/application/7")))
+            .andExpect(model().attribute("deleteButtonFormActionValue", is("/web/application/7/edit")))
             .andExpect(view().name("application/application-form :: replacement-item"));
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"/web/application/new", "/web/application/21"})
+    @ValueSource(strings = {"/web/application/new", "/web/application/21/edit"})
     void ensureReplacementDeletionForOtherPersonIsNotAllowedWhenMyRoleIsUser(String url) {
         final Person signedInUser = new Person();
         signedInUser.setId(42L);
@@ -1120,12 +1118,10 @@ class ApplicationForLeaveFormViewControllerTest {
         person.setId(1337L);
         when(personService.getPersonByID(1337L)).thenReturn(Optional.of(person));
 
-        assertThatThrownBy(() -> {
-            perform(post(url)
-                .param("remove-holiday-replacement", "1")
-                .param("person", "1337")
-                .param("vacationType.id", "1"));
-        }).hasCauseInstanceOf(AccessDeniedException.class);
+        assertThatThrownBy(() -> perform(post(url)
+            .param("remove-holiday-replacement", "1")
+            .param("person", "1337")
+            .param("vacationType.id", "1"))).hasCauseInstanceOf(AccessDeniedException.class);
     }
 
     @Test
@@ -1499,7 +1495,7 @@ class ApplicationForLeaveFormViewControllerTest {
         when(vacationTypeService.getById(1L)).thenReturn(Optional.of(vacationType));
 
         perform(
-            post("/web/application/7")
+            post("/web/application/7/edit")
                 .locale(locale)
                 .param("vacationType.id", "1")
                 .param("id", "7")
@@ -1546,7 +1542,7 @@ class ApplicationForLeaveFormViewControllerTest {
         when(vacationTypeService.getById(1L)).thenReturn(Optional.of(vacationType));
 
         perform(
-            post("/web/application/7")
+            post("/web/application/7/edit")
                 .locale(locale)
                 .param("vacationType.id", "1")
                 .param("id", "7")
@@ -1601,7 +1597,7 @@ class ApplicationForLeaveFormViewControllerTest {
         when(vacationTypeService.getById(1L)).thenReturn(Optional.of(vacationType));
 
         perform(
-            post("/web/application/7")
+            post("/web/application/7/edit")
                 .locale(locale)
                 .param("vacationType.id", "1")
                 .param("id", "7")