forked from uncloud/uncloud
Move bill generation logic to Bill class, initial work for prepaid
This commit is contained in:
parent
9aabc66e57
commit
9e8149135b
2 changed files with 56 additions and 31 deletions
|
@ -1,12 +1,12 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
from uncloud_auth.models import User
|
||||
from uncloud_pay.models import Order, Bill, get_balance_for
|
||||
from uncloud_pay.models import Bill
|
||||
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Generate bills and charge customers if necessary.'
|
||||
help = 'Take action on overdue bills.'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
pass
|
||||
|
@ -15,28 +15,9 @@ class Command(BaseCommand):
|
|||
users = User.objects.all()
|
||||
print("Processing {} users.".format(users.count()))
|
||||
for user in users:
|
||||
balance = get_balance_for(user)
|
||||
if balance < 0:
|
||||
print("User {} has negative balance ({}), checking for overdue bills."
|
||||
.format(user.username, balance))
|
||||
|
||||
# Get bills DESCENDING by creation date (= latest at top).
|
||||
bills = Bill.objects.filter(
|
||||
owner=user,
|
||||
due_date__lt=timezone.now()
|
||||
).order_by('-creation_date')
|
||||
overdue_balance = abs(balance)
|
||||
overdue_bills = []
|
||||
for bill in bills:
|
||||
if overdue_balance < 0:
|
||||
break # XXX: I'm (fnux) not fond of breaks!
|
||||
|
||||
overdue_balance -= bill.amount
|
||||
overdue_bills.append(bill)
|
||||
|
||||
for bill in overdue_bills:
|
||||
print("/!\ Overdue bill for {}, {} with amount {}"
|
||||
.format(user.username, bill.uuid, bill.amount))
|
||||
# TODO: take action?
|
||||
for bill in Bill.get_overdue_for(user):
|
||||
print("/!\ Overdue bill for {}, {} with amount {}"
|
||||
.format(user.username, bill.uuid, bill.amount))
|
||||
# TODO: take action?
|
||||
|
||||
print("=> Done.")
|
||||
|
|
|
@ -18,10 +18,14 @@ import uncloud_pay.stripe
|
|||
from uncloud_pay.helpers import beginning_of_month, end_of_month
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
# Define DecimalField properties, used to represent amounts of money.
|
||||
AMOUNT_MAX_DIGITS=10
|
||||
AMOUNT_DECIMALS=2
|
||||
|
||||
# Used to generate bill due dates.
|
||||
BILL_PAYMENT_DELAY=timedelta(days=10)
|
||||
|
||||
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
|
||||
class RecurringPeriod(models.TextChoices):
|
||||
ONE_TIME = 'ONCE', _('Onetime')
|
||||
|
@ -86,6 +90,20 @@ 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)
|
||||
|
||||
# newly_paid_bills = list(
|
||||
# set(unpaid_bills_before_payment) - set(unpaid_bills_after_payment))
|
||||
# for bill in newly_paid_bills:
|
||||
# bill.activate_orders()
|
||||
|
||||
|
||||
class PaymentMethod(models.Model):
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
owner = models.ForeignKey(get_user_model(),
|
||||
|
@ -183,7 +201,7 @@ class Bill(models.Model):
|
|||
return self.ending_date < timezone.now()
|
||||
|
||||
@staticmethod
|
||||
def generate_for(year, month, user, allowed_delay):
|
||||
def generate_for(year, month, user):
|
||||
# /!\ We exclusively work on the specified year and month.
|
||||
|
||||
# Default values for next bill (if any). Only saved at the end of
|
||||
|
@ -192,7 +210,7 @@ class Bill(models.Model):
|
|||
starting_date=beginning_of_month(year, month),
|
||||
ending_date=end_of_month(year, month),
|
||||
creation_date=timezone.now(),
|
||||
due_date=timezone.now() + allowed_delay)
|
||||
due_date=timezone.now() + BILL_PAYMENT_DELAY)
|
||||
|
||||
# Select all orders active on the request period.
|
||||
orders = Order.objects.filter(
|
||||
|
@ -231,6 +249,35 @@ class Bill(models.Model):
|
|||
# Return None if no bill was created.
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_unpaid_for(user):
|
||||
balance = get_balance_for(user)
|
||||
unpaid_bills = []
|
||||
# No unpaid bill if balance is positive.
|
||||
if balance >= 0:
|
||||
return []
|
||||
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:
|
||||
break
|
||||
|
||||
unpaid_balance -= bill.amount
|
||||
unpaid_bills.append(bill)
|
||||
|
||||
return unpaid_bills
|
||||
|
||||
@staticmethod
|
||||
def get_overdue_for(user):
|
||||
unpaid_bills = Bill.get_unpaid_for(user)
|
||||
return list(filter(lambda bill: bill.due_date > timezone.now(), unpaid_bills))
|
||||
|
||||
class BillRecord():
|
||||
"""
|
||||
Entry of a bill, dynamically generated from order records.
|
||||
|
@ -345,9 +392,6 @@ class Order(models.Model):
|
|||
recurring_price=recurring_price,
|
||||
description=description)
|
||||
|
||||
def generate_bill(self):
|
||||
pass
|
||||
|
||||
|
||||
class OrderRecord(models.Model):
|
||||
"""
|
||||
|
@ -398,7 +442,7 @@ class Product(models.Model):
|
|||
|
||||
status = models.CharField(max_length=32,
|
||||
choices=ProductStatus.choices,
|
||||
default=ProductStatus.AWAITING_PAYMENT)
|
||||
default=ProductStatus.PENDING)
|
||||
|
||||
order = models.ForeignKey(Order,
|
||||
on_delete=models.CASCADE,
|
||||
|
|
Loading…
Reference in a new issue