Initial product activation implementation
This commit is contained in:
parent
839bd5f8a9
commit
5d5bf486b5
3 changed files with 43 additions and 24 deletions
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
@ -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,24 +350,23 @@ 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_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,
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue