diff --git a/uncloud_net/models.py b/uncloud_net/models.py index b5a181e..4f80246 100644 --- a/uncloud_net/models.py +++ b/uncloud_net/models.py @@ -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): diff --git a/uncloud_pay/models.py b/uncloud_pay/models.py index c0ffd31..85c280b 100644 --- a/uncloud_pay/models.py +++ b/uncloud_pay/models.py @@ -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 diff --git a/uncloud_service/models.py b/uncloud_service/models.py index 35a479e..a9cea0b 100644 --- a/uncloud_service/models.py +++ b/uncloud_service/models.py @@ -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 diff --git a/uncloud_vm/models.py b/uncloud_vm/models.py index cc07986..440df08 100644 --- a/uncloud_vm/models.py +++ b/uncloud_vm/models.py @@ -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):