Example #1
0
 def setUpClass(cls):
     super().setUpClass()
     with freezegun.freeze_time(str(parse_billing_week('2016-06 1').wed)):
         cls.cp1 = CollectionPoint.objects.create(pk=21, name="First")
         cls.cp2 = CollectionPoint.objects.create(pk=22, name="Second")
         cls.cp3 = CollectionPoint.objects.create(pk=23, name="Third")
         cls.large_veg = BagType.objects.create(
             pk=31,
             name='Large Veg',
             tag_color='Blue',
         )
         cls.small_fruit = BagType.objects.create(
             pk=32,
             name='Small Fruit',
             tag_color='Red',
         )
         cls.customer1 = Customer.objects.create_now(pk=41, full_name='One')
         cls.customer2 = Customer.objects.create_now(pk=42, full_name='Two')
         cls.customer2.bag_quantities = {cls.large_veg: 1, cls.small_fruit: 2}
         cls.customer1.collection_point = cls.cp1
         cls.customer2.collection_point = cls.cp1
     with freezegun.freeze_time(str(parse_billing_week('2016-06 2').wed)):
         cls.customer1.collection_point = cls.cp2
         cls.customer2.collection_point = cls.cp2
     with freezegun.freeze_time(str(parse_billing_week('2016-06 3').wed)):
         cls.customer1.collection_point = cls.cp3
         cls.customer2.collection_point = cls.cp3
Example #2
0
 def setUpClass(cls):
     super().setUpClass()
     with freezegun.freeze_time(str(parse_billing_week('2016-06 1').wed)):
         cls.cp1 = CollectionPoint.objects.create(pk=21, name="First")
         cls.cp2 = CollectionPoint.objects.create(pk=22, name="Second")
         cls.cp3 = CollectionPoint.objects.create(pk=23, name="Third")
         cls.large_veg = BagType.objects.create(
             pk=31,
             name='Large Veg',
             tag_color='Blue',
         )
         cls.small_fruit = BagType.objects.create(
             pk=32,
             name='Small Fruit',
             tag_color='Red',
         )
         cls.customer1 = Customer.objects.create_now(pk=41, full_name='One')
         cls.customer2 = Customer.objects.create_now(pk=42, full_name='Two')
         cls.customer2.bag_quantities = {
             cls.large_veg: 1,
             cls.small_fruit: 2
         }
         cls.customer1.collection_point = cls.cp1
         cls.customer2.collection_point = cls.cp1
     with freezegun.freeze_time(str(parse_billing_week('2016-06 2').wed)):
         cls.customer1.collection_point = cls.cp2
         cls.customer2.collection_point = cls.cp2
     with freezegun.freeze_time(str(parse_billing_week('2016-06 3').wed)):
         cls.customer1.collection_point = cls.cp3
         cls.customer2.collection_point = cls.cp3
Example #3
0
 def pickup_list(self, request, queryset):
     now = timezone.now()
     bw = get_billing_week(now)
     initial = {'billing_week': str(bw)}
     if '_generate' in request.POST:
         form = pickupListForm(request.POST, initial=initial)
         if form.is_valid():
             context = pickup_list(
                 queryset,
                 parse_billing_week(form.cleaned_data['billing_week']))
             context['now_billing_week'] = bw
             # context['now_billing_week'] = context['billing_week']
             context['now'] = now
             return render(request, 'pickup-list.html', context)
     else:
         form = pickupListForm(initial=initial)
     return render(
         request, 'pickup-list-date.html', {
             'form': form,
             'post_vars': request.POST.lists(),
             'opts': CollectionPoint._meta,
             'change': True,
             'is_popup': False,
             'save_as': False,
             'has_delete_permission': False,
             'has_add_permission': False,
             'has_change_permission': False,
         })
Example #4
0
    def pickup_list(self, request, queryset):
        now = timezone.now()
        bw = get_billing_week(now)
        initial = {
            'billing_week': str(bw)
        }
        if '_generate' in request.POST:
            form = pickupListForm(request.POST, initial=initial)
            if form.is_valid():
                context = pickup_list(
                    queryset,
                    parse_billing_week(form.cleaned_data['billing_week'])
                )
                context['now_billing_week'] = bw
                # context['now_billing_week'] = context['billing_week']
                context['now'] = now
                return render(request, 'pickup-list.html', context)
        else:
            form = pickupListForm(initial=initial)
        return render(
            request,
            'pickup-list-date.html',
            {
                'form': form,
                'post_vars': request.POST.lists(),

                'opts': CollectionPoint._meta,
                'change': True,
                'is_popup': False,
                'save_as': False,
                'has_delete_permission': False,
                'has_add_permission': False,
                'has_change_permission': False,
            }
        )
