MailboxBillingTest tests passing
This commit is contained in:
parent
5a031b81cb
commit
259bc07b71
|
@ -35,7 +35,7 @@ class BillsBackend(object):
|
|||
# Create bill line
|
||||
billine = bill.lines.create(
|
||||
rate=service.nominal_price,
|
||||
quantity=line.size,
|
||||
quantity=line.metric*line.size,
|
||||
subtotal=line.subtotal,
|
||||
tax=service.tax,
|
||||
description=self.get_line_description(line),
|
||||
|
@ -54,11 +54,14 @@ class BillsBackend(object):
|
|||
service = line.order.service
|
||||
if service.is_fee:
|
||||
return self.format_period(line.ini, line.end)
|
||||
else:
|
||||
description = line.order.description
|
||||
if service.billing_period != service.NEVER:
|
||||
description += " %s" % self.format_period(line.ini, line.end)
|
||||
return description
|
||||
description = line.order.description
|
||||
if service.billing_period != service.NEVER:
|
||||
description += " %s" % self.format_period(line.ini, line.end)
|
||||
if service.metric and service.billing_period != service.NEVER and service.pricing_period == service.NEVER:
|
||||
metric = format(line.metric, '.2f').rstrip('0').rstrip('.')
|
||||
size = format(line.size, '.2f').rstrip('0').rstrip('.')
|
||||
description += " (%s*%s)" % (metric, size)
|
||||
return description
|
||||
|
||||
def create_sublines(self, line, discounts):
|
||||
for discount in discounts:
|
||||
|
|
|
@ -141,14 +141,32 @@ class Order(models.Model):
|
|||
self.save()
|
||||
logger.info("CANCELLED order id: {id}".format(id=self.id))
|
||||
|
||||
def get_metric(self, ini, end):
|
||||
return MetricStorage.get(self, ini, end)
|
||||
def get_metric(self, ini, end, changes=False):
|
||||
if changes:
|
||||
result = []
|
||||
prev = None
|
||||
for metric in self.metrics.filter(created_on__lt=end).order_by('created_on'):
|
||||
created = metric.created_on.date()
|
||||
if created > ini:
|
||||
cini = prev.created_on.date()
|
||||
if not result:
|
||||
cini = ini
|
||||
result.append((cini, created, prev.value))
|
||||
prev = metric
|
||||
if created < end:
|
||||
result.append((created, end, metric.value))
|
||||
return result
|
||||
try:
|
||||
metrics = self.metrics.filter(updated_on__lt=end, updated_on__gte=ini)
|
||||
return metrics.latest('updated_on').value
|
||||
except MetricStorage.DoesNotExist:
|
||||
return decimal.Decimal(0)
|
||||
|
||||
|
||||
class MetricStorage(models.Model):
|
||||
order = models.ForeignKey(Order, verbose_name=_("order"), related_name='metrics')
|
||||
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
|
||||
created_on = models.DateField(_("created"), auto_now_add=True)
|
||||
created_on = models.DateTimeField(_("created"), auto_now_add=True)
|
||||
updated_on = models.DateTimeField(_("updated"))
|
||||
|
||||
class Meta:
|
||||
|
@ -170,25 +188,19 @@ class MetricStorage(models.Model):
|
|||
else:
|
||||
metric.updated_on = now
|
||||
metric.save()
|
||||
|
||||
@classmethod
|
||||
def get(cls, order, ini, end):
|
||||
try:
|
||||
return order.metrics.filter(updated_on__lt=end, updated_on__gte=ini).latest('updated_on').value
|
||||
except cls.DoesNotExist:
|
||||
return decimal.Decimal(0)
|
||||
|
||||
|
||||
_excluded_models = (MetricStorage, LogEntry, Order, ContentType, MigrationRecorder.Migration)
|
||||
|
||||
|
||||
@receiver(post_delete, dispatch_uid="orders.cancel_orders")
|
||||
def cancel_orders(sender, **kwargs):
|
||||
if sender not in _excluded_models:
|
||||
instance = kwargs['instance']
|
||||
if sender in services:
|
||||
if hasattr(instance, 'account'):
|
||||
for order in Order.objects.by_object(instance).active():
|
||||
order.cancel()
|
||||
elif not hasattr(instance, 'account'):
|
||||
else:
|
||||
related = helpers.get_related_objects(instance)
|
||||
if related and related != instance:
|
||||
Order.update_orders(related)
|
||||
|
@ -198,12 +210,13 @@ def cancel_orders(sender, **kwargs):
|
|||
def update_orders(sender, **kwargs):
|
||||
if sender not in _excluded_models:
|
||||
instance = kwargs['instance']
|
||||
if sender in services:
|
||||
if hasattr(instance, 'account'):
|
||||
Order.update_orders(instance)
|
||||
elif not hasattr(instance, 'account'):
|
||||
else:
|
||||
related = helpers.get_related_objects(instance)
|
||||
if related and related != instance:
|
||||
Order.update_orders(related)
|
||||
|
||||
|
||||
|
||||
accounts.register(Order)
|
||||
|
|
|
@ -311,7 +311,9 @@ class TrafficBillingTest(BaseBillingTest):
|
|||
self.assertEqual(0, bills[0].get_total())
|
||||
|
||||
self.report_traffic(account, date, 10**10*9)
|
||||
order.metrics.filter(id=3).update(updated_on=F('updated_on')-delta)
|
||||
metric = order.metrics.latest()
|
||||
metric.updated_on -= delta
|
||||
metric.save()
|
||||
bills = service.orders.bill(proforma=True)
|
||||
self.assertEqual(900, bills[0].get_total())
|
||||
|
||||
|
@ -320,6 +322,11 @@ class TrafficBillingTest(BaseBillingTest):
|
|||
resource = self.create_traffic_resource()
|
||||
account1 = self.create_account()
|
||||
account2 = self.create_account()
|
||||
# TODO
|
||||
|
||||
def test_traffic_prepay(self):
|
||||
pass
|
||||
# TODO
|
||||
|
||||
|
||||
class MailboxBillingTest(BaseBillingTest):
|
||||
|
@ -361,8 +368,7 @@ class MailboxBillingTest(BaseBillingTest):
|
|||
nominal_price=10
|
||||
)
|
||||
plan = Plan.objects.create(is_default=True, name='Default')
|
||||
service.rates.create(plan=plan, quantity=1, price=0)
|
||||
service.rates.create(plan=plan, quantity=2, price=10)
|
||||
service.rates.create(plan=plan, quantity=1, price=10)
|
||||
return service
|
||||
|
||||
def create_disk_resource(self):
|
||||
|
@ -398,18 +404,56 @@ class MailboxBillingTest(BaseBillingTest):
|
|||
self.allocate_disk(mailbox, 10)
|
||||
bill = service.orders.bill()[0]
|
||||
self.assertEqual(0, bill.get_total())
|
||||
bill = disk_service.orders.bill()[0]
|
||||
for line in bill.lines.all():
|
||||
for discount in line.sublines.all():
|
||||
print discount.__dict__
|
||||
self.assertEqual(80, bill.get_total())
|
||||
bp = timezone.now().date() + relativedelta.relativedelta(years=1)
|
||||
bill = disk_service.orders.bill(billing_point=bp, fixed_point=True)[0]
|
||||
self.assertEqual(90, bill.get_total())
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
bill = service.orders.bill()[0]
|
||||
print disk_service.orders.bill()[0].get_total()
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
bill = service.orders.bill(billing_point=bp, fixed_point=True)[0]
|
||||
self.assertEqual(120, bill.get_total())
|
||||
|
||||
def test_mailbox_size_with_changes(self):
|
||||
service = self.create_mailbox_disk_service()
|
||||
self.create_disk_resource()
|
||||
account = self.create_account()
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
now = timezone.now()
|
||||
bp = now.date() + relativedelta.relativedelta(years=1)
|
||||
|
||||
self.allocate_disk(mailbox, 10)
|
||||
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
|
||||
self.assertEqual(9*10, bill.get_total())
|
||||
|
||||
self.allocate_disk(mailbox, 20)
|
||||
created_on = now+relativedelta.relativedelta(months=6)
|
||||
order = service.orders.get()
|
||||
metric = order.metrics.latest('id')
|
||||
metric.created_on = created_on
|
||||
metric.save()
|
||||
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
|
||||
self.assertEqual(9*10*0.5 + 19*10*0.5, bill.get_total())
|
||||
|
||||
self.allocate_disk(mailbox, 30)
|
||||
created_on = now+relativedelta.relativedelta(months=9)
|
||||
order = service.orders.get()
|
||||
metric = order.metrics.latest('id')
|
||||
metric.created_on = created_on
|
||||
metric.save()
|
||||
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
|
||||
self.assertEqual(9*10*0.5 + 19*10*0.25 + 29*10*0.25, bill.get_total())
|
||||
|
||||
self.allocate_disk(mailbox, 10)
|
||||
created_on = now+relativedelta.relativedelta(years=1)
|
||||
order = service.orders.get()
|
||||
metric = order.metrics.latest('id')
|
||||
metric.created_on = created_on
|
||||
metric.save()
|
||||
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
|
||||
self.assertEqual(9*10*0.5 + 19*10*0.25 + 29*10*0.25, bill.get_total())
|
||||
|
||||
|
||||
class JobBillingTest(BaseBillingTest):
|
||||
|
@ -418,10 +462,10 @@ class JobBillingTest(BaseBillingTest):
|
|||
description="Random job",
|
||||
content_type=ContentType.objects.get_for_model(Miscellaneous),
|
||||
match="miscellaneous.is_active and miscellaneous.service.name.lower() == 'job'",
|
||||
billing_period=Service.MONTHLY,
|
||||
billing_point=Service.FIXED_DATE,
|
||||
billing_period=Service.NEVER,
|
||||
billing_point=Service.ON_REGISTER,
|
||||
is_fee=False,
|
||||
metric='mailbox.resources.disk.allocated',
|
||||
metric='miscellaneous.amount',
|
||||
pricing_period=Service.BILLING_PERIOD,
|
||||
rate_algorithm=Service.STEP_PRICE,
|
||||
on_cancel=Service.NOTHING,
|
||||
|
@ -434,15 +478,19 @@ class JobBillingTest(BaseBillingTest):
|
|||
service.rates.create(plan=plan, quantity=11, price=10)
|
||||
return service
|
||||
|
||||
def create_job(self, account=None):
|
||||
def create_job(self, amount, account=None):
|
||||
if not account:
|
||||
account = self.create_account()
|
||||
job_name = '%s.es' % random_ascii(10)
|
||||
job_service, __ = MiscService.objects.get_or_create(name='job', description='Random job')
|
||||
return Miscellaneous.objects.create(service=job_service, description=job_name, account=account)
|
||||
job_service, __ = MiscService.objects.get_or_create(name='job', description='Random job', has_amount=True)
|
||||
return Miscellaneous.objects.create(service=job_service, description=job_name, account=account, amount=amount)
|
||||
|
||||
def test_job(self):
|
||||
pass
|
||||
service = self.create_job_service()
|
||||
account = self.create_account()
|
||||
job = self.create_job(10, account=account)
|
||||
print service.orders.all()
|
||||
print service.orders.bill()[0].get_total()
|
||||
|
||||
|
||||
class PlanBillingTest(BaseBillingTest):
|
||||
|
|
|
@ -150,14 +150,29 @@ class ServiceHandler(plugins.Plugin):
|
|||
'total': price,
|
||||
}))
|
||||
|
||||
def generate_line(self, order, price, size, ini, end, discounts=[]):
|
||||
subtotal = self.nominal_price * size
|
||||
def generate_line(self, order, price, *dates, **kwargs):
|
||||
if len(dates) == 2:
|
||||
ini, end = dates
|
||||
elif len(dates) == 1:
|
||||
ini, end = dates[0], dates[0]
|
||||
else:
|
||||
raise AttributeError
|
||||
metric = kwargs.pop('metric', 1)
|
||||
discounts = kwargs.pop('discounts', ())
|
||||
computed = kwargs.pop('computed', False)
|
||||
if kwargs:
|
||||
raise AttributeError
|
||||
size = self.get_price_size(ini, end)
|
||||
if not computed:
|
||||
price = price * size
|
||||
subtotal = self.nominal_price * size * metric
|
||||
line = AttributeDict(**{
|
||||
'order': order,
|
||||
'subtotal': subtotal,
|
||||
'size': size,
|
||||
'ini': ini,
|
||||
'end': end,
|
||||
'size': size,
|
||||
'metric': metric,
|
||||
'discounts': [],
|
||||
})
|
||||
discounted = 0
|
||||
|
@ -249,7 +264,7 @@ class ServiceHandler(plugins.Plugin):
|
|||
csize += self.get_price_size(intersect.ini, intersect.end)
|
||||
price = self.get_price(account, metric, position=position, rates=rates)
|
||||
price = price * size
|
||||
cprice = price * (size-csize)
|
||||
cprice = price * csize
|
||||
if order in priced:
|
||||
priced[order][0] += price
|
||||
priced[order][1] += cprice
|
||||
|
@ -269,7 +284,7 @@ class ServiceHandler(plugins.Plugin):
|
|||
size = self.get_price_size(order.new_billed_until, new_end)
|
||||
price += price*size
|
||||
order.new_billed_until = new_end
|
||||
line = self.generate_line(order, price, size, ini, end, discounts=discounts)
|
||||
line = self.generate_line(order, price, ini, new_end or end, discounts=discounts, computed=True)
|
||||
lines.append(line)
|
||||
if commit:
|
||||
order.billed_until = order.new_billed_until
|
||||
|
@ -302,8 +317,7 @@ class ServiceHandler(plugins.Plugin):
|
|||
order.new_billed_until = new_end
|
||||
end = new_end
|
||||
size = self.get_price_size(ini, end)
|
||||
price = price * size
|
||||
line = self.generate_line(order, price, size, ini, end, discounts=discounts)
|
||||
line = self.generate_line(order, price, ini, end, discounts=discounts)
|
||||
lines.append(line)
|
||||
if commit:
|
||||
order.billed_until = order.new_billed_until
|
||||
|
@ -322,6 +336,7 @@ class ServiceHandler(plugins.Plugin):
|
|||
bp = None
|
||||
ini = datetime.date.max
|
||||
end = datetime.date.min
|
||||
# TODO compensation with one time billing?
|
||||
for order in orders:
|
||||
cini = order.registered_on
|
||||
if order.billed_until:
|
||||
|
@ -370,8 +385,7 @@ class ServiceHandler(plugins.Plugin):
|
|||
if new_end:
|
||||
order.new_billed_until = new_end
|
||||
end = new_end
|
||||
size = self.get_price_size(ini, end)
|
||||
line = self.generate_line(order, price*size, size, ini, end, discounts=discounts)
|
||||
line = self.generate_line(order, price, ini, end, discounts=discounts)
|
||||
lines.append(line)
|
||||
if commit:
|
||||
order.billed_until = order.new_billed_until
|
||||
|
@ -379,40 +393,36 @@ class ServiceHandler(plugins.Plugin):
|
|||
return lines
|
||||
|
||||
def bill_with_metric(self, orders, account, **options):
|
||||
# TODO filter out orders with cancelled_on < billed_until ?
|
||||
lines = []
|
||||
commit = options.get('commit', True)
|
||||
bp = None
|
||||
for order in orders:
|
||||
if order.billed_until and order.cancelled_on >= order.billed_until:
|
||||
continue
|
||||
bp = self.get_billing_point(order, bp=bp, **options)
|
||||
ini = order.billed_until or order.registered_on
|
||||
if bp <= ini:
|
||||
# TODO except one time service
|
||||
continue
|
||||
order.new_billed_until = bp
|
||||
# weighted metric; bill line per pricing period
|
||||
prev = None
|
||||
lines_info = []
|
||||
if self.billing_period != self.NEVER:
|
||||
if self.get_pricing_period() == self.NEVER:
|
||||
# Changes
|
||||
for ini, end, metric in order.get_metric(ini, end, changes=True)
|
||||
size = self.get_price_size(ini, end)
|
||||
for ini, end, metric in order.get_metric(ini, bp, changes=True):
|
||||
price = self.get_price(order, metric)
|
||||
price = price * size
|
||||
# TODO metric and size in invoice (period and quantity)
|
||||
lines.append(self.generate_line(order, price, metric, ini, end))
|
||||
lines.append(self.generate_line(order, price, ini, end, metric=metric))
|
||||
else:
|
||||
# pricing_slots
|
||||
for ini, end in self.get_pricing_slots(ini, bp):
|
||||
metric = order.get_metric(ini, end)
|
||||
price = self.get_price(order, metric)
|
||||
lines.append(self.generate_line(order, price, metric, ini, end))
|
||||
lines.append(self.generate_line(order, price, ini, end, metric=metric))
|
||||
else:
|
||||
if self.get_pricing_period() == self.NEVER:
|
||||
# get metric
|
||||
metric = order.get_metric(ini, end)
|
||||
price = self.get_price(order, metric)
|
||||
lines.append(self.generate_line(order, price, metric, ini, end))
|
||||
lines.append(self.generate_line(order, price, ini, bp, metric=metric))
|
||||
else:
|
||||
raise NotImplementedError
|
||||
if commit:
|
||||
|
|
Loading…
Reference in a new issue