from django.core.management.base import BaseCommand from uncloud_auth.models import User from uncloud_pay.models import Order, Bill from django.core.exceptions import ObjectDoesNotExist from datetime import timedelta from django.utils import timezone BILL_PAYMENT_DELAY=timedelta(days=10) class Command(BaseCommand): help = 'Generate bills and charge customers if necessary.' def add_arguments(self, parser): pass def handle(self, *args, **options): users = User.objects.all() print("Processing {} users.".format(users.count())) for user in users: # Fetch all the orders of a customer. orders = Order.objects.filter(owner=user) # Default values for next bill (if any). Only saved at the end of # this method, if relevant. next_bill = Bill(owner=user, starting_date=timezone.now(), # Will be set to oldest unpaid order (means unpaid starting date). ending_date=timezone.now(), # Bill covers everything until today. due_date=timezone.now() + BILL_PAYMENT_DELAY) unpaid_orders = [] # Store orders in need of a payment. for order in orders: # Only bill if there is an 'unpaid period' on an active order. # XXX: Assume everything before latest bill is paid. => might be dangerous. try: previous_bill = order.bill.latest('ending_date') except ObjectDoesNotExist: previous_bill = None is_unpaid_period = True if order.ending_date and previous_bill != None: is_unpaid_period = previous_bill.ending_date < order.ending_date if is_unpaid_period: # Update bill starting date to match period. if previous_bill == None: next_bill.starting_date = order.starting_date elif previous_bill.ending_date < next_bill.starting_date: next_bill.starting_date = previous_bill.ending_date # Add order to bill unpaid_orders.append(order) # Save next_bill if it contains any unpaid product. if len(unpaid_orders) > 0: next_bill.save() # It is not possible to register many-to-many relationship before # the two end-objects are saved in database. for order in unpaid_orders: order.bill.add(next_bill) print("Created bill {} for user {}".format(next_bill.uuid, user.username)) # We're done for this round :-) print("=> Done.")