Example #5
0
 def validate(self, value):
     # Use the parent's handling of required fields, etc.
     super().validate(value)
     try:
         return parse_billing_week(value)
     except Exception as e:
         raise ValidationError(
             'Not a valid billing week. Error was: {}'.format(e))
Example #6
0
 def validate(self, value):
     # Use the parent's handling of required fields, etc.
     super().validate(value)
     try:
         return parse_billing_week(value)
     except Exception as e:
         raise ValidationError(
             'Not a valid billing week. Error was: {}'.format(e)
         )
Example #7
0
 def test_distinct_on_subquery(self):
     ccpcs = (CustomerCollectionPointChange.objects.order_by(
         'customer',
         '-changed').filter(changed_in_billing_week__lte=parse_billing_week(
             '2016-06 2')).distinct('customer'))
     qs = CustomerCollectionPointChange.objects.filter(
         id__in=ccpcs, customer__full_name='Two')
     self.assertEqual(
         [(change.customer.full_name, change.collection_point.name)
          for change in qs], [('Two', 'Second')])
Example #8
0
 def test_distinct_on_subquery(self):
     ccpcs = (
         CustomerCollectionPointChange.objects
         .order_by('customer', '-changed')
         .filter(
             changed_in_billing_week__lte=parse_billing_week('2016-06 2')
         )
         .distinct('customer')
     )
     qs = CustomerCollectionPointChange.objects.filter(
         id__in=ccpcs, customer__full_name='Two')
     self.assertEqual(
         [
             (change.customer.full_name, change.collection_point.name)
             for change in qs
         ],
         [('Two', 'Second')]
     )
Example #9
0
def create_line_items(year, month, start_customer=0):
    '''
    We run this immediately after the billing date affecting the first billing
    week of the month.  So, if given 2016, 8 to run a bill in advance for
    August, the code should be run on Sunday 31 July 2016 just after 3pm GMT.

    We will create line items for August, and adjustments due during July.


    XXX What happens if someone leaves - should we automatically set skip weeks
        until the end of the month -> Then Leave status only applies on the month
        following and skip weeks is what causes the refund

    XXX Also need to make sure that collection points don't show up for LEAVEs
    '''
    # e.g. 31st July 2016
    now = timezone.now()

    last_run = LineItemRun.objects.order_by('-started')[:1]
    if last_run:
        assert last_run[0].started < now, 'The last run was in the future'

    line_item_run = LineItemRun.objects.create(
        job_id='xxx',
        year=year,
        month=month,
        started=now,
        finished=None,
        start_customer=start_customer or None,
        currently_processing_customer=None,
    )
    line_item_run.save()
    line_items_by_customer = {}
    now_bw = get_billing_week(now)
    # Find the last first billing week of the previous month
    billing_week = future_billing_week = parse_billing_week(
        '{0}-{1:02d} {2}'.format(year, month, 1))
    assert now_bw >= billing_week, 'Cannot run line items for future weeks'
    billing_weeks_next_month = []
    while future_billing_week.month == month:
        billing_weeks_next_month.append(future_billing_week)
        future_billing_week = future_billing_week.next()

    old_billing_week = start = billing_week.prev()
    billing_weeks_last_month = []
    while old_billing_week.month == start.month:
        billing_weeks_last_month.append(old_billing_week)
        old_billing_week = old_billing_week.prev()
    billing_weeks_last_month.reverse()

    print(billing_weeks_last_month)
    print(billing_weeks_next_month)

    # For each customer
    customers = Customer.objects.order_by('pk').filter(
        created__lt=billing_weeks_next_month[0].start)
    print(customers.all())
    for customer in customers:
        if customer.pk < start_customer:
            continue
        if not line_item_run.start_customer:
            line_item_run.start_customer = customer
        line_item_run.currently_processing_customer = customer
        line_item_run.save()
        print(
            'Processing', customer,
            customer.account_status_before(billing_weeks_next_month[0].start))

        if customer.account_status not in (
                AccountStatusChange.AWAITING_DIRECT_DEBIT):
            # For each billing week in the previous month...
            for past_billing_week in billing_weeks_last_month:
                # Look up what the order actually was
                bag_quantities = CustomerOrderChange.as_of(past_billing_week,
                                                           customer=customer)
                should_have_billed = calculate_weekly_fee(bag_quantities)
                # Look up the line item for that week
                lis = LineItem.objects.filter(
                    customer=customer,
                    bill_against=str(past_billing_week),
                    reason__in=(LineItem.REGULAR, LineItem.NEW_JOINER),
                ).all()
                assert len(lis) <= 1
                if len(lis) == 1:
                    billed = lis[0].amount
                else:
                    billed = 0
                print('Billed:', billed, 'Should have billed:',
                      should_have_billed)
                correction = should_have_billed - billed
                if correction != 0:
                    cli = LineItem(
                        amount=correction,
                        created=now,
                        created_in_billing_week=now_bw,
                        customer=customer,
                        bill_against=str(past_billing_week),
                        reason=LineItem.ORDER_CHANGE_ADJUSTMENT,
                    )
                    cli.save()
                    line_items_by_customer.setdefault(customer, []).append(cli)
                # If there is a skip week, refund the cost of the new order
                skips = Skip.objects.filter(
                    customer=customer, billing_week=past_billing_week).all()
                assert len(skips) <= 1
                if len(skips):
                    print('Skips:', skips, past_billing_week,
                          -should_have_billed)
                    cli = LineItem(
                        amount=-should_have_billed,
                        created=now,
                        created_in_billing_week=now_bw,
                        customer=customer,
                        bill_against=str(past_billing_week),
                        reason=LineItem.SKIP_REFUND,
                    )
                    cli.save()
                    line_items_by_customer.setdefault(customer, []).append(cli)
        # If the customer has left, or not set up don't bill them:
        if customer.account_status_before(
                billing_weeks_next_month[0].start) not in (
                    AccountStatusChange.AWAITING_DIRECT_DEBIT,
                    AccountStatusChange.LEFT):
            # For each billing week in the next month, add a line item to the order
            print('Adding in line items for next months order')
            next_month_bag_quantities = CustomerOrderChange.as_of(
                billing_weeks_next_month[0],
                customer=customer,
            )
            weekly_cost = calculate_weekly_fee(next_month_bag_quantities)
            for future_billing_week in billing_weeks_next_month:
                li = LineItem(
                    amount=weekly_cost,
                    created=now,
                    created_in_billing_week=now_bw,
                    customer=customer,
                    bill_against=str(future_billing_week),
                    reason=LineItem.REGULAR,
                )
                li.save()
                line_items_by_customer.setdefault(customer, []).append(li)
    return line_items_by_customer
