From adb57c55ca0c439a0577ebb1a0c24fbb678350ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Fri, 28 Feb 2020 09:58:01 +0100 Subject: [PATCH] Revamp generate-bills logic to avoid overlapping --- .../management/commands/generate-bills.py | 69 ++++++++++++------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/uncloud/uncloud_pay/management/commands/generate-bills.py b/uncloud/uncloud_pay/management/commands/generate-bills.py index 92075ce..aad7a82 100644 --- a/uncloud/uncloud_pay/management/commands/generate-bills.py +++ b/uncloud/uncloud_pay/management/commands/generate-bills.py @@ -1,48 +1,67 @@ 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 - # TODO: check for existing bills def handle(self, *args, **options): - customers = User.objects.all() - print("Processing {} users.".format(customers.count())) - for customer in customers: - orders = Order.objects.filter(owner=customer) + users = User.objects.all() + print("Processing {} users.".format(users.count())) - # Pay all non-billed usage untill now. - bill_starting_date = timezone.now() - bill_ending_date = timezone.now() + for user in users: + # Fetch all the orders of a customer. + 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: - print(order) - if True: # FIXME - billed_orders.append(order) + # 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 - # Update starting date if need be. - if order.starting_date < bill_starting_date: - bill_starting_date = order.starting_date + is_unpaid_period = True + if order.ending_date and previous_bill != None: + is_unpaid_period = previous_bill.ending_date < order.ending_date - if len(billed_orders) > 0: - bill = Bill(owner=customer, - starting_date=bill_starting_date, - ending_date=bill_starting_date, - due_date=timezone.now() + timedelta(days=10)) - bill.save() + 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 - for order in billed_orders: - print(order) - order.bill.add(bill) + # Add order to bill + unpaid_orders.append(order) - 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.")