diff --git a/temba/contacts/migrations/0199_contactfire.py b/temba/contacts/migrations/0199_contactfire.py new file mode 100644 index 0000000000..b5b9941274 --- /dev/null +++ b/temba/contacts/migrations/0199_contactfire.py @@ -0,0 +1,45 @@ +# Generated by Django 5.1.4 on 2025-01-17 15:15 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("contacts", "0198_squashed"), + ("orgs", "0165_grant_prometheus_feature"), + ] + + operations = [ + migrations.CreateModel( + name="ContactFire", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ( + "fire_type", + models.CharField(choices=[("E", "Expiration"), ("T", "Timeout"), ("C", "Campaign")], max_length=1), + ), + ("scope", models.CharField(max_length=64)), + ("extra", models.JSONField(default=dict)), + ( + "contact", + models.ForeignKey( + db_index=False, + on_delete=django.db.models.deletion.PROTECT, + related_name="fires", + to="contacts.contact", + ), + ), + ("fire_on", models.DateTimeField(db_index=True)), + ("org", models.ForeignKey(db_index=False, on_delete=django.db.models.deletion.PROTECT, to="orgs.org")), + ], + options={ + "constraints": [ + models.UniqueConstraint( + fields=("contact", "fire_type", "scope"), name="fire_contact_type_scope_unique" + ) + ], + }, + ), + ] diff --git a/temba/contacts/models.py b/temba/contacts/models.py index 38ac418e3e..58d609dd23 100644 --- a/temba/contacts/models.py +++ b/temba/contacts/models.py @@ -1156,6 +1156,8 @@ def _full_release(self): # and any event fire history self.campaign_fires.all().delete() + delete_in_batches(self.fires.all()) + # take us out of broadcast addressed contacts for broadcast in self.addressed_broadcasts.all(): broadcast.contacts.remove(self) @@ -1734,6 +1736,31 @@ class Meta: ] +class ContactFire(models.Model): + """ + Something to happen to a contact in the future - processed by mailroom. + """ + + TYPE_WAIT_EXPIRATION = "E" + TYPE_WAIT_TIMEOUT = "T" + TYPE_CAMPAIGN = "C" + TYPE_CHOICES = ((TYPE_WAIT_EXPIRATION, "Expiration"), (TYPE_WAIT_TIMEOUT, "Timeout"), (TYPE_CAMPAIGN, "Campaign")) + + id = models.BigAutoField(auto_created=True, primary_key=True) + org = models.ForeignKey(Org, on_delete=models.PROTECT, db_index=False) + contact = models.ForeignKey(Contact, on_delete=models.PROTECT, related_name="fires", db_index=False) # index below + fire_type = models.CharField(max_length=1, choices=TYPE_CHOICES) + scope = models.CharField(max_length=64) # e.g. campaign event id + extra = models.JSONField(default=dict) # e.g. session id + fire_on = models.DateTimeField(db_index=True) + + class Meta: + constraints = [ + # used to prevent adding duplicate fires for the same contact and scope + models.UniqueConstraint(name="fire_contact_type_scope_unique", fields=("contact", "fire_type", "scope")) + ] + + class ContactExport(ExportType): """ Export of contacts