Initial product activation implementation

This commit is contained in:
fnux 2020-03-10 09:14:52 +01:00
parent 839bd5f8a9
commit 5d5bf486b5
3 changed files with 43 additions and 24 deletions

View file

@ -1,6 +1,6 @@
from django.core.management.base import BaseCommand
from uncloud_auth.models import User
from uncloud_pay.models import Order, Bill, PaymentMethod, get_balance_for
from uncloud_pay.models import Order, Bill, PaymentMethod, get_balance_for_user
from datetime import timedelta
from django.utils import timezone
@ -15,7 +15,7 @@ class Command(BaseCommand):
users = User.objects.all()
print("Processing {} users.".format(users.count()))
for user in users:
balance = get_balance_for(user)
balance = get_balance_for_user(user)
if balance < 0:
print("User {} has negative balance ({}), charging.".format(user.username, balance))
payment_method = PaymentMethod.get_primary_for(user)

View file

@ -92,19 +92,19 @@ class Payment(models.Model):
default='unknown')
timestamp = models.DateTimeField(editable=False, auto_now_add=True)
# WIP prepaid and service activation logic by fnux.
## We override save() in order to active products awaiting payment.
#def save(self, *args, **kwargs):
# # TODO: only run activation logic on creation, not on update.
# unpaid_bills_before_payment = Bill.get_unpaid_for(self.owner)
# super(Payment, self).save(*args, **kwargs) # Save payment in DB.
# unpaid_bills_after_payment = Bill.get_unpaid_for(self.owner)
# We override save() in order to active products awaiting payment.
def save(self, *args, **kwargs):
# _state.adding is switched to false after super(...) call.
being_created = self._state.adding
# newly_paid_bills = list(
# set(unpaid_bills_before_payment) - set(unpaid_bills_after_payment))
# for bill in newly_paid_bills:
# bill.activate_orders()
unpaid_bills_before_payment = Bill.get_unpaid_for(self.owner)
super(Payment, self).save(*args, **kwargs) # Save payment in DB.
unpaid_bills_after_payment = Bill.get_unpaid_for(self.owner)
newly_paid_bills = list(
set(unpaid_bills_before_payment) - set(unpaid_bills_after_payment))
for bill in newly_paid_bills:
bill.activate_products()
class PaymentMethod(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
@ -201,6 +201,12 @@ class Bill(models.Model):
valid = models.BooleanField(default=True)
# Trigger product activation if bill paid at creation (from balance).
def save(self, *args, **kwargs):
super(Bill, self).save(*args, **kwargs)
if not self in Bill.get_unpaid_for(self.owner):
self.activate_products()
@property
def reference(self):
return "{}-{}".format(
@ -227,6 +233,15 @@ class Bill(models.Model):
# A bill is final when its ending date is passed.
return self.ending_date < timezone.now()
def activate_products(self):
for order in self.order_set.all():
# FIXME: using __something might not be a good idea.
for product_class in Product.__subclasses__():
for product in product_class.objects.filter(order=order):
if product.status == UncloudStatus.AWAITING_PAYMENT:
product.status = UncloudStatus.PENDING
product.save()
@staticmethod
def generate_for(year, month, user):
# /!\ We exclusively work on the specified year and month.
@ -248,7 +263,7 @@ class Bill(models.Model):
# (next_bill) ending_date, a new bill has to be generated.
# * For yearly bill: if previous_bill.ending_date is on working
# month, generate new bill.
unpaid_orders = { 'monthly_or_less': [], 'yearly': {}}
unpaid_orders = { 'monthly_or_less': [], 'yearly': {} }
for order in orders:
try:
previous_bill = order.bill.latest('ending_date')
@ -276,7 +291,7 @@ class Bill(models.Model):
else:
unpaid_orders['yearly'][next_yearly_bill_start_on] = bucket + [order]
else:
if previous_bill == None or previous_bill.ending_date <= ending_date:
if previous_bill == None or previous_bill.ending_date < ending_date:
unpaid_orders['monthly_or_less'].append(order)
# Handle working month's billing.
@ -335,25 +350,24 @@ class Bill(models.Model):
@staticmethod
def get_unpaid_for(user):
balance = get_balance_for(user)
balance = get_balance_for_user(user)
unpaid_bills = []
# No unpaid bill if balance is positive.
if balance >= 0:
return []
return unpaid_bills
else:
bills = Bill.objects.filter(
owner=user,
due_date__lt=timezone.now()
).order_by('-creation_date')
# Amount to be paid by the customer.
unpaid_balance = abs(balance)
for bill in bills:
if unpaid_balance < 0:
if unpaid_balance <= 0:
break
unpaid_balance -= bill.amount
unpaid_bills.append(bill)
unpaid_balance -= bill.total
unpaid_bills.append(bill)
return unpaid_bills
@ -464,6 +478,11 @@ class Order(models.Model):
choices = RecurringPeriod.choices,
default = RecurringPeriod.PER_MONTH)
# Trigger initial bill generation at order creation.
def save(self, *args, **kwargs):
super(Order, self).save(*args, **kwargs)
Bill.generate_for(timezone.now().year, timezone.now().month, self.owner)
@property
def records(self):
return OrderRecord.objects.filter(order=self)
@ -531,11 +550,11 @@ class Product(UncloudModel):
on_delete=models.CASCADE,
editable=False)
description = ""
description = "Generic Product"
status = models.CharField(max_length=32,
choices=UncloudStatus.choices,
default=UncloudStatus.PENDING)
default=UncloudStatus.AWAITING_PAYMENT)
order = models.ForeignKey(Order,
on_delete=models.CASCADE,

View file

@ -116,7 +116,7 @@ class VMProductViewSet(ProductViewSet):
order_recurring_period = serializer.validated_data.pop("recurring_period")
# Create base order.
order = Order.objects.create(
order = Order(
recurring_period=order_recurring_period,
owner=request.user,
starting_date=timezone.now()