Example #10
0
    def setUp(self):
        # Freeze time in UTC
        with freezegun.freeze_time('2015-11-01 00:00', tick=False):
            bag_type1 = BagType.objects.create(name='Large Bag')
            bag_type1.weekly_cost = Decimal('8.00')
            bag_type2 = BagType.objects.create(name='Small Bag')
            bag_type2.weekly_cost = Decimal('5.52')
        # In billing week 3, just before the start of billing week 4 (the last of the month)
        with freezegun.freeze_time('2015-11-22 11:50', tick=False):
            # Customer signs up with one collection left in July
            now3 = timezone.now()
            bw3 = get_billing_week(now3)
            print('Creating Customer One in {}'.format(bw3))
            self.customer1 = Customer.objects.create_now(
                full_name='Customer One')
            self.customer1._set_bag_quantities({
                bag_type1: 1,
                bag_type2: 2
            },
                                               reason='JOIN')
            payment = Payment(
                customer=self.customer1,
                gocardless_mandate_id='xxx',
                amount=Decimal('19.04'),
                reason=Payment.JOIN_WITH_COLLECTIONS_AVAILABLE,
                gocardless_payment_id='xxx',
                created=now3,
                created_in_billing_week=str(bw3),
            )
            payment.save()
            payment_status_change = PaymentStatusChange(
                changed=now3,
                changed_in_billing_week=str(bw3),
                status='xxx',
                payment=payment,
            )
            payment_status_change.save()
            for x in range(1):
                li = LineItem(
                    payment=payment,
                    created=now3,
                    created_in_billing_week=bw3,
                    # The changes are from now:
                    bill_against=str(bw3.next()),
                    customer=self.customer1,
                    amount=Decimal('19.04'),
                    reason=LineItem.NEW_JOINER)
                li.save()
            account_status_change = AccountStatusChange(
                changed=now3,
                changed_in_billing_week=str(bw3),
                customer=self.customer1,
                status=AccountStatusChange.ACTIVE,
            )
            account_status_change.save()
        # Just after the deadline for billing week 4 => No billing dates left this month
        with freezegun.freeze_time('2015-11-22 15:10', tick=False):
            # Customer signs up with no collections left in July
            now4 = timezone.now()
            bw4 = get_billing_week(now4)
            print('Creating Customer Two in {}'.format(bw4))
            assert bw4 == bw3.next()
            self.customer2 = Customer.objects.create_now(
                full_name='Customer Two')
            self.customer2._set_bag_quantities({
                bag_type1: 1,
                bag_type2: 3
            },
                                               reason='JOIN')
            account_status_change = AccountStatusChange(
                changed=now4,
                changed_in_billing_week=str(bw4),
                customer=self.customer2,
                status=AccountStatusChange.ACTIVE,
            )
            account_status_change.save()
            # Customer signs up but never completes GoCardless
            print('Creating Customer Three in {}'.format(bw4))
            self.customer3 = Customer.objects.create_now(
                full_name='Customer Three')
            self.customer3._set_bag_quantities({
                bag_type1: 1,
                bag_type2: 3
            },
                                               reason='JOIN')
            account_status_change = AccountStatusChange(
                changed=now4,
                changed_in_billing_week=str(bw4),
                customer=self.customer3,
                status=AccountStatusChange.AWAITING_DIRECT_DEBIT,
            )
            account_status_change.save()

        # We change Customer 2's order (from 3 to 2 bag types) and set some skip weeks:
        with freezegun.freeze_time(
                '2015-12-09 15:10', tick=False
        ):  # Change in billing week 2, takes effect billing week 3
            now2 = timezone.now()
            bw2 = get_billing_week(now2)
            print('Changing Customer Two in {}'.format(bw2))
            self.customer2._set_bag_quantities({
                bag_type1: 1,
                bag_type2: 2
            },
                                               reason='JOIN')
        skip = Skip(customer=self.customer2, billing_week=bw2)
        skip.save()
        # Skip the same week as the order change above
        skip = Skip(customer=self.customer2,
                    billing_week=parse_billing_week('2015-12 3'))
        skip.save()
