Revamp generate-bills logic to avoid overlapping
This commit is contained in:
		
					parent
					
						
							
								37ed126bc1
							
						
					
				
			
			
				commit
				
					
						adb57c55ca
					
				
			
		
					 1 changed files with 44 additions and 25 deletions
				
			
		| 
						 | 
					@ -1,48 +1,67 @@
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
from uncloud_auth.models import User
 | 
					from uncloud_auth.models import User
 | 
				
			||||||
from uncloud_pay.models import Order, Bill
 | 
					from uncloud_pay.models import Order, Bill
 | 
				
			||||||
 | 
					from django.core.exceptions import ObjectDoesNotExist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from datetime import timedelta
 | 
					from datetime import timedelta
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BILL_PAYMENT_DELAY=timedelta(days=10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
    help = 'Generate bills and charge customers if necessary.'
 | 
					    help = 'Generate bills and charge customers if necessary.'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add_arguments(self, parser):
 | 
					    def add_arguments(self, parser):
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO: check for existing bills
 | 
					 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
        customers = User.objects.all()
 | 
					        users = User.objects.all()
 | 
				
			||||||
        print("Processing {} users.".format(customers.count()))
 | 
					        print("Processing {} users.".format(users.count()))
 | 
				
			||||||
        for customer in customers:
 | 
					 | 
				
			||||||
            orders = Order.objects.filter(owner=customer)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Pay all non-billed usage untill now.
 | 
					        for user in users:
 | 
				
			||||||
            bill_starting_date = timezone.now()
 | 
					            # Fetch all the orders of a customer.
 | 
				
			||||||
            bill_ending_date = timezone.now()
 | 
					            orders = Order.objects.filter(owner=user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            billed_orders = []
 | 
					            # 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:
 | 
					            for order in orders:
 | 
				
			||||||
                print(order)
 | 
					                # Only bill if there is an 'unpaid period' on an active order.
 | 
				
			||||||
                if True: # FIXME
 | 
					                # XXX: Assume everything before latest bill is paid. => might be dangerous.
 | 
				
			||||||
                    billed_orders.append(order)
 | 
					                try:
 | 
				
			||||||
 | 
					                    previous_bill = order.bill.latest('ending_date')
 | 
				
			||||||
 | 
					                except ObjectDoesNotExist:
 | 
				
			||||||
 | 
					                    previous_bill = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # Update starting date if need be.
 | 
					                is_unpaid_period = True
 | 
				
			||||||
                    if order.starting_date < bill_starting_date:
 | 
					                if order.ending_date and previous_bill != None:
 | 
				
			||||||
                        bill_starting_date = order.starting_date
 | 
					                    is_unpaid_period = previous_bill.ending_date < order.ending_date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if len(billed_orders) > 0:
 | 
					                if is_unpaid_period:
 | 
				
			||||||
              bill = Bill(owner=customer,
 | 
					                    # Update bill starting date to match period.
 | 
				
			||||||
                       starting_date=bill_starting_date,
 | 
					                    if previous_bill == None:
 | 
				
			||||||
                       ending_date=bill_starting_date,
 | 
					                        next_bill.starting_date = order.starting_date
 | 
				
			||||||
                       due_date=timezone.now() + timedelta(days=10))
 | 
					                    elif previous_bill.ending_date < next_bill.starting_date:
 | 
				
			||||||
              bill.save()
 | 
					                        next_bill.starting_date = previous_bill.ending_date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              for order in billed_orders:
 | 
					                    # Add order to bill
 | 
				
			||||||
                  print(order)
 | 
					                    unpaid_orders.append(order)
 | 
				
			||||||
                  order.bill.add(bill)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
              print("Created bill {} for user {}".format(bill.uuid, customer.username))
 | 
					            # 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.")
 | 
					        print("=> Done.")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue