From ea03786da86acd8601ab5555d471f7ebd7c4e92a Mon Sep 17 00:00:00 2001 From: imperosol Date: Sun, 14 Sep 2025 21:47:51 +0200 Subject: [PATCH] `ScheduledProductAction` model to store tasks related to products --- .../migrations/0032_scheduledproductaction.py | 43 +++++++++++++++++++ counter/models.py | 28 ++++++++++++ counter/tasks.py | 11 ++++- 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 counter/migrations/0032_scheduledproductaction.py diff --git a/counter/migrations/0032_scheduledproductaction.py b/counter/migrations/0032_scheduledproductaction.py new file mode 100644 index 00000000..40d00b9a --- /dev/null +++ b/counter/migrations/0032_scheduledproductaction.py @@ -0,0 +1,43 @@ +# Generated by Django 5.2.3 on 2025-09-14 11:29 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("counter", "0031_alter_counter_options"), + ("django_celery_beat", "0019_alter_periodictasks_options"), + ] + + operations = [ + migrations.CreateModel( + name="ScheduledProductAction", + fields=[ + ( + "periodictask_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="django_celery_beat.periodictask", + ), + ), + ( + "product", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="scheduled_actions", + to="counter.product", + ), + ), + ], + options={ + "verbose_name": "Product scheduled action", + "ordering": ["-clocked"], + }, + bases=("django_celery_beat.periodictask",), + ), + ] diff --git a/counter/models.py b/counter/models.py index c1181de7..7c461886 100644 --- a/counter/models.py +++ b/counter/models.py @@ -34,6 +34,7 @@ from django.urls import reverse from django.utils import timezone from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ +from django_celery_beat.models import PeriodicTask from django_countries.fields import CountryField from ordered_model.models import OrderedModel from phonenumber_field.modelfields import PhoneNumberField @@ -1363,3 +1364,30 @@ class ReturnableProductBalance(models.Model): f"return balance of {self.customer} " f"for {self.returnable.product_id} : {self.balance}" ) + + +def get_product_actions(): + return [ + ("counter.tasks.archive_product", _("Archiving")), + ("counter.tasks.change_counters", _("Counters change")), + ] + + +class ScheduledProductAction(PeriodicTask): + """Extension of celery-beat tasks dedicated to perform actions on Product.""" + + product = models.ForeignKey( + Product, related_name="scheduled_actions", on_delete=models.CASCADE + ) + + class Meta: + verbose_name = _("Product scheduled action") + ordering = ["-clocked"] + + def __init__(self, *args, **kwargs): + self._meta.get_field("task").choices = get_product_actions() + super().__init__(*args, **kwargs) + + def save(self, *args, **kwargs): + self.one_off = True # A product action should occur one time only + return super().save(*args, **kwargs) diff --git a/counter/tasks.py b/counter/tasks.py index 6c6a46b7..19245e36 100644 --- a/counter/tasks.py +++ b/counter/tasks.py @@ -2,12 +2,19 @@ from celery import shared_task -from counter.models import Product +from counter.models import Counter, Product @shared_task -def archive_product(product_id): +def archive_product(*, product_id: int): product = Product.objects.get(id=product_id) product.archived = True product.save() product.counters.clear() + + +@shared_task +def change_counters(*, product_id: int, counters: list[int]): + product = Product.objects.get(id=product_id) + counters = Counter.objects.filter(id__in=counters) + product.counters.set(counters)