Example #11
0
 def test_pickup_list(self):
     customers = OrderedDict([
         (self.customer1, {
             'holiday': False,
             'bags': {
                 self.large_veg: 0,
                 self.small_fruit: 0
             },
             'new': False,
         }),
         (self.customer2, {
             'holiday': False,
             'bags': {
                 self.large_veg: 1,
                 self.small_fruit: 2
             },
             'new': False,
         }),
     ])
     # Test in the week while the data was collected, should get nothing
     result = pickup_list(CollectionPoint.objects.all(),
                          parse_billing_week('2016-06 1'))
     self.check_lookups(result)
     expected = OrderedDict()
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 1'),
     )
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
     # Test in the week after (both in collection point 21)
     result = pickup_list(CollectionPoint.objects.all(),
                          parse_billing_week('2016-06 2'))
     self.check_lookups(result)
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 2'),
     )
     expected = OrderedDict([(self.cp1, {'customers': customers})])
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
     # Test in the last week (should be collection point 22)
     result = pickup_list(
         CollectionPoint.objects.all(),
         parse_billing_week('2016-06 3'),
     )
     self.check_lookups(result)
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 3'),
     )
     expected = OrderedDict([(self.cp2, {'customers': customers})])
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
     # Test in the last week (should be collection point 23)
     result = pickup_list(
         CollectionPoint.objects.all(),
         parse_billing_week('2016-06 4'),
     )
     self.check_lookups(result)
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 4'),
     )
     expected = OrderedDict([(self.cp3, {'customers': customers})])
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
Example #12
0
def get_order_history_events(customer):
    created = None
    changes = []
    for order_change in CustomerOrderChange.objects.filter(
        customer=customer
    ).order_by(
        '-changed'
    ):
        if not created:
            created = order_change.changed
        changes.append(
            {
                'date': order_change.changed,
                'bw': parse_billing_week(
                    order_change.changed_in_billing_week
                ),
                'reason': order_change.reason,
                'type': 'ORDER_CHANGE',
                'new_bag_quantities': render_bag_quantities(
                    order_change.bag_quantities.all()
                ),
            }
        )
    for collection_point_change in CustomerCollectionPointChange.objects.filter(
        customer=customer
    ).order_by(
        '-changed'
    ):
        changes.append(
            {
                'date': collection_point_change.changed,
                'bw': parse_billing_week(
                    order_change.changed_in_billing_week
                ),
                'type': 'COLLECTION_POINT_CHANGE',
                'new_collection_point':
                    collection_point_change.collection_point.name,
            }
        )
    for account_status_change in AccountStatusChange.objects.filter(
        customer=customer
    ).order_by(
        '-changed'
    ):
        changes.append(
            {
                'date': account_status_change.changed,
                'bw': parse_billing_week(
                    order_change.changed_in_billing_week
                ),
                'type': 'ACCOUNT_STATUS_CHANGE',
                'new_account_status':
                    account_status_change.get_status_display(),
            }
        )
    for skip in Skip.objects.filter(
        customer=customer,
        created__lt=timezone.now()
    ).order_by(
        '-created'
    ):
        changes.append(
            {
                'date': skip.created,
                'bw': parse_billing_week(skip.created_in_billing_week),
                'type': 'SKIP_WEEK',
                'billing_week': parse_billing_week(skip.billing_week),
            }
        )
    for payment in Payment.objects.filter(
        customer=customer,
        created__lt=timezone.now()
    ).order_by(
        '-created'
    ):
        changes.append(
            {
                'date': payment.created,
                'type': 'OUT_OF_CYCLE_PAYMENT',
                'amount': payment.amount,
                'status': payment.status,
                'line_items': payment.line_items,
                'description': payment.description,
            }
        )
    for pending_line_item in LineItem.objects.filter(
        customer=customer,
        payment=None,
    ).order_by(
        '-created'
    ):
        changes.append(
            {
                'date': pending_line_item.created,
                'type': 'PENDING_LINE_ITEM',
                'amount': pending_line_item.amount,
                'reason': pending_line_item.reason,
                'description': pending_line_item.description,
            }
        )
    # Get pickup dates since the account was created
    # pd = get_pickup_dates(created, timezone.now())
    res = OrderedDict()
    for change in sorted(changes, key=itemgetter('date'), reverse=True):
        _add_change(res, change)
    return created, res
