def test_sort_billed_until_or_registered_on(self): now = timezone.now().date() order = Order( billed_until=now+datetime.timedelta(days=200)) order1 = Order( registered_on=now+datetime.timedelta(days=5), billed_until=now+datetime.timedelta(days=200)) order2 = Order( registered_on=now+datetime.timedelta(days=6), billed_until=now+datetime.timedelta(days=200)) order3 = Order( registered_on=now+datetime.timedelta(days=6), billed_until=now+datetime.timedelta(days=201)) order4 = Order( registered_on=now+datetime.timedelta(days=6)) order5 = Order( registered_on=now+datetime.timedelta(days=7)) order6 = Order( registered_on=now+datetime.timedelta(days=8)) orders = [order3, order, order1, order2, order4, order5, order6] self.assertEqual(orders, sorted(orders, key=cmp_to_key(helpers.cmp_billed_until_or_registered_on)))
def bill_with_orders(self, orders, account, **options): # For the "boundary conditions" just think that: # date(2011, 1, 1) is equivalent to datetime(2011, 1, 1, 0, 0, 0) # In most cases: # ini >= registered_date, end < registered_date # boundary lookup and exclude cancelled and billed orders_ = [] bp = None ini = datetime.date.max end = datetime.date.min for order in orders: cini = order.registered_on if order.billed_until: # exclude cancelled and billed if self.on_cancel != self.REFUND: if order.cancelled_on and order.billed_until > order.cancelled_on: continue cini = order.billed_until bp = self.get_billing_point(order, bp=bp, **options) if order.billed_until and order.billed_until >= bp: continue order.new_billed_until = bp ini = min(ini, cini) end = max(end, bp) orders_.append(order) orders = orders_ # Compensation related_orders = account.orders.filter(service=self.service) if self.payment_style == self.PREPAY and self.on_cancel == self.COMPENSATE: # Get orders pending for compensation givers = list(related_orders.givers(ini, end)) givers = sorted(givers, key=cmp_to_key(helpers.cmp_billed_until_or_registered_on)) orders = sorted(orders, key=cmp_to_key(helpers.cmp_billed_until_or_registered_on)) self.assign_compensations(givers, orders, **options) rates = self.get_rates(account) has_billing_period = self.billing_period != self.NEVER has_pricing_period = self.get_pricing_period() != self.NEVER if rates and (has_billing_period or has_pricing_period): concurrent = has_billing_period and not has_pricing_period if not concurrent: rdelta = self.get_pricing_rdelta() ini -= rdelta porders = related_orders.pricing_orders(ini, end) porders = list(set(orders).union(set(porders))) porders = sorted(porders, key=cmp_to_key(helpers.cmp_billed_until_or_registered_on)) if concurrent: # Periodic billing with no pricing period lines = self.bill_concurrent_orders(account, porders, rates, ini, end) else: # Periodic and one-time billing with pricing period lines = self.bill_registered_or_renew_events(account, porders, rates) else: # No rates optimization or one-time billing without pricing period lines = [] price = self.nominal_price # Calculate nominal price for order in orders: ini = order.billed_until or order.registered_on end = order.new_billed_until discounts = () dsize, new_end = self.apply_compensations(order) if dsize: discounts=( (self._COMPENSATION, -dsize*price), ) if new_end: order.new_billed_until = new_end end = new_end line = self.generate_line(order, price, ini, end, discounts=discounts) lines.append(line) return lines
def test_compensation(self): now = timezone.now().date() order = Order( description='0', billed_until=now+datetime.timedelta(days=220), cancelled_on=now+datetime.timedelta(days=100)) order1 = Order( description='1', registered_on=now+datetime.timedelta(days=5), cancelled_on=now+datetime.timedelta(days=190), billed_until=now+datetime.timedelta(days=200)) order2 = Order( description='2', registered_on=now+datetime.timedelta(days=6), cancelled_on=now+datetime.timedelta(days=200), billed_until=now+datetime.timedelta(days=200)) order3 = Order( description='3', registered_on=now+datetime.timedelta(days=6), billed_until=now+datetime.timedelta(days=200)) tests = [] order4 = Order( description='4', registered_on=now+datetime.timedelta(days=6), billed_until=now+datetime.timedelta(days=102)) order4.new_billed_until = now+datetime.timedelta(days=200) tests.append([ [now+datetime.timedelta(days=102), now+datetime.timedelta(days=220), order], ]) order5 = Order( description='5', registered_on=now+datetime.timedelta(days=7), billed_until=now+datetime.timedelta(days=102)) order5.new_billed_until = now+datetime.timedelta(days=195) tests.append([ [now+datetime.timedelta(days=190), now+datetime.timedelta(days=200), order1] ]) order6 = Order( description='6', registered_on=now+datetime.timedelta(days=8)) order6.new_billed_until = now+datetime.timedelta(days=200) tests.append([ [now+datetime.timedelta(days=100), now+datetime.timedelta(days=102), order], ]) porders = [order3, order, order1, order2, order4, order5, order6] porders = sorted(porders, key=cmp_to_key(helpers.cmp_billed_until_or_registered_on)) compensations = [] receivers = [] for order in porders: if order.billed_until and order.cancelled_on and order.cancelled_on < order.billed_until: compensations.append(helpers.Interval(order.cancelled_on, order.billed_until, order=order)) elif hasattr(order, 'new_billed_until') and (not order.billed_until or order.billed_until < order.new_billed_until): receivers.append(order) for order, test in zip(receivers, tests): ini = order.billed_until or order.registered_on end = order.cancelled_on or now+datetime.timedelta(days=20000) order_interval = helpers.Interval(ini, end) (compensations, used_compensations) = helpers.compensate(order_interval, compensations) for compensation, test_line in zip(used_compensations, test): self.assertEqual(test_line[0], compensation.ini) self.assertEqual(test_line[1], compensation.end) self.assertEqual(test_line[2], compensation.order)