begin to change to day based differences
Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
This commit is contained in:
parent
18b862c2e1
commit
15535433e8
4 changed files with 67 additions and 70 deletions
|
@ -173,7 +173,7 @@ class VPNNetwork(Product):
|
||||||
|
|
||||||
wireguard_public_key = models.CharField(max_length=48)
|
wireguard_public_key = models.CharField(max_length=48)
|
||||||
|
|
||||||
default_recurring_period = RecurringPeriod.PER_YEAR
|
default_recurring_period = RecurringPeriod.PER_365D
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def recurring_price(self):
|
def recurring_price(self):
|
||||||
|
|
|
@ -30,19 +30,15 @@ BILL_PAYMENT_DELAY=timedelta(days=10)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# 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.TextChoices):
|
class RecurringPeriod(models.IntegerChoices):
|
||||||
PER_YEAR = 'YEAR', _('Per Year') # this is broken - we can make it 365 days - REMOVE ME
|
PER_365D = 365*24*3600, _('Per 365 days')
|
||||||
PER_MONTH = 'MONTH', _('Per Month') # this is broken - varying times - REMOVE ME
|
PER_30D = 30*24*3600, _('Per 30 days')
|
||||||
|
PER_WEEK = 7*24*3600, _('Per Week')
|
||||||
ONE_TIME = 'ONCE', _('Onetime') # this is ok
|
PER_DAY = 24*3600, _('Per Day')
|
||||||
PER_365D = '365D', _('Per 365 days') # this is ok
|
PER_HOUR = 3600, _('Per Hour')
|
||||||
PER_30D = '30D', _('Per 30 days') # this is ok
|
PER_MINUTE = 60, _('Per Minute')
|
||||||
PER_WEEK = 'WEEK', _('Per Week') # this is ok
|
PER_SECOND = 1, _('Per Second')
|
||||||
PER_DAY = 'DAY', _('Per Day') # this is ok
|
ONE_TIME = 0, _('Onetime')
|
||||||
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 CountryField(models.CharField):
|
class CountryField(models.CharField):
|
||||||
|
@ -292,6 +288,9 @@ class BillNico(models.Model):
|
||||||
Can we do this even for recurring / all of them
|
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),
|
for order in Order.objects.filter(Q(starting_date__gte=self.starting_date),
|
||||||
Q(starting_date__lte=self.ending_date),
|
Q(starting_date__lte=self.ending_date),
|
||||||
owner=owner):
|
owner=owner):
|
||||||
|
@ -427,28 +426,28 @@ class Bill(models.Model):
|
||||||
previous_bill = None
|
previous_bill = None
|
||||||
|
|
||||||
# FIXME: control flow is confusing in this block.
|
# FIXME: control flow is confusing in this block.
|
||||||
if order.recurring_period == RecurringPeriod.PER_YEAR:
|
# if order.recurring_period == RecurringPeriod.PER_YEAR:
|
||||||
# We ignore anything smaller than a day in here.
|
# # We ignore anything smaller than a day in here.
|
||||||
next_yearly_bill_start_on = None
|
# next_yearly_bill_start_on = None
|
||||||
if previous_bill == None:
|
# if previous_bill == None:
|
||||||
next_yearly_bill_start_on = order.starting_date
|
# next_yearly_bill_start_on = order.starting_date
|
||||||
elif previous_bill.ending_date <= ending_date:
|
# elif previous_bill.ending_date <= ending_date:
|
||||||
next_yearly_bill_start_on = (previous_bill.ending_date + timedelta(days=1))
|
# 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.
|
# # Store for bill generation. One bucket per day of month with a starting bill.
|
||||||
# bucket is a reference here, no need to reassign.
|
# # bucket is a reference here, no need to reassign.
|
||||||
if next_yearly_bill_start_on:
|
# if next_yearly_bill_start_on:
|
||||||
# We want to group orders by date but keep using datetimes.
|
# # We want to group orders by date but keep using datetimes.
|
||||||
next_yearly_bill_start_on = next_yearly_bill_start_on.replace(
|
# next_yearly_bill_start_on = next_yearly_bill_start_on.replace(
|
||||||
minute=0, hour=0, second=0, microsecond=0)
|
# minute=0, hour=0, second=0, microsecond=0)
|
||||||
bucket = unpaid_orders['yearly'].get(next_yearly_bill_start_on)
|
# bucket = unpaid_orders['yearly'].get(next_yearly_bill_start_on)
|
||||||
if bucket == None:
|
# if bucket == None:
|
||||||
unpaid_orders['yearly'][next_yearly_bill_start_on] = [order]
|
# unpaid_orders['yearly'][next_yearly_bill_start_on] = [order]
|
||||||
else:
|
# else:
|
||||||
unpaid_orders['yearly'][next_yearly_bill_start_on] = bucket + [order]
|
# unpaid_orders['yearly'][next_yearly_bill_start_on] = bucket + [order]
|
||||||
else:
|
# 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)
|
# unpaid_orders['monthly_or_less'].append(order)
|
||||||
|
|
||||||
# Handle working month's billing.
|
# Handle working month's billing.
|
||||||
if len(unpaid_orders['monthly_or_less']) > 0:
|
if len(unpaid_orders['monthly_or_less']) > 0:
|
||||||
|
@ -586,25 +585,25 @@ class BillRecord():
|
||||||
|
|
||||||
# TODO: refactor this thing?
|
# TODO: refactor this thing?
|
||||||
# TODO: weekly
|
# TODO: weekly
|
||||||
if self.recurring_period == RecurringPeriod.PER_YEAR:
|
# if self.recurring_period == RecurringPeriod.PER_YEAR:
|
||||||
# XXX: Should always be one => we do not bill for more than one year.
|
# # XXX: Should always be one => we do not bill for more than one year.
|
||||||
# TODO: check billed_delta is ~365 days.
|
# # TODO: check billed_delta is ~365 days.
|
||||||
return 1
|
# return 1
|
||||||
elif self.recurring_period == RecurringPeriod.PER_MONTH:
|
# elif self.recurring_period == RecurringPeriod.PER_MONTH:
|
||||||
days = ceil(billed_delta / timedelta(days=1))
|
# days = ceil(billed_delta / timedelta(days=1))
|
||||||
|
|
||||||
# Monthly bills always cover one single month.
|
# # Monthly bills always cover one single month.
|
||||||
if (self.bill.starting_date.year != self.bill.starting_date.year or
|
# if (self.bill.starting_date.year != self.bill.starting_date.year or
|
||||||
self.bill.starting_date.month != self.bill.ending_date.month):
|
# self.bill.starting_date.month != self.bill.ending_date.month):
|
||||||
raise Exception('Bill {} covers more than one month. Cannot bill PER_MONTH.'.
|
# raise Exception('Bill {} covers more than one month. Cannot bill PER_MONTH.'.
|
||||||
format(self.bill.uuid))
|
# format(self.bill.uuid))
|
||||||
|
|
||||||
# XXX: minumal length of monthly order is to be enforced somewhere else.
|
# # XXX: minumal length of monthly order is to be enforced somewhere else.
|
||||||
(_, days_in_month) = monthrange(
|
# (_, days_in_month) = monthrange(
|
||||||
self.bill.starting_date.year,
|
# self.bill.starting_date.year,
|
||||||
self.bill.starting_date.month)
|
# self.bill.starting_date.month)
|
||||||
return round(days / days_in_month, AMOUNT_DECIMALS)
|
# return round(days / days_in_month, AMOUNT_DECIMALS)
|
||||||
elif self.recurring_period == RecurringPeriod.PER_WEEK:
|
if self.recurring_period == RecurringPeriod.PER_WEEK:
|
||||||
weeks = ceil(billed_delta / timedelta(week=1))
|
weeks = ceil(billed_delta / timedelta(week=1))
|
||||||
return weeks
|
return weeks
|
||||||
elif self.recurring_period == RecurringPeriod.PER_DAY:
|
elif self.recurring_period == RecurringPeriod.PER_DAY:
|
||||||
|
@ -663,8 +662,7 @@ class Order(models.Model):
|
||||||
blank=True)
|
blank=True)
|
||||||
|
|
||||||
recurring_period = models.CharField(max_length=32,
|
recurring_period = models.CharField(max_length=32,
|
||||||
choices = RecurringPeriod.choices,
|
choices = RecurringPeriod.choices, default = RecurringPeriod.PER_30D)
|
||||||
default = RecurringPeriod.PER_MONTH)
|
|
||||||
|
|
||||||
one_time_price = models.DecimalField(default=0.0,
|
one_time_price = models.DecimalField(default=0.0,
|
||||||
max_digits=AMOUNT_MAX_DIGITS,
|
max_digits=AMOUNT_MAX_DIGITS,
|
||||||
|
@ -712,8 +710,7 @@ class Order(models.Model):
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def is_to_be_charged_in(year, month):
|
def is_to_be_charged_in(year, month):
|
||||||
if self.recurring_period == RecurringPeriod.PER_YEAR:
|
pass
|
||||||
pass
|
|
||||||
|
|
||||||
# Trigger initial bill generation at order creation.
|
# Trigger initial bill generation at order creation.
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -771,7 +768,7 @@ class OrderTimothee(models.Model):
|
||||||
|
|
||||||
recurring_period = models.CharField(max_length=32,
|
recurring_period = models.CharField(max_length=32,
|
||||||
choices = RecurringPeriod.choices,
|
choices = RecurringPeriod.choices,
|
||||||
default = RecurringPeriod.PER_MONTH)
|
default = RecurringPeriod.PER_30D)
|
||||||
|
|
||||||
# Trigger initial bill generation at order creation.
|
# Trigger initial bill generation at order creation.
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -873,7 +870,7 @@ class Product(UncloudModel):
|
||||||
null=True)
|
null=True)
|
||||||
|
|
||||||
# Default period for all products
|
# Default period for all products
|
||||||
default_recurring_period = RecurringPeriod.PER_MONTH
|
default_recurring_period = RecurringPeriod.PER_30D
|
||||||
|
|
||||||
# Used to save records.
|
# Used to save records.
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -966,26 +963,26 @@ class Product(UncloudModel):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
if self.default_recurring_period == RecurringPeriod.PER_YEAR:
|
if self.default_recurring_period == RecurringPeriod.PER_365D:
|
||||||
if requested_period == RecurringPeriod.PER_YEAR:
|
if requested_period == RecurringPeriod.PER_365D:
|
||||||
return self.recurring_price
|
return self.recurring_price
|
||||||
if requested_period == RecurringPeriod.PER_MONTH:
|
if requested_period == RecurringPeriod.PER_30D:
|
||||||
return self.recurring_price/11.
|
return self.recurring_price/11.
|
||||||
if requested_period == RecurringPeriod.PER_DAY:
|
if requested_period == RecurringPeriod.PER_DAY:
|
||||||
return self.recurring_price/11./28.
|
return self.recurring_price/11./28.
|
||||||
|
|
||||||
elif self.default_recurring_period == RecurringPeriod.PER_MONTH:
|
elif self.default_recurring_period == RecurringPeriod.PER_30D:
|
||||||
if requested_period == RecurringPeriod.PER_YEAR:
|
if requested_period == RecurringPeriod.PER_365D:
|
||||||
return self.recurring_price*11
|
return self.recurring_price*11
|
||||||
if requested_period == RecurringPeriod.PER_MONTH:
|
if requested_period == RecurringPeriod.PER_30D:
|
||||||
return self.recurring_price
|
return self.recurring_price
|
||||||
if requested_period == RecurringPeriod.PER_DAY:
|
if requested_period == RecurringPeriod.PER_DAY:
|
||||||
return self.recurring_price/28.
|
return self.recurring_price/28.
|
||||||
|
|
||||||
elif self.default_recurring_period == RecurringPeriod.PER_DAY:
|
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
|
return self.recurring_price*11*28
|
||||||
if requested_period == RecurringPeriod.PER_MONTH:
|
if requested_period == RecurringPeriod.PER_30D:
|
||||||
return self.recurring_price*28
|
return self.recurring_price*28
|
||||||
if requested_period == RecurringPeriod.PER_DAY:
|
if requested_period == RecurringPeriod.PER_DAY:
|
||||||
return self.recurring_price
|
return self.recurring_price
|
||||||
|
|
|
@ -17,7 +17,7 @@ class MatrixServiceProduct(Product):
|
||||||
domain = models.CharField(max_length=255, default='domain.tld')
|
domain = models.CharField(max_length=255, default='domain.tld')
|
||||||
|
|
||||||
# Default recurring price is PER_MONT, see Product class.
|
# 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
|
return self.monthly_managment_fee
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -28,7 +28,7 @@ class MatrixServiceProduct(Product):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def allowed_recurring_periods():
|
def allowed_recurring_periods():
|
||||||
return list(filter(
|
return list(filter(
|
||||||
lambda pair: pair[0] in [RecurringPeriod.PER_MONTH],
|
lambda pair: pair[0] in [RecurringPeriod.PER_30D],
|
||||||
RecurringPeriod.choices))
|
RecurringPeriod.choices))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -88,8 +88,8 @@ class VMProduct(Product):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def allowed_recurring_periods():
|
def allowed_recurring_periods():
|
||||||
return list(filter(
|
return list(filter(
|
||||||
lambda pair: pair[0] in [RecurringPeriod.PER_YEAR,
|
lambda pair: pair[0] in [RecurringPeriod.PER_365D,
|
||||||
RecurringPeriod.PER_MONTH, RecurringPeriod.PER_HOUR],
|
RecurringPeriod.PER_30D, RecurringPeriod.PER_HOUR],
|
||||||
RecurringPeriod.choices))
|
RecurringPeriod.choices))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
Loading…
Reference in a new issue