Example #13
0
def create_line_items(year, month, start_customer=0):
    '''
    We run this immediately after the billing date affecting the first billing
    week of the month.  So, if given 2016, 8 to run a bill in advance for
    August, the code should be run on Sunday 31 July 2016 just after 3pm GMT.

    We will create line items for August, and adjustments due during July.


    XXX What happens if someone leaves - should we automatically set skip weeks
        until the end of the month -> Then Leave status only applies on the month
        following and skip weeks is what causes the refund

    XXX Also need to make sure that collection points don't show up for LEAVEs
    '''
    # e.g. 31st July 2016
    now = timezone.now()

    last_run = LineItemRun.objects.order_by('-started')[:1]
    if last_run:
        assert last_run[0].started < now, 'The last run was in the future'

    line_item_run = LineItemRun.objects.create(
        job_id = 'xxx',
        year = year,
        month = month,
        started = now,
        finished =None,
        start_customer = start_customer or None,
        currently_processing_customer = None,
    )
    line_item_run.save()
    line_items_by_customer = {}
    now_bw = get_billing_week(now)
    # Find the last first billing week of the previous month
    billing_week = future_billing_week = parse_billing_week('{0}-{1:02d} {2}'.format(year, month, 1))
    assert now_bw >= billing_week, 'Cannot run line items for future weeks'
    billing_weeks_next_month = []
    while future_billing_week.month == month:
        billing_weeks_next_month.append(future_billing_week)
        future_billing_week = future_billing_week.next()

    old_billing_week = start = billing_week.prev()
    billing_weeks_last_month = []
    while old_billing_week.month == start.month:
        billing_weeks_last_month.append(old_billing_week)
        old_billing_week = old_billing_week.prev()
    billing_weeks_last_month.reverse()

    print(billing_weeks_last_month)
    print(billing_weeks_next_month)

    # For each customer
    customers = Customer.objects.order_by('pk').filter(created__lt=billing_weeks_next_month[0].start)
    print(customers.all())
    for customer in customers:
        if customer.pk < start_customer:
            continue
        if not line_item_run.start_customer:
            line_item_run.start_customer = customer
        line_item_run.currently_processing_customer = customer
        line_item_run.save()
        print('Processing', customer, customer.account_status_before(billing_weeks_next_month[0].start))


        if customer.account_status not in (AccountStatusChange.AWAITING_DIRECT_DEBIT):
            # For each billing week in the previous month...
            for past_billing_week in billing_weeks_last_month:
                # Look up what the order actually was
                bag_quantities = CustomerOrderChange.as_of(past_billing_week, customer=customer)
                should_have_billed = calculate_weekly_fee(bag_quantities)
                # Look up the line item for that week
                lis = LineItem.objects.filter(
                    customer=customer,
                    bill_against=str(past_billing_week),
                    reason__in=(LineItem.REGULAR, LineItem.NEW_JOINER),
                ).all()
                assert len(lis) <= 1
                if len(lis) == 1:
                    billed = lis[0].amount
                else:
                    billed = 0
                print('Billed:', billed, 'Should have billed:', should_have_billed)
                correction = should_have_billed - billed
                if correction != 0:
                    cli = LineItem(
                        amount=correction,
                        created=now,
                        created_in_billing_week=now_bw,
                        customer=customer,
                        bill_against=str(past_billing_week),
                        reason=LineItem.ORDER_CHANGE_ADJUSTMENT,
                    )
                    cli.save()
                    line_items_by_customer.setdefault(customer, []).append(cli)
                # If there is a skip week, refund the cost of the new order
                skips = Skip.objects.filter(customer=customer, billing_week=past_billing_week).all()
                assert len(skips) <= 1
                if len(skips):
                    print('Skips:', skips, past_billing_week, -should_have_billed)
                    cli = LineItem(
                        amount=-should_have_billed,
                        created=now,
                        created_in_billing_week=now_bw,
                        customer=customer,
                        bill_against=str(past_billing_week),
                        reason=LineItem.SKIP_REFUND,
                    )
                    cli.save()
                    line_items_by_customer.setdefault(customer, []).append(cli)
        # If the customer has left, or not set up don't bill them:
        if customer.account_status_before(billing_weeks_next_month[0].start) not in (AccountStatusChange.AWAITING_DIRECT_DEBIT, AccountStatusChange.LEFT):
            # For each billing week in the next month, add a line item to the order
            print('Adding in line items for next months order')
            next_month_bag_quantities = CustomerOrderChange.as_of(
                billing_weeks_next_month[0],
                customer=customer,
            )
            weekly_cost = calculate_weekly_fee(next_month_bag_quantities)
            for future_billing_week in billing_weeks_next_month:
                li = LineItem(
                    amount=weekly_cost,
                    created=now,
                    created_in_billing_week=now_bw,
                    customer=customer,
                    bill_against=str(future_billing_week),
                    reason=LineItem.REGULAR,
                )
                li.save()
                line_items_by_customer.setdefault(customer, []).append(li)
    return line_items_by_customer
