begin to change to day based differences

Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
This commit is contained in:
Nico Schottelius 2020-05-23 23:32:45 +02:00
parent 18b862c2e1
commit 15535433e8
4 changed files with 67 additions and 70 deletions

View file

@ -173,7 +173,7 @@ class VPNNetwork(Product):
wireguard_public_key = models.CharField(max_length=48)
default_recurring_period = RecurringPeriod.PER_YEAR
default_recurring_period = RecurringPeriod.PER_365D
@property
def recurring_price(self):

View file

@ -30,19 +30,15 @@ BILL_PAYMENT_DELAY=timedelta(days=10)
logger = logging.getLogger(__name__)
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
class RecurringPeriod(models.TextChoices):
PER_YEAR = 'YEAR', _('Per Year') # this is broken - we can make it 365 days - REMOVE ME
PER_MONTH = 'MONTH', _('Per Month') # this is broken - varying times - REMOVE ME
ONE_TIME = 'ONCE', _('Onetime') # this is ok
PER_365D = '365D', _('Per 365 days') # this is ok
PER_30D = '30D', _('Per 30 days') # this is ok
PER_WEEK = 'WEEK', _('Per Week') # this is ok
PER_DAY = 'DAY', _('Per Day') # this is ok
PER_HOUR = 'HOUR', _('Per Hour') # this is ok
PER_MINUTE = 'MINUTE', _('Per Minute') # this is ok
PER_SECOND = 'SECOND', _('Per Second') # this is ok
class RecurringPeriod(models.IntegerChoices):
PER_365D = 365*24*3600, _('Per 365 days')
PER_30D = 30*24*3600, _('Per 30 days')
PER_WEEK = 7*24*3600, _('Per Week')
PER_DAY = 24*3600, _('Per Day')
PER_HOUR = 3600, _('Per Hour')
PER_MINUTE = 60, _('Per Minute')
PER_SECOND = 1, _('Per Second')
ONE_TIME = 0, _('Onetime')
class CountryField(models.CharField):
@ -292,6 +288,9 @@ class BillNico(models.Model):
Can we do this even for recurring / all of them
"""
# FIXME: add something to check whether the order should be billed at all - i.e. a marker that
# disables searching -> optimization for later
for order in Order.objects.filter(Q(starting_date__gte=self.starting_date),
Q(starting_date__lte=self.ending_date),
owner=owner):
@ -427,28 +426,28 @@ class Bill(models.Model):
previous_bill = None
# FIXME: control flow is confusing in this block.
if order.recurring_period == RecurringPeriod.PER_YEAR:
# We ignore anything smaller than a day in here.
next_yearly_bill_start_on = None
if previous_bill == None:
next_yearly_bill_start_on = order.starting_date
elif previous_bill.ending_date <= ending_date:
next_yearly_bill_start_on = (previous_bill.ending_date + timedelta(days=1))
# if order.recurring_period == RecurringPeriod.PER_YEAR:
# # We ignore anything smaller than a day in here.
# next_yearly_bill_start_on = None
# if previous_bill == None:
# next_yearly_bill_start_on = order.starting_date
# elif previous_bill.ending_date <= ending_date:
# next_yearly_bill_start_on = (previous_bill.ending_date + timedelta(days=1))
# Store for bill generation. One bucket per day of month with a starting bill.
# bucket is a reference here, no need to reassign.
if next_yearly_bill_start_on:
# We want to group orders by date but keep using datetimes.
next_yearly_bill_start_on = next_yearly_bill_start_on.replace(
minute=0, hour=0, second=0, microsecond=0)
bucket = unpaid_orders['yearly'].get(next_yearly_bill_start_on)
if bucket == None:
unpaid_orders['yearly'][next_yearly_bill_start_on] = [order]
else:
unpaid_orders['yearly'][next_yearly_bill_start_on] = bucket + [order]
else:
if previous_bill == None or previous_bill.ending_date < ending_date:
unpaid_orders['monthly_or_less'].append(order)
# # Store for bill generation. One bucket per day of month with a starting bill.
# # bucket is a reference here, no need to reassign.
# if next_yearly_bill_start_on:
# # We want to group orders by date but keep using datetimes.
# next_yearly_bill_start_on = next_yearly_bill_start_on.replace(
# minute=0, hour=0, second=0, microsecond=0)
# bucket = unpaid_orders['yearly'].get(next_yearly_bill_start_on)
# if bucket == None:
# unpaid_orders['yearly'][next_yearly_bill_start_on] = [order]
# else:
# unpaid_orders['yearly'][next_yearly_bill_start_on] = bucket + [order]
# else:
# if previous_bill == None or previous_bill.ending_date < ending_date:
# unpaid_orders['monthly_or_less'].append(order)
# Handle working month's billing.
if len(unpaid_orders['monthly_or_less']) > 0:
@ -586,25 +585,25 @@ class BillRecord():
# TODO: refactor this thing?
# TODO: weekly
if self.recurring_period == RecurringPeriod.PER_YEAR:
# XXX: Should always be one => we do not bill for more than one year.
# TODO: check billed_delta is ~365 days.
return 1
elif self.recurring_period == RecurringPeriod.PER_MONTH:
days = ceil(billed_delta / timedelta(days=1))
# if self.recurring_period == RecurringPeriod.PER_YEAR:
# # XXX: Should always be one => we do not bill for more than one year.
# # TODO: check billed_delta is ~365 days.
# return 1
# elif self.recurring_period == RecurringPeriod.PER_MONTH:
# days = ceil(billed_delta / timedelta(days=1))
# Monthly bills always cover one single month.
if (self.bill.starting_date.year != self.bill.starting_date.year or
self.bill.starting_date.month != self.bill.ending_date.month):
raise Exception('Bill {} covers more than one month. Cannot bill PER_MONTH.'.
format(self.bill.uuid))
# # Monthly bills always cover one single month.
# if (self.bill.starting_date.year != self.bill.starting_date.year or
# self.bill.starting_date.month != self.bill.ending_date.month):
# raise Exception('Bill {} covers more than one month. Cannot bill PER_MONTH.'.
# format(self.bill.uuid))
# XXX: minumal length of monthly order is to be enforced somewhere else.
(_, days_in_month) = monthrange(
self.bill.starting_date.year,
self.bill.starting_date.month)
return round(days / days_in_month, AMOUNT_DECIMALS)
elif self.recurring_period == RecurringPeriod.PER_WEEK:
# # XXX: minumal length of monthly order is to be enforced somewhere else.
# (_, days_in_month) = monthrange(
# self.bill.starting_date.year,
# self.bill.starting_date.month)
# return round(days / days_in_month, AMOUNT_DECIMALS)
if self.recurring_period == RecurringPeriod.PER_WEEK:
weeks = ceil(billed_delta / timedelta(week=1))
return weeks
elif self.recurring_period == RecurringPeriod.PER_DAY:
@ -663,8 +662,7 @@ class Order(models.Model):
blank=True)
recurring_period = models.CharField(max_length=32,
choices = RecurringPeriod.choices,
default = RecurringPeriod.PER_MONTH)
choices = RecurringPeriod.choices, default = RecurringPeriod.PER_30D)
one_time_price = models.DecimalField(default=0.0,
max_digits=AMOUNT_MAX_DIGITS,
@ -712,8 +710,7 @@ class Order(models.Model):
self.save()
def is_to_be_charged_in(year, month):
if self.recurring_period == RecurringPeriod.PER_YEAR:
pass
pass
# Trigger initial bill generation at order creation.
def save(self, *args, **kwargs):
@ -771,7 +768,7 @@ class OrderTimothee(models.Model):
recurring_period = models.CharField(max_length=32,
choices = RecurringPeriod.choices,
default = RecurringPeriod.PER_MONTH)
default = RecurringPeriod.PER_30D)
# Trigger initial bill generation at order creation.
def save(self, *args, **kwargs):
@ -873,7 +870,7 @@ class Product(UncloudModel):
null=True)
# Default period for all products
default_recurring_period = RecurringPeriod.PER_MONTH
default_recurring_period = RecurringPeriod.PER_30D
# Used to save records.
def save(self, *args, **kwargs):
@ -966,26 +963,26 @@ class Product(UncloudModel):
"""
if self.default_recurring_period == RecurringPeriod.PER_YEAR:
if requested_period == RecurringPeriod.PER_YEAR:
if self.default_recurring_period == RecurringPeriod.PER_365D:
if requested_period == RecurringPeriod.PER_365D:
return self.recurring_price
if requested_period == RecurringPeriod.PER_MONTH:
if requested_period == RecurringPeriod.PER_30D:
return self.recurring_price/11.
if requested_period == RecurringPeriod.PER_DAY:
return self.recurring_price/11./28.
elif self.default_recurring_period == RecurringPeriod.PER_MONTH:
if requested_period == RecurringPeriod.PER_YEAR:
elif self.default_recurring_period == RecurringPeriod.PER_30D:
if requested_period == RecurringPeriod.PER_365D:
return self.recurring_price*11
if requested_period == RecurringPeriod.PER_MONTH:
if requested_period == RecurringPeriod.PER_30D:
return self.recurring_price
if requested_period == RecurringPeriod.PER_DAY:
return self.recurring_price/28.
elif self.default_recurring_period == RecurringPeriod.PER_DAY:
if requested_period == RecurringPeriod.PER_YEAR:
if requested_period == RecurringPeriod.PER_365D:
return self.recurring_price*11*28
if requested_period == RecurringPeriod.PER_MONTH:
if requested_period == RecurringPeriod.PER_30D:
return self.recurring_price*28
if requested_period == RecurringPeriod.PER_DAY:
return self.recurring_price

View file

@ -17,7 +17,7 @@ class MatrixServiceProduct(Product):
domain = models.CharField(max_length=255, default='domain.tld')
# Default recurring price is PER_MONT, see Product class.
def recurring_price(self, recurring_period=RecurringPeriod.PER_MONTH):
def recurring_price(self, recurring_period=RecurringPeriod.PER_30D):
return self.monthly_managment_fee
@staticmethod
@ -28,7 +28,7 @@ class MatrixServiceProduct(Product):
@staticmethod
def allowed_recurring_periods():
return list(filter(
lambda pair: pair[0] in [RecurringPeriod.PER_MONTH],
lambda pair: pair[0] in [RecurringPeriod.PER_30D],
RecurringPeriod.choices))
@property

View file

@ -88,8 +88,8 @@ class VMProduct(Product):
@staticmethod
def allowed_recurring_periods():
return list(filter(
lambda pair: pair[0] in [RecurringPeriod.PER_YEAR,
RecurringPeriod.PER_MONTH, RecurringPeriod.PER_HOUR],
lambda pair: pair[0] in [RecurringPeriod.PER_365D,
RecurringPeriod.PER_30D, RecurringPeriod.PER_HOUR],
RecurringPeriod.choices))
def __str__(self):