Add initial generate-bills and charge-negative-balance uncloud-pay

commands
This commit is contained in:
fnux 2020-02-28 08:59:32 +01:00
parent ef5e7e8035
commit 059791e2f2
10 changed files with 275 additions and 12 deletions

View file

@ -0,0 +1,12 @@
from functools import reduce
from .models import Bill, Payment
def sum_amounts(entries):
return reduce(lambda acc, entry: acc + entry.amount, entries, 0)
def get_balance_for(user):
bills = sum_amounts(Bill.objects.filter(owner=user))
payments = sum_amounts(Payment.objects.filter(owner=user))
return payments - bills

View file

@ -0,0 +1,23 @@
from django.core.management.base import BaseCommand
from uncloud_auth.models import User
from uncloud_pay.models import Order, Bill
from uncloud_pay.helpers import get_balance_for
from datetime import timedelta
from django.utils import timezone
class Command(BaseCommand):
help = 'Generate bills and charge customers if necessary.'
def add_arguments(self, parser):
pass
def handle(self, *args, **options):
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 ({}), charging.".format(user.username, balance))
# TODO: charge
print("=> Done.")

View file

@ -0,0 +1,48 @@
from django.core.management.base import BaseCommand
from uncloud_auth.models import User
from uncloud_pay.models import Order, Bill
from datetime import timedelta
from django.utils import timezone
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)
# Pay all non-billed usage untill now.
bill_starting_date = timezone.now()
bill_ending_date = timezone.now()
billed_orders = []
for order in orders:
print(order)
if True: # FIXME
billed_orders.append(order)
# Update starting date if need be.
if order.starting_date < bill_starting_date:
bill_starting_date = order.starting_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()
for order in billed_orders:
print(order)
order.bill.add(bill)
print("Created bill {} for user {}".format(bill.uuid, customer.username))
print("=> Done.")

View file