Example #14
0
    def setUp(self):
        # Freeze time in UTC
        with freezegun.freeze_time('2015-11-01 00:00', tick=False):
            bag_type1 = BagType.objects.create(name='Large Bag')
            bag_type1.weekly_cost=Decimal('8.00')
            bag_type2 = BagType.objects.create(name='Small Bag')
            bag_type2.weekly_cost=Decimal('5.52')
        # In billing week 3, just before the start of billing week 4 (the last of the month)
        with freezegun.freeze_time('2015-11-22 11:50', tick=False):
            # Customer signs up with one collection left in July
            now3 = timezone.now()
            bw3 = get_billing_week(now3)
            print('Creating Customer One in {}'.format(bw3))
            self.customer1 = Customer.objects.create_now(full_name='Customer One')
            self.customer1._set_bag_quantities({bag_type1: 1, bag_type2: 2}, reason='JOIN')
            payment = Payment(
                customer=self.customer1,
                gocardless_mandate_id='xxx',
                amount=Decimal('19.04'),
                reason=Payment.JOIN_WITH_COLLECTIONS_AVAILABLE,
                gocardless_payment_id='xxx',
                created=now3,
                created_in_billing_week=str(bw3),
            )
            payment.save()
            payment_status_change = PaymentStatusChange(
                changed=now3,
                changed_in_billing_week=str(bw3),
                status='xxx',
                payment=payment,
            )
            payment_status_change.save()
            for x in range(1):
                li = LineItem(
                    payment=payment,
                    created=now3,
                    created_in_billing_week=bw3,
                    # The changes are from now:
                    bill_against=str(bw3.next()),
                    customer=self.customer1,
                    amount=Decimal('19.04'),
                    reason=LineItem.NEW_JOINER
                )
                li.save()
            account_status_change = AccountStatusChange(
                changed=now3,
                changed_in_billing_week=str(bw3),
                customer=self.customer1,
                status=AccountStatusChange.ACTIVE,
            )
            account_status_change.save()
        # Just after the deadline for billing week 4 => No billing dates left this month
        with freezegun.freeze_time('2015-11-22 15:10', tick=False):
            # Customer signs up with no collections left in July
            now4 = timezone.now()
            bw4 = get_billing_week(now4)
            print('Creating Customer Two in {}'.format(bw4))
            assert bw4 == bw3.next()
            self.customer2 = Customer.objects.create_now(full_name='Customer Two')
            self.customer2._set_bag_quantities({bag_type1: 1, bag_type2: 3}, reason='JOIN')
            account_status_change = AccountStatusChange(
                changed=now4,
                changed_in_billing_week=str(bw4),
                customer=self.customer2,
                status=AccountStatusChange.ACTIVE,
            )
            account_status_change.save()
            # Customer signs up but never completes GoCardless
            print('Creating Customer Three in {}'.format(bw4))
            self.customer3 = Customer.objects.create_now(full_name='Customer Three')
            self.customer3._set_bag_quantities({bag_type1: 1, bag_type2: 3}, reason='JOIN')
            account_status_change = AccountStatusChange(
                changed=now4,
                changed_in_billing_week=str(bw4),
                customer=self.customer3,
                status=AccountStatusChange.AWAITING_DIRECT_DEBIT,
            )
            account_status_change.save()

        # We change Customer 2's order (from 3 to 2 bag types) and set some skip weeks:
        with freezegun.freeze_time('2015-12-09 15:10', tick=False): # Change in billing week 2, takes effect billing week 3
            now2 = timezone.now()
            bw2 = get_billing_week(now2)
            print('Changing Customer Two in {}'.format(bw2))
            self.customer2._set_bag_quantities({bag_type1: 1, bag_type2: 2}, reason='JOIN')
        skip = Skip(customer=self.customer2, billing_week=bw2)
        skip.save()
        # Skip the same week as the order change above
        skip = Skip(customer=self.customer2, billing_week=parse_billing_week('2015-12 3'))
        skip.save()
