Include BillRecords in the admin
This commit is contained in:
parent
721472b416
commit
8a17ee6de5
4 changed files with 128 additions and 21 deletions
|
@ -2,11 +2,13 @@ from django.contrib import admin
|
||||||
|
|
||||||
from uncloud_pay.models import Bill, Order, BillRecord, BillingAddress
|
from uncloud_pay.models import Bill, Order, BillRecord, BillingAddress
|
||||||
|
|
||||||
|
class BillRecordInline(admin.TabularInline):
|
||||||
|
model = Bill.bill_records.through
|
||||||
|
|
||||||
class BillAdmin(admin.ModelAdmin):
|
class BillAdmin(admin.ModelAdmin):
|
||||||
pass
|
inlines = [ BillRecordInline ]
|
||||||
|
;
|
||||||
admin.site.register(Bill, BillAdmin)
|
admin.site.register(Bill, BillAdmin)
|
||||||
|
|
||||||
admin.site.register(Order)
|
admin.site.register(Order)
|
||||||
admin.site.register(BillRecord)
|
admin.site.register(BillRecord)
|
||||||
admin.site.register(BillingAddress)
|
admin.site.register(BillingAddress)
|
||||||
|
|
40
uncloud_pay/management/commands/bootstrap-user.py
Normal file
40
uncloud_pay/management/commands/bootstrap-user.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from uncloud_pay.models import *
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Bootstrap user (for testing)'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('username', type=str)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
user = get_user_model().objects.get(username=options['username'])
|
||||||
|
|
||||||
|
addr = BillingAddress.objects.get_or_create(
|
||||||
|
owner=user,
|
||||||
|
active=True,
|
||||||
|
defaults={'organization': 'ungleich',
|
||||||
|
'name': 'Nico Schottelius',
|
||||||
|
'street': 'Hauptstrasse 14',
|
||||||
|
'city': 'Luchsingen',
|
||||||
|
'postal_code': '8775',
|
||||||
|
'country': 'CH' }
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
bills = Bill.objects.filter(owner=user)
|
||||||
|
|
||||||
|
# not even one bill? create!
|
||||||
|
if bills:
|
||||||
|
bill = bills[0]
|
||||||
|
else:
|
||||||
|
bill = Bill.objects.create(owner=user)
|
||||||
|
|
||||||
|
# find any order that is associated to this bill
|
||||||
|
orders = Order.objects.filter(owner=user)
|
||||||
|
)
|
||||||
|
print(f"Addr: {addr}")
|
||||||
|
print(f"Bill: {bill}")
|
33
uncloud_pay/migrations/0002_auto_20200621_1335.py
Normal file
33
uncloud_pay/migrations/0002_auto_20200621_1335.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 3.0.6 on 2020-06-21 13:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import uncloud_pay.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('uncloud_pay', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bill',
|
||||||
|
name='due_date',
|
||||||
|
field=models.DateField(default=uncloud_pay.models.default_payment_delay),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bill',
|
||||||
|
name='ending_date',
|
||||||
|
field=models.DateTimeField(default=uncloud_pay.models.end_of_this_month),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bill',
|
||||||
|
name='starting_date',
|
||||||
|
field=models.DateTimeField(default=uncloud_pay.models.start_of_this_month),
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='bill',
|
||||||
|
constraint=models.UniqueConstraint(fields=('owner', 'starting_date', 'ending_date'), name='one_bill_per_month_per_user'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -10,7 +10,7 @@ import logging
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
import itertools
|
import itertools
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from datetime import timedelta
|
import datetime
|
||||||
from calendar import monthrange
|
from calendar import monthrange
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
@ -23,11 +23,36 @@ from decimal import Decimal
|
||||||
import decimal
|
import decimal
|
||||||
|
|
||||||
# Used to generate bill due dates.
|
# Used to generate bill due dates.
|
||||||
BILL_PAYMENT_DELAY=timedelta(days=10)
|
BILL_PAYMENT_DELAY=datetime.timedelta(days=10)
|
||||||
|
|
||||||
# Initialize logger.
|
# Initialize logger.
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def start_of_month(a_day):
|
||||||
|
""" Returns first of the month of a given datetime object"""
|
||||||
|
return a_day.replace(day=1,hour=0,minute=0,second=0, microsecond=0)
|
||||||
|
|
||||||
|
def end_of_month(a_day):
|
||||||
|
""" Returns first of the month of a given datetime object"""
|
||||||
|
|
||||||
|
_, last_day = monthrange(a_day.year, a_day.month)
|
||||||
|
return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0)
|
||||||
|
|
||||||
|
def start_of_this_month():
|
||||||
|
""" Returns first of this month"""
|
||||||
|
a_day = timezone.now()
|
||||||
|
return a_day.replace(day=1,hour=0,minute=0,second=0, microsecond=0)
|
||||||
|
|
||||||
|
def end_of_this_month():
|
||||||
|
""" Returns first of this month"""
|
||||||
|
a_day = timezone.now()
|
||||||
|
|
||||||
|
_, last_day = monthrange(a_day.year, a_day.month)
|
||||||
|
return a_day.replace(day=last_day,hour=23,minute=59,second=59, microsecond=0)
|
||||||
|
|
||||||
|
def default_payment_delay():
|
||||||
|
return timezone.now() + BILL_PAYMENT_DELAY
|
||||||
|
|
||||||
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
|
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
|
||||||
class RecurringPeriod(models.IntegerChoices):
|
class RecurringPeriod(models.IntegerChoices):
|
||||||
"""
|
"""
|
||||||
|
@ -390,34 +415,41 @@ class Order(models.Model):
|
||||||
description=description)
|
description=description)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{} created at {}, {}->{}, recurring period {}. One time price {}, recurring price {}".format(
|
return f"Order {self.owner}-{self.id}"
|
||||||
self.id, self.creation_date,
|
|
||||||
self.starting_date, self.ending_date,
|
# return "{} created at {}, {}->{}, recurring period {}. One time price {}, recurring price {}".format(
|
||||||
self.recurring_period,
|
# self.id, self.creation_date,
|
||||||
self.one_time_price,
|
# self.starting_date, self.ending_date,
|
||||||
self.recurring_price)
|
# self.recurring_period,
|
||||||
|
# self.one_time_price,
|
||||||
|
# self.recurring_price)
|
||||||
|
|
||||||
|
|
||||||
class Bill(models.Model):
|
class Bill(models.Model):
|
||||||
""" FIXME:
|
|
||||||
Bill needs to be unique in the triple (owner, year, month)
|
|
||||||
"""
|
|
||||||
|
|
||||||
owner = models.ForeignKey(get_user_model(),
|
owner = models.ForeignKey(get_user_model(),
|
||||||
on_delete=models.CASCADE)
|
on_delete=models.CASCADE)
|
||||||
|
|
||||||
creation_date = models.DateTimeField(auto_now_add=True)
|
creation_date = models.DateTimeField(auto_now_add=True)
|
||||||
starting_date = models.DateTimeField()
|
# FIXME: this is a race condition, if ending_date is evaluated
|
||||||
ending_date = models.DateTimeField()
|
# in the next month the bill spawns two months!
|
||||||
due_date = models.DateField()
|
starting_date = models.DateTimeField(default=start_of_this_month)
|
||||||
|
ending_date = models.DateTimeField(default=end_of_this_month)
|
||||||
|
due_date = models.DateField(default=default_payment_delay)
|
||||||
|
|
||||||
valid = models.BooleanField(default=True)
|
valid = models.BooleanField(default=True)
|
||||||
|
|
||||||
# Mapping to BillRecords
|
# Mapping to BillRecords
|
||||||
# https://stackoverflow.com/questions/4443190/djangos-manytomany-relationship-with-additional-fields
|
# https://stackoverflow.com/questions/4443190/djangos-manytomany-relationship-with-additional-fields
|
||||||
|
|
||||||
bill_records = models.ManyToManyField(Order, through="BillRecord")
|
bill_records = models.ManyToManyField(Order, through="BillRecord")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(fields=['owner',
|
||||||
|
'starting_date',
|
||||||
|
'ending_date' ],
|
||||||
|
name='one_bill_per_month_per_user')
|
||||||
|
]
|
||||||
|
|
||||||
# billing address and vat rate is the same for the whole bill
|
# billing address and vat rate is the same for the whole bill
|
||||||
# @property
|
# @property
|
||||||
# def vat_rate(self):
|
# def vat_rate(self):
|
||||||
|
@ -425,7 +457,7 @@ class Bill(models.Model):
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"uc-{self.id}"
|
return f"Bill {self.owner}-{self.id}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_all_bills(cls):
|
def create_all_bills(cls):
|
||||||
|
@ -516,7 +548,7 @@ class BillRecord(models.Model):
|
||||||
ending_date = models.DateTimeField()
|
ending_date = models.DateTimeField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{order.owner}"
|
return f"{self.bill}: {self.usage_count} x {self.order}"
|
||||||
|
|
||||||
|
|
||||||
class OrderRecord(models.Model):
|
class OrderRecord(models.Model):
|
||||||
|
|
Loading…
Reference in a new issue