forked from uncloud/uncloud
in the middle of restructering
This commit is contained in:
parent
5d1eaaf0af
commit
a3f3ca8cf9
2 changed files with 103 additions and 386 deletions
|
@ -31,6 +31,11 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
|
||||
class RecurringPeriod(models.IntegerChoices):
|
||||
"""
|
||||
We don't support months are years, because the vary in length.
|
||||
This is not only complicated, but also unfair to the user, as the user pays the same
|
||||
amount for different durations.
|
||||
"""
|
||||
PER_365D = 365*24*3600, _('Per 365 days')
|
||||
PER_30D = 30*24*3600, _('Per 30 days')
|
||||
PER_WEEK = 7*24*3600, _('Per Week')
|
||||
|
@ -40,14 +45,13 @@ class RecurringPeriod(models.IntegerChoices):
|
|||
PER_SECOND = 1, _('Per Second')
|
||||
ONE_TIME = 0, _('Onetime')
|
||||
|
||||
|
||||
class CountryField(models.CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault('choices', COUNTRIES)
|
||||
kwargs.setdefault('default', 'CH')
|
||||
kwargs.setdefault('max_length', 2)
|
||||
|
||||
super(CountryField, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_internal_type(self):
|
||||
return "CharField"
|
||||
|
@ -246,7 +250,23 @@ class VATRate(models.Model):
|
|||
logger.debug("Did not find VAT rate for %s, returning 0" % country_code)
|
||||
return 0
|
||||
|
||||
class BillNico(models.Model):
|
||||
class BillRecord(models.Model):
|
||||
"""
|
||||
Entry of a bill, dynamically generated from an order.
|
||||
"""
|
||||
|
||||
bill = models.ForeignKey(Bill, on_delete=models.CASCADE)
|
||||
|
||||
# How many times the order has been used in this record
|
||||
usage_count = models.IntegerField(default=1)
|
||||
|
||||
# The timeframe the bill record is for can (and probably often will) differ
|
||||
# from the bill time
|
||||
creation_date = models.DateTimeField(auto_now_add=True)
|
||||
starting_date = models.DateTimeField()
|
||||
ending_date = models.DateTimeField()
|
||||
|
||||
class Bill(models.Model):
|
||||
""" FIXME:
|
||||
Bill needs to be unique in the triple (owner, year, month)
|
||||
"""
|
||||
|
@ -262,12 +282,20 @@ class BillNico(models.Model):
|
|||
|
||||
valid = models.BooleanField(default=True)
|
||||
|
||||
@staticmethod
|
||||
def create_all_bills():
|
||||
# billing address and vat rate is the same for the whole bill
|
||||
# @property
|
||||
# def vat_rate(self):
|
||||
# return Decimal(VATRate.get_for_country(self.bill.billing_address.country))
|
||||
|
||||
|
||||
@classmethod
|
||||
def create_all_bills(cls):
|
||||
for owner in get_user_model().objects.all():
|
||||
# mintime = time of first order
|
||||
# maxtime = time of last order
|
||||
# iterate month based through it
|
||||
|
||||
cls.assign_orders_to_bill(owner, year, month)
|
||||
pass
|
||||
|
||||
def assign_orders_to_bill(self, owner, year, month):
|
||||
|
@ -291,359 +319,55 @@ class BillNico(models.Model):
|
|||
|
||||
# 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):
|
||||
# Create the initial bill record
|
||||
# FIXME: maybe limit not even to starting/ending date, but to empty_bill record -- to be fixed in the future
|
||||
# for order in Order.objects.filter(Q(starting_date__gte=self.starting_date),
|
||||
# Q(starting_date__lte=self.ending_date),
|
||||
|
||||
# order.bill.add(this_bill)
|
||||
pass
|
||||
# FIXME below: only check for active orders
|
||||
|
||||
# Ensure all orders of that owner have at least one bill record
|
||||
for order in Order.objects.filter(owner=owner,
|
||||
bill_records=None):
|
||||
|
||||
bill_record = BillRecord.objects.create(bill=self,
|
||||
usage_count=1,
|
||||
starting_date=order.starting_date,
|
||||
ending_date=order.starting_date + timedelta(seconds=order.recurring_period))
|
||||
|
||||
|
||||
"""
|
||||
Find all recurring orders that did not start in this time frame, but need
|
||||
to be billed in this time frame.
|
||||
|
||||
This is:
|
||||
- order starting time before our starting time
|
||||
- order start time + (x * (the_period)) is inside our time frame, x must be integer
|
||||
test cases:
|
||||
+ 365days:
|
||||
time_since_last_billed = self.starting_or_ending_date - order.last_bill_date
|
||||
periods =
|
||||
[ we could in theory add this as a property to the order: next
|
||||
"""
|
||||
# For each recurring order get the usage and bill it
|
||||
for order in Order.objects.filter(~Q(recurring_period=RecurringPeriod.ONE_TIME),
|
||||
Q(starting_date__lt=self.starting_date),
|
||||
owner=owner):
|
||||
|
||||
if order.recurring_period > 0: # avoid div/0 - these are one time payments
|
||||
pass
|
||||
|
||||
# How much time will have passed by the end of the billing cycle
|
||||
td = self.ending_date - order.starting_date
|
||||
|
||||
# How MANY times it will have been used by then
|
||||
used_times = ceil(td / timedelta(seconds=order.recurring_period))
|
||||
|
||||
class Bill(models.Model):
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
owner = models.ForeignKey(get_user_model(),
|
||||
on_delete=models.CASCADE)
|
||||
billed_times = len(order.bills)
|
||||
|
||||
creation_date = models.DateTimeField(auto_now_add=True)
|
||||
starting_date = models.DateTimeField()
|
||||
ending_date = models.DateTimeField()
|
||||
due_date = models.DateField()
|
||||
# How many times it WAS billed -- can also be inferred from the bills that link to it!
|
||||
if used_times > billed_times:
|
||||
billing_times = used_times - billed_times
|
||||
|
||||
valid = models.BooleanField(default=True)
|
||||
# ALSO REGISTER THE TIME PERIOD!
|
||||
pass
|
||||
|
||||
# Trigger product activation if bill paid at creation (from balance).
|
||||
def save(self, *args, **kwargs):
|
||||
super(Bill, self).save(*args, **kwargs)
|
||||
if not self in Bill.get_unpaid_for(self.owner):
|
||||
self.activate_products()
|
||||
|
||||
@property
|
||||
def reference(self):
|
||||
return "{}-{}".format(
|
||||
self.owner.username,
|
||||
self.creation_date.strftime("%Y-%m-%d-%H%M"))
|
||||
|
||||
@property
|
||||
def records(self):
|
||||
bill_records = []
|
||||
orders = Order.objects.filter(bill=self)
|
||||
for order in orders:
|
||||
bill_record = BillRecord(self, order)
|
||||
bill_records.append(bill_record)
|
||||
|
||||
return bill_records
|
||||
|
||||
@property
|
||||
def amount(self):
|
||||
return reduce(lambda acc, record: acc + record.amount, self.records, 0)
|
||||
|
||||
@property
|
||||
def vat_amount(self):
|
||||
return reduce(lambda acc, record: acc + record.vat_amount, self.records, 0)
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self.amount + self.vat_amount
|
||||
|
||||
@property
|
||||
def final(self):
|
||||
# A bill is final when its ending date is passed, or when all of its
|
||||
# orders have been terminated.
|
||||
every_order_terminated = True
|
||||
billing_period_is_over = self.ending_date < timezone.now()
|
||||
for order in self.order_set.all():
|
||||
every_order_terminated = every_order_terminated and order.is_terminated
|
||||
|
||||
return billing_period_is_over or every_order_terminated
|
||||
|
||||
def activate_products(self):
|
||||
for order in self.order_set.all():
|
||||
# FIXME: using __something might not be a good idea.
|
||||
for product_class in Product.__subclasses__():
|
||||
for product in product_class.objects.filter(order=order):
|
||||
if product.status == UncloudStatus.AWAITING_PAYMENT:
|
||||
product.status = UncloudStatus.PENDING
|
||||
product.save()
|
||||
|
||||
@property
|
||||
def billing_address(self):
|
||||
orders = Order.objects.filter(bill=self)
|
||||
# The genrate_for method makes sure all the orders of a bill share the
|
||||
# same billing address. TODO: It would be nice to enforce that somehow...
|
||||
if orders:
|
||||
return orders[0].billing_address
|
||||
else:
|
||||
return None
|
||||
|
||||
# TODO: split this huuuge method!
|
||||
@staticmethod
|
||||
def generate_for(year, month, user):
|
||||
# /!\ We exclusively work on the specified year and month.
|
||||
generated_bills = []
|
||||
|
||||
# Default values for next bill (if any).
|
||||
starting_date=beginning_of_month(year, month)
|
||||
ending_date=end_of_month(year, month)
|
||||
creation_date=timezone.now()
|
||||
|
||||
# Select all orders active on the request period (i.e. starting on or after starting_date).
|
||||
orders = Order.objects.filter(
|
||||
Q(ending_date__gte=starting_date) | Q(ending_date__isnull=True),
|
||||
owner=user)
|
||||
|
||||
# Check if there is already a bill covering the order and period pair:
|
||||
# * Get latest bill by ending_date: previous_bill.ending_date
|
||||
# * For monthly bills: if previous_bill.ending_date is before
|
||||
# (next_bill) ending_date, a new bill has to be generated.
|
||||
# * For yearly bill: if previous_bill.ending_date is on working
|
||||
# month, generate new bill.
|
||||
unpaid_orders = { 'monthly_or_less': [], 'yearly': {} }
|
||||
for order in orders:
|
||||
try:
|
||||
previous_bill = order.bill.latest('ending_date')
|
||||
except ObjectDoesNotExist:
|
||||
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))
|
||||
|
||||
# # 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:
|
||||
# TODO: PREPAID billing is not supported yet.
|
||||
prepaid_due_date = min(creation_date, starting_date) + BILL_PAYMENT_DELAY
|
||||
postpaid_due_date = max(creation_date, ending_date) + BILL_PAYMENT_DELAY
|
||||
|
||||
# There should not be any bill linked to orders with different
|
||||
# billing addresses.
|
||||
per_address_orders = itertools.groupby(
|
||||
unpaid_orders['monthly_or_less'],
|
||||
lambda o: o.billing_address)
|
||||
|
||||
for addr, bill_orders in per_address_orders:
|
||||
next_monthly_bill = Bill.objects.create(owner=user,
|
||||
creation_date=creation_date,
|
||||
starting_date=starting_date, # FIXME: this is a hack!
|
||||
ending_date=ending_date,
|
||||
due_date=postpaid_due_date)
|
||||
|
||||
# It is not possible to register many-to-many relationship before
|
||||
# the two end-objects are saved in database.
|
||||
for order in bill_orders:
|
||||
order.bill.add(next_monthly_bill)
|
||||
|
||||
logger.info("Generated monthly bill {} (amount: {}) for user {}."
|
||||
.format(next_monthly_bill.uuid, next_monthly_bill.total, user))
|
||||
|
||||
# Add to output.
|
||||
generated_bills.append(next_monthly_bill)
|
||||
|
||||
# Handle yearly bills starting on working month.
|
||||
if len(unpaid_orders['yearly']) > 0:
|
||||
# For every starting date, generate new bill.
|
||||
for next_yearly_bill_start_on in unpaid_orders['yearly']:
|
||||
# No postpaid for yearly payments.
|
||||
prepaid_due_date = min(creation_date, next_yearly_bill_start_on) + BILL_PAYMENT_DELAY
|
||||
# Bump by one year, remove one day.
|
||||
ending_date = next_yearly_bill_start_on.replace(
|
||||
year=next_yearly_bill_start_on.year+1) - timedelta(days=1)
|
||||
|
||||
# There should not be any bill linked to orders with different
|
||||
# billing addresses.
|
||||
per_address_orders = itertools.groupby(
|
||||
unpaid_orders['yearly'][next_yearly_bill_start_on],
|
||||
lambda o: o.billing_address)
|
||||
|
||||
for addr, bill_orders in per_address_orders:
|
||||
next_yearly_bill = Bill.objects.create(owner=user,
|
||||
creation_date=creation_date,
|
||||
starting_date=next_yearly_bill_start_on,
|
||||
ending_date=ending_date,
|
||||
due_date=prepaid_due_date)
|
||||
|
||||
# It is not possible to register many-to-many relationship before
|
||||
# the two end-objects are saved in database.
|
||||
for order in bill_orders:
|
||||
order.bill.add(next_yearly_bill)
|
||||
|
||||
logger.info("Generated yearly bill {} (amount: {}) for user {}."
|
||||
.format(next_yearly_bill.uuid, next_yearly_bill.total, user))
|
||||
|
||||
# Add to output.
|
||||
generated_bills.append(next_yearly_bill)
|
||||
|
||||
# Return generated (monthly + yearly) bills.
|
||||
return generated_bills
|
||||
|
||||
@staticmethod
|
||||
def get_unpaid_for(user):
|
||||
balance = get_balance_for_user(user)
|
||||
unpaid_bills = []
|
||||
# No unpaid bill if balance is positive.
|
||||
if balance >= 0:
|
||||
return unpaid_bills
|
||||
else:
|
||||
bills = Bill.objects.filter(
|
||||
owner=user,
|
||||
).order_by('-creation_date')
|
||||
|
||||
# Amount to be paid by the customer.
|
||||
unpaid_balance = abs(balance)
|
||||
for bill in bills:
|
||||
if unpaid_balance <= 0:
|
||||
break
|
||||
|
||||
unpaid_balance -= bill.total
|
||||
unpaid_bills.append(bill)
|
||||
|
||||
return unpaid_bills
|
||||
|
||||
@staticmethod
|
||||
def get_overdue_for(user):
|
||||
unpaid_bills = Bill.get_unpaid_for(user)
|
||||
return list(filter(lambda bill: bill.due_date > timezone.now(), unpaid_bills))
|
||||
|
||||
class BillRecord():
|
||||
"""
|
||||
Entry of a bill, dynamically generated from an order.
|
||||
"""
|
||||
|
||||
def __init__(self, bill, order):
|
||||
self.bill = bill
|
||||
self.order = order
|
||||
self.recurring_price = order.recurring_price
|
||||
self.recurring_period = order.recurring_period
|
||||
self.description = order.description
|
||||
|
||||
if self.order.starting_date >= self.bill.starting_date:
|
||||
self.one_time_price = order.one_time_price
|
||||
else:
|
||||
self.one_time_price = 0
|
||||
|
||||
# Set decimal context for amount computations.
|
||||
# XXX: understand why we need +1 here.
|
||||
decimal.getcontext().prec = AMOUNT_DECIMALS + 1
|
||||
|
||||
@property
|
||||
def recurring_count(self):
|
||||
# Compute billing delta.
|
||||
billed_until = self.bill.ending_date
|
||||
if self.order.ending_date != None and self.order.ending_date <= self.bill.ending_date:
|
||||
billed_until = self.order.ending_date
|
||||
|
||||
billed_from = self.bill.starting_date
|
||||
if self.order.starting_date > self.bill.starting_date:
|
||||
billed_from = self.order.starting_date
|
||||
|
||||
if billed_from > billed_until:
|
||||
# TODO: think about and check edge cases. This should not be
|
||||
# possible.
|
||||
raise Exception('Impossible billing delta!')
|
||||
|
||||
billed_delta = billed_until - billed_from
|
||||
|
||||
# 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))
|
||||
|
||||
# # 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)
|
||||
if self.recurring_period == RecurringPeriod.PER_WEEK:
|
||||
weeks = ceil(billed_delta / timedelta(week=1))
|
||||
return weeks
|
||||
elif self.recurring_period == RecurringPeriod.PER_DAY:
|
||||
days = ceil(billed_delta / timedelta(days=1))
|
||||
return days
|
||||
elif self.recurring_period == RecurringPeriod.PER_HOUR:
|
||||
hours = ceil(billed_delta / timedelta(hours=1))
|
||||
return hours
|
||||
elif self.recurring_period == RecurringPeriod.PER_SECOND:
|
||||
seconds = ceil(billed_delta / timedelta(seconds=1))
|
||||
return seconds
|
||||
elif self.recurring_period == RecurringPeriod.ONE_TIME:
|
||||
return 0
|
||||
else:
|
||||
raise Exception('Unsupported recurring period: {}.'.
|
||||
format(self.order.recurring_period))
|
||||
|
||||
@property
|
||||
def vat_rate(self):
|
||||
return Decimal(VATRate.get_for_country(self.bill.billing_address.country))
|
||||
|
||||
@property
|
||||
def vat_amount(self):
|
||||
return self.amount * self.vat_rate
|
||||
|
||||
@property
|
||||
def amount(self):
|
||||
return Decimal(float(self.recurring_price) * self.recurring_count) + self.one_time_price
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self.amount + self.vat_amount
|
||||
|
||||
###
|
||||
# Orders.
|
||||
|
||||
# Order are assumed IMMUTABLE and used as SOURCE OF TRUST for generating
|
||||
# bills. Do **NOT** mutate then!
|
||||
class Order(models.Model):
|
||||
"""
|
||||
Order are assumed IMMUTABLE and used as SOURCE OF TRUST for generating
|
||||
bills. Do **NOT** mutate then!
|
||||
"""
|
||||
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
owner = models.ForeignKey(get_user_model(),
|
||||
on_delete=models.CASCADE,
|
||||
|
@ -658,11 +382,22 @@ class Order(models.Model):
|
|||
ending_date = models.DateTimeField(blank=True,
|
||||
null=True)
|
||||
|
||||
bill = models.ManyToManyField(Bill,
|
||||
editable=False,
|
||||
blank=True)
|
||||
bill_records = models.ManyToManyField(BillRecord,
|
||||
editable=False,
|
||||
blank=True)
|
||||
|
||||
recurring_period = models.IntegerField(choices = RecurringPeriod.choices, default = RecurringPeriod.PER_30D)
|
||||
@property
|
||||
def count_billed(self):
|
||||
"""
|
||||
How many times this order was billed so far.
|
||||
This logic is mainly thought to be for recurring bills, but also works for one time bills
|
||||
"""
|
||||
|
||||
return sum([ br.usage_count for br in self.bill_records.all() ])
|
||||
|
||||
|
||||
recurring_period = models.IntegerField(choices = RecurringPeriod.choices,
|
||||
default = RecurringPeriod.PER_30D)
|
||||
|
||||
one_time_price = models.DecimalField(default=0.0,
|
||||
max_digits=AMOUNT_MAX_DIGITS,
|
||||
|
@ -696,7 +431,6 @@ class Order(models.Model):
|
|||
@property
|
||||
def is_recurring(self):
|
||||
return not self.recurring_period == RecurringPeriod.ONE_TIME
|
||||
|
||||
@property
|
||||
def is_terminated(self):
|
||||
return self.ending_date != None and self.ending_date < timezone.now()
|
||||
|
@ -709,9 +443,6 @@ class Order(models.Model):
|
|||
self.ending_date = timezone.now()
|
||||
self.save()
|
||||
|
||||
def is_to_be_charged_in(year, month):
|
||||
pass
|
||||
|
||||
# Trigger initial bill generation at order creation.
|
||||
def save(self, *args, **kwargs):
|
||||
if self.ending_date and self.ending_date < self.starting_date:
|
||||
|
@ -749,35 +480,6 @@ class Order(models.Model):
|
|||
self.one_time_price,
|
||||
self.recurring_price)
|
||||
|
||||
class OrderTimothee(models.Model):
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
owner = models.ForeignKey(get_user_model(),
|
||||
on_delete=models.CASCADE,
|
||||
editable=False)
|
||||
billing_address = models.ForeignKey(BillingAddress, on_delete=models.CASCADE)
|
||||
|
||||
# TODO: enforce ending_date - starting_date to be larger than recurring_period.
|
||||
creation_date = models.DateTimeField(auto_now_add=True)
|
||||
starting_date = models.DateTimeField(default=timezone.now)
|
||||
ending_date = models.DateTimeField(blank=True,
|
||||
null=True)
|
||||
|
||||
bill = models.ManyToManyField(Bill,
|
||||
editable=False,
|
||||
blank=True)
|
||||
|
||||
recurring_period = models.IntegerField(choices = RecurringPeriod.choices,
|
||||
default = RecurringPeriod.PER_30D)
|
||||
|
||||
# Trigger initial bill generation at order creation.
|
||||
def save(self, *args, **kwargs):
|
||||
if self.ending_date and self.ending_date < self.starting_date:
|
||||
raise ValidationError("End date cannot be before starting date")
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
Bill.generate_for(self.starting_date.year, self.starting_date.month, self.owner)
|
||||
|
||||
@property
|
||||
def records(self):
|
||||
return OrderRecord.objects.filter(order=self)
|
||||
|
@ -988,3 +690,16 @@ class Product(UncloudModel):
|
|||
else:
|
||||
# FIXME: use the right type of exception here!
|
||||
raise Exception("Did not implement the discounter for this case")
|
||||
|
||||
|
||||
|
||||
# Interesting snippets
|
||||
|
||||
# # Trigger initial bill generation at order creation.
|
||||
# def save(self, *args, **kwargs):
|
||||
# if self.ending_date and self.ending_date < self.starting_date:
|
||||
# raise ValidationError("End date cannot be before starting date")
|
||||
|
||||
# super().save(*args, **kwargs)
|
||||
|
||||
# Bill.generate_for(self.starting_date.year, self.starting_date.month, self.owner)
|
||||
|
|
|
@ -5,7 +5,8 @@ from datetime import datetime, date, timedelta
|
|||
from .models import *
|
||||
from uncloud_service.models import GenericServiceProduct
|
||||
|
||||
class BillingTestCase(TestCase):
|
||||
class NotABillingTC(TestCase):
|
||||
#class BillingTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.user = get_user_model().objects.create(
|
||||
username='jdoe',
|
||||
|
@ -22,15 +23,16 @@ class BillingTestCase(TestCase):
|
|||
description = "Test Product 1"
|
||||
|
||||
# Three months: full, full, partial.
|
||||
starting_date = datetime.fromisoformat('2020-03-01')
|
||||
ending_date = datetime.fromisoformat('2020-05-08')
|
||||
# starting_date = datetime.fromisoformat('2020-03-01')
|
||||
starting_date = datetime(2020,3,1)
|
||||
ending_date = datetime(2020,5,8)
|
||||
|
||||
# Create order to be billed.
|
||||
order = Order.objects.create(
|
||||
owner=self.user,
|
||||
starting_date=starting_date,
|
||||
ending_date=ending_date,
|
||||
recurring_period=RecurringPeriod.PER_MONTH,
|
||||
recurring_period=RecurringPeriod.PER_30D,
|
||||
recurring_price=recurring_price,
|
||||
one_time_price=one_time_price,
|
||||
description=description,
|
||||
|
@ -67,7 +69,7 @@ class BillingTestCase(TestCase):
|
|||
order = Order.objects.create(
|
||||
owner=self.user,
|
||||
starting_date=starting_date,
|
||||
recurring_period=RecurringPeriod.PER_YEAR,
|
||||
recurring_period=RecurringPeriod.PER_365D,
|
||||
recurring_price=recurring_price,
|
||||
one_time_price=one_time_price,
|
||||
description=description,
|
||||
|
@ -150,7 +152,7 @@ class ProductActivationTestCase(TestCase):
|
|||
order = Order.objects.create(
|
||||
owner=self.user,
|
||||
starting_date=starting_date,
|
||||
recurring_period=RecurringPeriod.PER_MONTH,
|
||||
recurring_period=RecurringPeriod.PER_30D,
|
||||
recurring_price=recurring_price,
|
||||
one_time_price=one_time_price,
|
||||
description=description,
|
||||
|
@ -205,12 +207,12 @@ class BillingAddressTestCase(TestCase):
|
|||
order_01 = Order.objects.create(
|
||||
owner=self.user,
|
||||
starting_date=starting_date,
|
||||
recurring_period=RecurringPeriod.PER_MONTH,
|
||||
recurring_period=RecurringPeriod.PER_30D,
|
||||
billing_address=self.billing_address_01)
|
||||
order_02 = Order.objects.create(
|
||||
owner=self.user,
|
||||
starting_date=starting_date,
|
||||
recurring_period=RecurringPeriod.PER_MONTH,
|
||||
recurring_period=RecurringPeriod.PER_30D,
|
||||
billing_address=self.billing_address_01)
|
||||
|
||||
# We need a single bill since we work with a single address.
|
||||
|
@ -225,12 +227,12 @@ class BillingAddressTestCase(TestCase):
|
|||
order_01 = Order.objects.create(
|
||||
owner=self.user,
|
||||
starting_date=starting_date,
|
||||
recurring_period=RecurringPeriod.PER_MONTH,
|
||||
recurring_period=RecurringPeriod.PER_30D,
|
||||
billing_address=self.billing_address_01)
|
||||
order_02 = Order.objects.create(
|
||||
owner=self.user,
|
||||
starting_date=starting_date,
|
||||
recurring_period=RecurringPeriod.PER_MONTH,
|
||||
recurring_period=RecurringPeriod.PER_30D,
|
||||
billing_address=self.billing_address_02)
|
||||
|
||||
# We need different bills since we work with different addresses.
|
||||
|
|
Loading…
Reference in a new issue