Example #15
0
 def test_pickup_list(self):
     customers = OrderedDict([
         (
             self.customer1,
             {
                 'holiday': False,
                 'bags': {self.large_veg: 0, self.small_fruit: 0},
                 'new': False,
             }
         ),
         (
             self.customer2,
             {
                 'holiday': False,
                 'bags': {self.large_veg: 1, self.small_fruit: 2},
                 'new': False,
             }
         ),
     ])
     # Test in the week while the data was collected, should get nothing
     result = pickup_list(
         CollectionPoint.objects.all(),
         parse_billing_week('2016-06 1')
     )
     self.check_lookups(result)
     expected = OrderedDict()
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 1'),
     )
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
     # Test in the week after (both in collection point 21)
     result = pickup_list(
         CollectionPoint.objects.all(),
         parse_billing_week('2016-06 2')
     )
     self.check_lookups(result)
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 2'),
     )
     expected = OrderedDict([(self.cp1, {'customers': customers})])
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
     # Test in the last week (should be collection point 22)
     result = pickup_list(
         CollectionPoint.objects.all(),
         parse_billing_week('2016-06 3'),
     )
     self.check_lookups(result)
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 3'),
     )
     expected = OrderedDict([(self.cp2, {'customers': customers})])
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
     # Test in the last week (should be collection point 23)
     result = pickup_list(
         CollectionPoint.objects.all(),
         parse_billing_week('2016-06 4'),
     )
     self.check_lookups(result)
     self.assertEqual(
         result['billing_week'],
         parse_billing_week('2016-06 4'),
     )
     expected = OrderedDict([(self.cp3, {'customers': customers})])
     self.assertEqual(self.strip_result(result['pickup_list']), expected)
Example #16
0
def create_payments(year, month, start_customer=0):
    now = timezone.now()

    # If the account is in credit after taking the line items into account, it will be ignored this month.
    # Upcoming line items can show on the billing history page.
    now_bw = get_billing_week(now)
    # Find the last deadline of the month
    billing_week = parse_billing_week('{0}-{1:02d} {2}'.format(year, month, 1))
    assert now_bw >= billing_week, 'Cannot create payments in the future'

    last_run = PaymentRun.objects.order_by('-started')[:1]
    if last_run:
        assert last_run[0].started < now, 'The last run was in the future'

    payment_run = PaymentRun.objects.create(
        job_id='xxx',
        year=year,
        month=month,
        started=now,
        finished=None,
        start_customer=start_customer or None,
        currently_processing_customer=None,
    )
    payment_run.save()
    payment_by_customer = {}
    # billing_weeks_this_month = []
    # while billing_week.month == month:
    #     billing_weeks_this_month.append(billing_week)
    #     billing_week = billing_week.next()
    # print(billing_weeks_this_month)
    # For each customer
    for customer in Customer.objects.order_by('pk'):
        if customer.pk < start_customer:
            print('Skip')
            continue
        if not payment_run.start_customer:
            payment_run.start_customer = customer
        payment_run.currently_processing_customer = customer
        payment_run.save()
        print(customer)
        # Look at *all* line items that don't have a payment and create the payment object for them
        line_items = LineItem.objects.filter(payment=None, customer=customer)
        total = Decimal(0)
        for line_item in line_items:
            total += line_item.amount
        print(total)
        # If the account is in credit after taking the line items into account, it will be ignored this month.
        if total > 0:
            mandate_id = 'xxx',  #customer.gocardless_current_mandate.gocardless_mandate_id
            payment_response_id = 'xxx'
            payment_response_status = 'xxx'
            payment = Payment(
                customer=customer,
                gocardless_mandate_id=mandate_id,
                amount=total,
                reason=Payment.MONTHLY_INVOICE,
                gocardless_payment_id=payment_response_id,
                created=now,
                created_in_billing_week=str(now_bw),
            )
            payment.save()
            payment_status_change = PaymentStatusChange(
                changed=now,
                changed_in_billing_week=str(now_bw),
                status=payment_response_status,
                payment=payment,
            )
            payment_status_change.save()
            for line_item in line_items:
                line_item.payment = payment
                line_item.save()
            payment_by_customer[customer] = payment
    return payment_by_customer