@ -0,0 +1,42 @@
# Generated by Django 3.0.3 on 2020-02-28 07:37
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0004_auto_20200227_1532'),
]
operations = [
migrations.RemoveField(
model_name='bill',
name='id',
),
migrations.RemoveField(
model_name='bill',
name='paid',
),
migrations.AddField(
model_name='bill',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='bill',
name='creation_date',
field=models.DateTimeField(auto_now_add=True),
),
migrations.AlterField(
model_name='order',
name='creation_date',
field=models.DateTimeField(auto_now_add=True),
),
migrations.AlterField(
model_name='order',
name='starting_date',
field=models.DateTimeField(auto_now_add=True),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-02-28 07:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0005_auto_20200228_0737'),
]
operations = [
migrations.AlterField(
model_name='order',
name='bill',
field=models.ManyToManyField(blank=True, editable=False, to='uncloud_pay.Bill'),
),
]

View file

@ -0,0 +1,17 @@
# Generated by Django 3.0.3 on 2020-02-28 07:44
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0006_auto_20200228_0741'),
]
operations = [
migrations.RemoveField(
model_name='order',
name='bill',
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-02-28 07:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0007_remove_order_bill'),
]
operations = [
migrations.AddField(
model_name='order',
name='bill',
field=models.ManyToManyField(blank=True, editable=False, to='uncloud_pay.Bill'),
),
]

View file

@ -19,21 +19,26 @@ class RecurringPeriod(models.TextChoices):
PER_SECOND = 'SECOND', _('Per Second') PER_SECOND = 'SECOND', _('Per Second')
class Bill(models.Model): class Bill(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
owner = models.ForeignKey(get_user_model(), owner = models.ForeignKey(get_user_model(),
on_delete=models.CASCADE) on_delete=models.CASCADE)
creation_date = models.DateTimeField() creation_date = models.DateTimeField(auto_now_add=True)
starting_date = models.DateTimeField() starting_date = models.DateTimeField()
ending_date = models.DateTimeField() ending_date = models.DateTimeField()
due_date = models.DateField() due_date = models.DateField()
paid = models.BooleanField(default=False)
valid = models.BooleanField(default=True) valid = models.BooleanField(default=True)
@property @property
def amount(self): def amount(self):
# iterate over all related orders orders = Order.objects.filter(bill=self)
return 20 amount = 0
for order in orders:
amount += order.recurring_price
return amount
class Order(models.Model): class Order(models.Model):
@ -49,8 +54,7 @@ class Order(models.Model):
bill = models.ManyToManyField(Bill, bill = models.ManyToManyField(Bill,
editable=False, editable=False,
blank=True, blank=True)
null=True)
recurring_price = models.FloatField(editable=False) recurring_price = models.FloatField(editable=False)

View file

@ -1,6 +1,7 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from rest_framework import serializers from rest_framework import serializers
from .models import * from .models import *
from .helpers import get_balance_for
from functools import reduce from functools import reduce
from uncloud_vm.serializers import VMProductSerializer from uncloud_vm.serializers import VMProductSerializer
@ -10,7 +11,7 @@ class BillSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Bill model = Bill
fields = ['owner', 'amount', 'due_date', 'creation_date', fields = ['owner', 'amount', 'due_date', 'creation_date',
'starting_date', 'ending_date', 'paid'] 'starting_date', 'ending_date']
class PaymentSerializer(serializers.ModelSerializer): class PaymentSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -41,8 +42,4 @@ class UserSerializer(serializers.ModelSerializer):
return reduce(lambda acc, entry: acc + entry.amount, entries, 0) return reduce(lambda acc, entry: acc + entry.amount, entries, 0)
def get_balance(self, user): def get_balance(self, user):
bills = self.__sum_balance(Bill.objects.filter(owner=user)) return get_balance_for(user)
payments = self.__sum_balance(Payment.objects.filter(owner=user))
balance = payments - bills
return balance

View file

@ -0,0 +1,84 @@
from django.shortcuts import render
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from rest_framework import viewsets, permissions
from rest_framework.response import Response
<<<<<<< HEAD
from .models import VMHost, VMProduct, VMSnapshotProduct
from uncloud_pay.models import Order
=======
from uncloud_pay.models import Order, RecurringPeriod
from .models import VMHost, VMProduct
>>>>>>> Quickly wire vm creation to orders
from .serializers import VMHostSerializer, VMProductSerializer, VMSnapshotProductSerializer
import datetime
class VMHostViewSet(viewsets.ModelViewSet):
serializer_class = VMHostSerializer
queryset = VMHost.objects.all()
permission_classes = [permissions.IsAdminUser]
class VMProductViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]
serializer_class = VMProductSerializer
def get_queryset(self):
return VMProduct.objects.filter(owner=self.request.user)
def create(self, request):
# Create base order.
order = Order.objects.create(
recurring_period=RecurringPeriod.PER_MONTH,
recurring_price=0,
one_time_price=0,
owner=request.user
)
# Create VM.
serializer = VMProductSerializer(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
vm = serializer.save(owner=request.user, order=order)
# FIXME: commit everything (VM + order) at once.
order.recurring_price = vm.recurring_price(order.recurring_period)
order.one_time_price = 0
order.save()
return Response(serializer.data)
class VMSnapshotProductViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]
serializer_class = VMSnapshotProductSerializer
def get_queryset(self):
return VMSnapshotProduct.objects.filter(owner=self.request.user)
def create(self, request):
serializer = VMSnapshotProductSerializer(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
# Create order
now = datetime.datetime.now()
order = Order(owner=request.user,
creation_date=now,
starting_date=now,
recurring_price=20,
one_time_price=0,
recurring_period="per_month")
order.save()
# FIXME: calculate the gb_* values
serializer.save(owner=request.user,
order=order,
gb_ssd=12,
gb_hdd=20)
return Response(serializer.data)