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)