Example #17
0
def create_payments(year, month, start_customer=0):
    now = timezone.now()

    # If the account is in credit after taking the line items into account, it will be ignored this month.
    # Upcoming line items can show on the billing history page.
    now_bw = get_billing_week(now)
    # Find the last deadline of the month
    billing_week = parse_billing_week('{0}-{1:02d} {2}'.format(year, month, 1))
    assert now_bw >= billing_week, 'Cannot create payments in the future'

    last_run = PaymentRun.objects.order_by('-started')[:1]
    if last_run:
        assert last_run[0].started < now, 'The last run was in the future'

    payment_run = PaymentRun.objects.create(
        job_id = 'xxx',
        year = year,
        month = month,
        started = now,
        finished =None,
        start_customer = start_customer or None,
        currently_processing_customer = None,
    )
    payment_run.save()
    payment_by_customer = {}
    # billing_weeks_this_month = []
    # while billing_week.month == month:
    #     billing_weeks_this_month.append(billing_week)
    #     billing_week = billing_week.next()
    # print(billing_weeks_this_month)
    # For each customer
    for customer in Customer.objects.order_by('pk'):
        if customer.pk < start_customer:
            print('Skip')
            continue
        if not payment_run.start_customer:
            payment_run.start_customer = customer
        payment_run.currently_processing_customer = customer
        payment_run.save()
        print(customer)
        # Look at *all* line items that don't have a payment and create the payment object for them
        line_items = LineItem.objects.filter(payment=None, customer=customer)
        total = Decimal(0)
        for line_item in line_items:
            total += line_item.amount
        print(total)
        # If the account is in credit after taking the line items into account, it will be ignored this month.
        if total > 0:
            mandate_id = 'xxx', #customer.gocardless_current_mandate.gocardless_mandate_id
            payment_response_id = 'xxx'
            payment_response_status = 'xxx'
            payment = Payment(
                customer=customer,
                gocardless_mandate_id=mandate_id,
                amount=total,
                reason=Payment.MONTHLY_INVOICE,
                gocardless_payment_id=payment_response_id,
                created=now,
                created_in_billing_week=str(now_bw),
            )
            payment.save()
            payment_status_change = PaymentStatusChange(
                changed=now,
                changed_in_billing_week=str(now_bw),
                status=payment_response_status,
                payment=payment,
            )
            payment_status_change.save()
            for line_item in line_items:
                line_item.payment = payment
                line_item.save()
            payment_by_customer[customer] = payment
    return payment_by_customer
Example #18
0
def get_order_history_events(customer):
    created = None
    changes = []
    for order_change in CustomerOrderChange.objects.filter(
            customer=customer).order_by('-changed'):
        if not created:
            created = order_change.changed
        changes.append({
            'date':
            order_change.changed,
            'bw':
            parse_billing_week(order_change.changed_in_billing_week),
            'reason':
            order_change.reason,
            'type':
            'ORDER_CHANGE',
            'new_bag_quantities':
            render_bag_quantities(order_change.bag_quantities.all()),
        })
    for collection_point_change in CustomerCollectionPointChange.objects.filter(
            customer=customer).order_by('-changed'):
        changes.append({
            'date':
            collection_point_change.changed,
            'bw':
            parse_billing_week(order_change.changed_in_billing_week),
            'type':
            'COLLECTION_POINT_CHANGE',
            'new_collection_point':
            collection_point_change.collection_point.name,
        })
    for account_status_change in AccountStatusChange.objects.filter(
            customer=customer).order_by('-changed'):
        changes.append({
            'date':
            account_status_change.changed,
            'bw':
            parse_billing_week(order_change.changed_in_billing_week),
            'type':
            'ACCOUNT_STATUS_CHANGE',
            'new_account_status':
            account_status_change.get_status_display(),
        })
    for skip in Skip.objects.filter(
            customer=customer,
            created__lt=timezone.now()).order_by('-created'):
        changes.append({
            'date': skip.created,
            'bw': parse_billing_week(skip.created_in_billing_week),
            'type': 'SKIP_WEEK',
            'billing_week': parse_billing_week(skip.billing_week),
        })
    for payment in Payment.objects.filter(
            customer=customer,
            created__lt=timezone.now()).order_by('-created'):
        changes.append({
            'date': payment.created,
            'type': 'OUT_OF_CYCLE_PAYMENT',
            'amount': payment.amount,
            'status': payment.status,
            'line_items': payment.line_items,
            'description': payment.description,
        })
    for pending_line_item in LineItem.objects.filter(
            customer=customer,
            payment=None,
    ).order_by('-created'):
        changes.append({
            'date': pending_line_item.created,
            'type': 'PENDING_LINE_ITEM',
            'amount': pending_line_item.amount,
            'reason': pending_line_item.reason,
            'description': pending_line_item.description,
        })
    # Get pickup dates since the account was created
    # pd = get_pickup_dates(created, timezone.now())
    res = OrderedDict()
    for change in sorted(changes, key=itemgetter('date'), reverse=True):
        _add_change(res, change)
    return created, res