diff --git a/src/openapi.yaml b/src/openapi.yaml
index f7dbb3086b..29aee529b7 100644
--- a/src/openapi.yaml
+++ b/src/openapi.yaml
@@ -10240,7 +10240,7 @@ components:
successfulSubmissionsRemovalLimit:
type: integer
maximum: 2147483647
- minimum: 1
+ minimum: 0
nullable: true
title: Successful submission removal limit
description: Amount of days successful submissions of this form will remain
@@ -10257,7 +10257,7 @@ components:
incompleteSubmissionsRemovalLimit:
type: integer
maximum: 2147483647
- minimum: 1
+ minimum: 0
nullable: true
title: Incomplete submission removal limit
description: Amount of days incomplete submissions of this form will remain
@@ -10274,7 +10274,7 @@ components:
erroredSubmissionsRemovalLimit:
type: integer
maximum: 2147483647
- minimum: 1
+ minimum: 0
nullable: true
title: Errored submission removal limit
description: Amount of days errored submissions of this form will remain
@@ -10292,7 +10292,7 @@ components:
allSubmissionsRemovalLimit:
type: integer
maximum: 2147483647
- minimum: 1
+ minimum: 0
nullable: true
description: Amount of days when all submissions of this form will be permanently
deleted. Leave blank to use value in General Configuration.
diff --git a/src/openforms/config/migrations/0064_alter_globalconfiguration_submissions_removal_limit.py b/src/openforms/config/migrations/0064_alter_globalconfiguration_submissions_removal_limit.py
new file mode 100644
index 0000000000..2640cc362c
--- /dev/null
+++ b/src/openforms/config/migrations/0064_alter_globalconfiguration_submissions_removal_limit.py
@@ -0,0 +1,54 @@
+# Generated by Django 4.2.16 on 2024-11-11 14:08
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("config", "0063_merge_20240923_1612"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="globalconfiguration",
+ name="all_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ default=90,
+ help_text="Amount of days when all submissions will be permanently deleted",
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="all submissions removal limit",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="globalconfiguration",
+ name="errored_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ default=30,
+ help_text="Amount of days errored submissions will remain before being removed",
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="errored submission removal limit",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="globalconfiguration",
+ name="incomplete_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ default=7,
+ help_text="Amount of days incomplete submissions will remain before being removed",
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="incomplete submission removal limit",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="globalconfiguration",
+ name="successful_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ default=7,
+ help_text="Amount of days successful submissions will remain before being removed",
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="successful submission removal limit",
+ ),
+ ),
+ ]
diff --git a/src/openforms/config/models/config.py b/src/openforms/config/models/config.py
index a616f8d15f..980e74e8fc 100644
--- a/src/openforms/config/models/config.py
+++ b/src/openforms/config/models/config.py
@@ -421,7 +421,7 @@ class GlobalConfiguration(SingletonModel):
successful_submissions_removal_limit = models.PositiveIntegerField(
_("successful submission removal limit"),
default=7,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_(
"Amount of days successful submissions will remain before being removed"
),
@@ -436,7 +436,7 @@ class GlobalConfiguration(SingletonModel):
incomplete_submissions_removal_limit = models.PositiveIntegerField(
_("incomplete submission removal limit"),
default=7,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_(
"Amount of days incomplete submissions will remain before being removed"
),
@@ -451,7 +451,7 @@ class GlobalConfiguration(SingletonModel):
errored_submissions_removal_limit = models.PositiveIntegerField(
_("errored submission removal limit"),
default=30,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_(
"Amount of days errored submissions will remain before being removed"
),
@@ -466,7 +466,7 @@ class GlobalConfiguration(SingletonModel):
all_submissions_removal_limit = models.PositiveIntegerField(
_("all submissions removal limit"),
default=90,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_("Amount of days when all submissions will be permanently deleted"),
)
diff --git a/src/openforms/data_removal/tests/test_tasks.py b/src/openforms/data_removal/tests/test_tasks.py
index 49b980d01d..0dca1d6f30 100644
--- a/src/openforms/data_removal/tests/test_tasks.py
+++ b/src/openforms/data_removal/tests/test_tasks.py
@@ -58,6 +58,34 @@ def test_successful_submissions_correctly_deleted(self):
with self.assertRaises(ObjectDoesNotExist):
submission_to_be_deleted.refresh_from_db()
+ def test_successful_submissions_correctly_deleted_the_same_day_when_form_removal_limit_is_0(
+ self,
+ ):
+ form = FormFactory.create(
+ successful_submissions_removal_limit=0,
+ incomplete_submissions_removal_limit=7,
+ errored_submissions_removal_limit=7,
+ all_submissions_removal_limit=7,
+ )
+
+ with freeze_time("2024-11-12T18:00:00+01:00"):
+ # Submission not connected to form
+ SubmissionFactory.create(registration_success=True)
+
+ submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_success=True
+ )
+
+ self.assertEqual(Submission.objects.count(), 2)
+
+ with freeze_time("2024-11-12T19:00:00+01:00"):
+ delete_submissions()
+
+ # Only the submission connected to the form should be deleted
+ self.assertEqual(Submission.objects.count(), 1)
+ with self.assertRaises(ObjectDoesNotExist):
+ submission_to_be_deleted.refresh_from_db()
+
@tag("gh-2632")
def test_delete_successful_submission_with_deleted_form_step(self):
config = GlobalConfiguration.get_solo()
@@ -173,8 +201,44 @@ def test_incomplete_submissions_correctly_deleted(self):
self.assertEqual(Submission.objects.count(), 5)
with self.assertRaises(ObjectDoesNotExist):
pending_submission_to_be_deleted.refresh_from_db()
+ with self.assertRaises(ObjectDoesNotExist):
+ in_progress_submission_to_be_deleted.refresh_from_db()
+
+ def test_incomplete_submissions_correctly_deleted_the_same_day_when_form_removal_limit_is_0(
+ self,
+ ):
+ form = FormFactory.create(
+ successful_submissions_removal_limit=7,
+ incomplete_submissions_removal_limit=0,
+ errored_submissions_removal_limit=7,
+ all_submissions_removal_limit=7,
+ )
+
+ with freeze_time("2024-11-12T18:00:00+01:00"):
+ # Incomplete submissions not connected to the form
+ SubmissionFactory.create(registration_status=RegistrationStatuses.pending)
+ SubmissionFactory.create(
+ registration_status=RegistrationStatuses.in_progress
+ )
+
+ SubmissionFactory.create(form=form, registration_success=True)
+ pending_submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.pending
+ )
+ in_progress_submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.in_progress
+ )
+
+ self.assertEqual(Submission.objects.count(), 5)
+
+ with freeze_time("2024-11-12T19:00:00+01:00"):
+ delete_submissions()
+
+ self.assertEqual(Submission.objects.count(), 3)
with self.assertRaises(ObjectDoesNotExist):
pending_submission_to_be_deleted.refresh_from_db()
+ with self.assertRaises(ObjectDoesNotExist):
+ in_progress_submission_to_be_deleted.refresh_from_db()
def test_incomplete_submissions_with_form_settings_override_global_configuration(
self,
@@ -277,6 +341,42 @@ def test_failed_submissions_correctly_deleted(self):
with self.assertRaises(ObjectDoesNotExist):
submission_to_be_deleted.refresh_from_db()
+ def test_failed_submissions_correctly_deleted_the_same_day_when_form_removal_limit_is_0(
+ self,
+ ):
+ form = FormFactory.create(
+ successful_submissions_removal_limit=7,
+ incomplete_submissions_removal_limit=7,
+ errored_submissions_removal_limit=0,
+ all_submissions_removal_limit=7,
+ )
+
+ with freeze_time("2024-11-12T18:00:00+01:00"):
+ # Failed submission not connected to the form
+ SubmissionFactory.create(registration_status=RegistrationStatuses.failed)
+
+ # Successful and incomplete submissions
+ SubmissionFactory.create(form=form, registration_success=True)
+ SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.pending
+ )
+ SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.in_progress
+ )
+
+ failed_submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.failed
+ )
+
+ self.assertEqual(Submission.objects.count(), 5)
+
+ with freeze_time("2024-11-12T19:00:00+01:00"):
+ delete_submissions()
+
+ self.assertEqual(Submission.objects.count(), 4)
+ with self.assertRaises(ObjectDoesNotExist):
+ failed_submission_to_be_deleted.refresh_from_db()
+
def test_failed_submissions_with_form_settings_override_global_configuration(self):
config = GlobalConfiguration.get_solo()
form_longer_limit = FormFactory.create(
@@ -339,6 +439,53 @@ def test_all_submissions_correctly_deleted(self):
with self.assertRaises(ObjectDoesNotExist):
old_submission.refresh_from_db()
+ def test_all_submissions_correctly_deleted_the_same_day_when_form_removal_limit_is_0(
+ self,
+ ):
+ form = FormFactory.create(
+ successful_submissions_removal_limit=7,
+ incomplete_submissions_removal_limit=7,
+ errored_submissions_removal_limit=7,
+ all_submissions_removal_limit=0,
+ )
+
+ with freeze_time("2024-11-12T18:00:00+01:00"):
+ # Submissions not connected to the form
+ SubmissionFactory.create(registration_success=True)
+ SubmissionFactory.create(registration_status=RegistrationStatuses.pending)
+ SubmissionFactory.create(
+ registration_status=RegistrationStatuses.in_progress
+ )
+ SubmissionFactory.create(registration_status=RegistrationStatuses.failed)
+
+ submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_success=True
+ )
+ pending_submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.pending
+ )
+ in_progress_submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.in_progress
+ )
+ failed_submission_to_be_deleted = SubmissionFactory.create(
+ form=form, registration_status=RegistrationStatuses.failed
+ )
+
+ self.assertEqual(Submission.objects.count(), 8)
+
+ with freeze_time("2024-11-12T19:00:00+01:00"):
+ delete_submissions()
+
+ self.assertEqual(Submission.objects.count(), 4)
+ with self.assertRaises(ObjectDoesNotExist):
+ submission_to_be_deleted.refresh_from_db()
+ with self.assertRaises(ObjectDoesNotExist):
+ pending_submission_to_be_deleted.refresh_from_db()
+ with self.assertRaises(ObjectDoesNotExist):
+ in_progress_submission_to_be_deleted.refresh_from_db()
+ with self.assertRaises(ObjectDoesNotExist):
+ failed_submission_to_be_deleted.refresh_from_db()
+
def test_all_submissions_with_form_settings_override_global_configuration(self):
config = GlobalConfiguration.get_solo()
form_longer_limit = FormFactory.create(
diff --git a/src/openforms/forms/migrations/0105_alter_form_all_submissions_removal_limit_and_more.py b/src/openforms/forms/migrations/0105_alter_form_all_submissions_removal_limit_and_more.py
new file mode 100644
index 0000000000..e3b54c84cc
--- /dev/null
+++ b/src/openforms/forms/migrations/0105_alter_form_all_submissions_removal_limit_and_more.py
@@ -0,0 +1,58 @@
+# Generated by Django 4.2.16 on 2024-11-14 09:31
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("forms", "0104_select_datatype_string"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="form",
+ name="all_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ blank=True,
+ help_text="Amount of days when all submissions of this form will be permanently deleted. Leave blank to use value in General Configuration.",
+ null=True,
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="all submissions removal limit",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="form",
+ name="errored_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ blank=True,
+ help_text="Amount of days errored submissions of this form will remain before being removed. Leave blank to use value in General Configuration.",
+ null=True,
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="errored submission removal limit",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="form",
+ name="incomplete_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ blank=True,
+ help_text="Amount of days incomplete submissions of this form will remain before being removed. Leave blank to use value in General Configuration.",
+ null=True,
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="incomplete submission removal limit",
+ ),
+ ),
+ migrations.AlterField(
+ model_name="form",
+ name="successful_submissions_removal_limit",
+ field=models.PositiveIntegerField(
+ blank=True,
+ help_text="Amount of days successful submissions of this form will remain before being removed. Leave blank to use value in General Configuration.",
+ null=True,
+ validators=[django.core.validators.MinValueValidator(0)],
+ verbose_name="successful submission removal limit",
+ ),
+ ),
+ ]
diff --git a/src/openforms/forms/models/form.py b/src/openforms/forms/models/form.py
index b45ab28fdc..b08c994c73 100644
--- a/src/openforms/forms/models/form.py
+++ b/src/openforms/forms/models/form.py
@@ -295,7 +295,7 @@ class Form(models.Model):
_("successful submission removal limit"),
blank=True,
null=True,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_(
"Amount of days successful submissions of this form will remain before being removed. "
"Leave blank to use value in General Configuration."
@@ -315,7 +315,7 @@ class Form(models.Model):
_("incomplete submission removal limit"),
blank=True,
null=True,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_(
"Amount of days incomplete submissions of this form will remain before being removed. "
"Leave blank to use value in General Configuration."
@@ -335,7 +335,7 @@ class Form(models.Model):
_("errored submission removal limit"),
blank=True,
null=True,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_(
"Amount of days errored submissions of this form will remain before being removed. "
"Leave blank to use value in General Configuration."
@@ -355,7 +355,7 @@ class Form(models.Model):
_("all submissions removal limit"),
blank=True,
null=True,
- validators=[MinValueValidator(1)],
+ validators=[MinValueValidator(0)],
help_text=_(
"Amount of days when all submissions of this form will be permanently deleted. "
"Leave blank to use value in General Configuration."
diff --git a/src/openforms/js/components/admin/form_design/DataRemoval.js b/src/openforms/js/components/admin/form_design/DataRemoval.js
index 7c8811ea24..1eb1e06bbd 100644
--- a/src/openforms/js/components/admin/form_design/DataRemoval.js
+++ b/src/openforms/js/components/admin/form_design/DataRemoval.js
@@ -61,7 +61,7 @@ const DataRemoval = ({submissionsRemovalOptions, onChange}) => {
@@ -109,7 +109,7 @@ const DataRemoval = ({submissionsRemovalOptions, onChange}) => {
@@ -154,7 +154,7 @@ const DataRemoval = ({submissionsRemovalOptions, onChange}) => {
/>
}
>
-
+
@@ -198,7 +198,7 @@ const DataRemoval = ({submissionsRemovalOptions, onChange}) => {
/>
}
>
-
+