diff --git a/local_units/migrations/0018_localunit_deprecated_reason_and_more.py b/local_units/migrations/0018_localunit_deprecated_reason_and_more.py new file mode 100644 index 000000000..0e79fccb3 --- /dev/null +++ b/local_units/migrations/0018_localunit_deprecated_reason_and_more.py @@ -0,0 +1,100 @@ +# Generated by Django 4.2.16 on 2024-11-29 11:02 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("local_units", "0017_alter_healthdata_other_medical_heal"), + ] + + operations = [ + migrations.AddField( + model_name="localunit", + name="deprecated_reason", + field=models.IntegerField( + blank=True, + choices=[ + (1, "Non-existent local unit"), + (2, "Incorrectly added local unit"), + (3, "Security concerns"), + (4, "Other"), + ], + null=True, + verbose_name="deprecated reason", + ), + ), + migrations.AddField( + model_name="localunit", + name="deprecated_reason_overview", + field=models.TextField(blank=True, null=True, verbose_name="Explain the reason why the local unit is being deleted"), + ), + migrations.AddField( + model_name="localunit", + name="is_deprecated", + field=models.BooleanField(blank=True, default=False, null=True, verbose_name="Is deprecated?"), + ), + migrations.AddField( + model_name="localunit", + name="status", + field=models.IntegerField( + blank=True, choices=[(1, "Verified"), (2, "Unverified")], default=2, null=True, verbose_name="status" + ), + ), + migrations.CreateModel( + name="LocalUnitChangeRequest", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("previous_data", models.JSONField(default=dict, verbose_name="Previous data")), + ( + "status", + models.IntegerField( + choices=[(1, "Pending"), (2, "Approved"), (3, "Revert")], default=1, verbose_name="status" + ), + ), + ( + "current_validator", + models.IntegerField( + choices=[(1, "Local"), (2, "Regional"), (3, "Global")], default=1, verbose_name="Current validator" + ), + ), + ("triggered_at", models.DateTimeField(auto_now=True, verbose_name="Triggered at")), + ("updated_at", models.DateTimeField(auto_now=True, verbose_name="Updated at")), + ("rejected_data", models.JSONField(default=dict, verbose_name="Rejected data")), + ("rejected_reason", models.TextField(blank=True, null=True, verbose_name="Rejected reason")), + ( + "local_unit", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="local_unit_change_request", + to="local_units.localunit", + verbose_name="Local Unit", + ), + ), + ( + "triggered_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="tiggered_by_local_unit", + to=settings.AUTH_USER_MODEL, + verbose_name="triggered by", + ), + ), + ( + "updated_by", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="updated_by_local_unit", + to=settings.AUTH_USER_MODEL, + verbose_name="updated by", + ), + ), + ], + ), + ] diff --git a/local_units/migrations/0019_alter_localunit_is_deprecated_alter_localunit_status_and_more.py b/local_units/migrations/0019_alter_localunit_is_deprecated_alter_localunit_status_and_more.py new file mode 100644 index 000000000..8d866a5d3 --- /dev/null +++ b/local_units/migrations/0019_alter_localunit_is_deprecated_alter_localunit_status_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.16 on 2024-12-02 11:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("local_units", "0018_localunit_deprecated_reason_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="localunit", + name="is_deprecated", + field=models.BooleanField(default=False, verbose_name="Is deprecated?"), + ), + migrations.AlterField( + model_name="localunit", + name="status", + field=models.IntegerField(choices=[(1, "Verified"), (2, "Unverified")], default=2, verbose_name="status"), + ), + migrations.AlterField( + model_name="localunitchangerequest", + name="triggered_at", + field=models.DateTimeField(auto_now_add=True, verbose_name="Triggered at"), + ), + ] diff --git a/local_units/models.py b/local_units/models.py index 9033d5090..cce3745dd 100644 --- a/local_units/models.py +++ b/local_units/models.py @@ -277,6 +277,16 @@ def __str__(self): @reversion.register(follow=("health",)) class LocalUnit(models.Model): + class Status(models.IntegerChoices): + VERIFIED = 1, _("Verified") + UNVERIFIED = 2, _("Unverified") + + class DeprecateReason(models.IntegerChoices): + NON_EXISTENT = 1, _("Non-existent local unit") + INCORRECTLY_ADDED = 2, _("Incorrectly added local unit") + SECURITY_CONCERNS = 3, _("Security concerns") + OTHER = 4, _("Other") + # added to track health local unit data (Table B) health = models.ForeignKey( HealthData, on_delete=models.SET_NULL, verbose_name=_("Health Data"), related_name="health_data", null=True, blank=True @@ -340,12 +350,68 @@ class LocalUnit(models.Model): email = models.EmailField(max_length=255, blank=True, null=True, verbose_name=_("Email")) link = models.URLField(max_length=255, blank=True, null=True, verbose_name=_("Social link")) location = models.PointField(srid=4326, help_text="Local Unit Location") + status = models.IntegerField(choices=Status.choices, verbose_name=_("status"), default=Status.UNVERIFIED) + is_deprecated = models.BooleanField(default=False, verbose_name=_("Is deprecated?")) + deprecated_reason = models.IntegerField( + choices=DeprecateReason.choices, verbose_name=_("deprecated reason"), blank=True, null=True + ) + deprecated_reason_overview = models.TextField( + verbose_name=_("Explain the reason why the local unit is being deleted"), blank=True, null=True + ) def __str__(self): branch_name = self.local_branch_name or self.english_branch_name return f"{branch_name} ({self.country.name})" +class LocalUnitChangeRequest(models.Model): + + class Status(models.IntegerChoices): + PENDING = 1, _("Pending") + APPROVED = 2, _("Approved") + REVERT = 3, _("Revert") + + class Validator(models.IntegerChoices): + LOCAL = 1, _("Local") + REGIONAL = 2, _("Regional") + GLOBAL = 3, _("Global") + + local_unit = models.ForeignKey( + LocalUnit, on_delete=models.CASCADE, verbose_name=_("Local Unit"), related_name="local_unit_change_request" + ) + previous_data = models.JSONField(verbose_name=_("Previous data"), default=dict) + status = models.IntegerField(choices=Status.choices, verbose_name=_("status"), default=Status.PENDING) + current_validator = models.IntegerField( + choices=Validator.choices, verbose_name=_("Current validator"), default=Validator.LOCAL + ) + + # NOTE: triggered_by is the user who created the request + triggered_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + verbose_name=_("triggered by"), + on_delete=models.SET_NULL, + null=True, + related_name="tiggered_by_local_unit", + ) + triggered_at = models.DateTimeField(verbose_name=_("Triggered at"), auto_now_add=True) + + # NOTE: updated_by is the user who approved/revert the request + updated_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + verbose_name=_("updated by"), + on_delete=models.SET_NULL, + null=True, + related_name="updated_by_local_unit", + ) + updated_at = models.DateTimeField(verbose_name=_("Updated at"), auto_now=True) + rejected_data = models.JSONField(verbose_name=_("Rejected data"), default=dict) + rejected_reason = models.TextField(verbose_name=_("Rejected reason"), blank=True, null=True) + + def __str__(self): + branch_name = self.local_branch_name or self.english_branch_name + return f"{branch_name}-Change Request-{self.id}" + + class DelegationOfficeType(models.Model): code = models.IntegerField(verbose_name=_("Type Code"), validators=[MaxValueValidator(10), MinValueValidator(0)]) name = models.CharField(max_length=100, verbose_name=_("Name"))