from django.core.management.base import BaseCommand from django.utils import timezone from django.conf import settings from django.template.loader import render_to_string from plans.models import UserPlan, Order from datetime import timedelta from django.utils.translation import activate, get_language from django_q.tasks import async_task import logging from inventory.tasks import send_bilingual_reminder, handle_email_result logger = logging.getLogger(__name__) class Command(BaseCommand): help = "Handles subscription plan maintenance tasks" def handle(self, *args, **options): self.stdout.write("Starting plans maintenance...") # 1. Send expiration reminders self.send_expiration_reminders() # 2. Deactivate expired plans self.deactivate_expired_plans() # 3. Clean up old incomplete orders self.cleanup_old_orders() self.stdout.write("Maintenance completed!") def send_expiration_reminders(self): """Queue email reminders for expiring plans""" reminder_days = getattr(settings, "PLANS_EXPIRATION_REMIND", [3, 7, 14]) today = timezone.now().date() for days in reminder_days: target_date = today + timedelta(days=days) expiring_plans = UserPlan.objects.filter( active=True, expire=target_date ).select_related("user", "plan") self.stdout.write( f"Queuing {days}-day reminders for {expiring_plans.count()} plans" ) for user_plan in expiring_plans: # Queue email task async_task( send_bilingual_reminder, user_plan.user_id, user_plan.plan_id, user_plan.expire, days, hook=handle_email_result, ) def deactivate_expired_plans(self): """Deactivate plans that have expired (synchronous)""" expired_plans = UserPlan.objects.filter( active=True, expire__lt=timezone.now().date() ) count = expired_plans.update(active=False) self.stdout.write(f"Deactivated {count} expired plans") def cleanup_old_orders(self): """Delete incomplete orders older than 30 days""" cutoff = timezone.now() - timedelta(days=30) count, _ = Order.objects.filter( created__lt=cutoff, status=Order.STATUS.NEW ).delete() self.stdout.write(f"Cleaned up {count} old incomplete orders")