Реализация логики на основе повторяющейся подписки в Django

У меня есть две модели. Модель плана, состоящая из трех планов, и модель подписки. Пользователю разрешено иметь только один активный план в одно время. Логика заключается в том, что когда пользователь впервые подписывается на план, по умолчанию status будет pending. После того, как администратор сайта подтвердит подписку (депозит), он переключит булево поле active, и автоматически статус изменится на Confirmed.

Теперь, где у меня возникают проблемы, так это с реализацией того, что происходит после подтверждения подписки. Я постараюсь объяснить логику как можно подробнее. Предполагается, что пользователь будет получать ежедневную прибыль до истечения срока действия плана. Позвольте мне привести пример: Предположим, пользователь А подписался на basic plan. Подписка basic plan имеет ежедневный процентный бонус 10% и длится 7 дней (duration). Теперь после того, как администратор активирует подписку, пользователь должен получать ежедневную прибыль в размере 10% от его инвестиций в течение 7 дней. Если предположить, что он подписывается на $100.00, то к концу первого дня его баланс должен составлять $110.00, и так далее. Затем, после истечения срока действия плана, статус должен автоматически измениться на Expired, поле active изменится на False, а процент перестанет прибавляться.

Вот мой вопрос, как реализовать эту логику ежедневного автоматического увеличения баланса пользователя до истечения срока действия его текущего плана, и как автоматически деактивировать подписку после истечения срока действия. Я пытался использовать циклы, но это была ужасная идея. Я видел несколько статей о Celery, RQ, threading, Cron и так далее. Но я не знаю, как поступить ни с одной из них. Может ли кто-нибудь, пожалуйста, помочь мне с решением проблемы. Я был бы признателен, если бы человек реализовал логику, используя любой инструмент по своему выбору, и объяснил мне идею, лежащую в ее основе. Вместо того чтобы направлять меня от одной статьи к другой. Спасибо. Если вам нужны дополнительные разъяснения, пожалуйста, дайте мне знать. Я использую PostgreSQL.

Вот моя модель плана

class Plan(models.Model):
    SUB_PLAN = (
        ('Basic', 'Basic Plan'),
        ('Gold', 'Gold Plan'),
        ('Platinum', 'Platinum Plan')
    )
    plan_name = models.CharField(max_length=8, choices=SUB_PLAN)
    percent = models.PositiveIntegerField()
    referral_bonus = models.PositiveIntegerField(default=10)
    min = models.DecimalField(max_digits=10, decimal_places=2)
    max = models.DecimalField(max_digits=10, decimal_places=2) 
    duration = models.PositiveIntegerField()

Вот моя модель подписки

class Subscription(models.Model):
    SUB_STATUS =(
        ('Pending', 'Pending'),
        ('Confirmed', 'Confirmed'),
        ('Expired', 'Expired')
    ) 
    user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name="user")
    type = models.CharField(max_length=10, default="Deposit", editable=False)
    plan = models.ForeignKey(Plan, on_delete=models.DO_NOTHING, related_name="plan")
    sub_method = models.CharField(max_length=24, choices=SUB_METHOD, default='Wallet')
    sub_currency = models.CharField(max_length=12, choices=SUB_CURRENCY, default='Btc')
    sub_amount = models.DecimalField(max_digits=1000, decimal_places=2, default=0.00)
    expires_at = models.DateField(null=True, blank=True)
    status = models.CharField(max_length=9, choices=SUB_STATUS, default="Pending")
    verified_on = models.DateTimeField(blank=True, null=True)
    active = models.BooleanField(default=False)
    initiated_on = models.DateTimeField(auto_now_add=True)

ЛОГИКА, ВОЗНИКАЮЩАЯ, КОГДА АДМИНИСТРАТОР ПЕРЕКЛЮЧАЕТ АКТИВНОЕ ПОЛЕ

@receiver(post_save, sender=Subscription)
def activate_subcription(sender, instance, created, *args, **kwargs):
    if instance.active == True:
        user=instance.user
        status = "Confirmed", verified_on = timezone.now()
        expires_at = date.today() + timedelta(days=instance.plan.duration)
        user.balance = F('balance') + instance.sub_amount
        user.save()
Вернуться на верх