Revamp generate-bills logic to avoid overlapping

This commit is contained in:
fnux 2020-02-28 09:58:01 +01:00
parent 37ed126bc1
commit adb57c55ca

View file

@ -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.")