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
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
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, })
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, } )
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))
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) )
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')])
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')] )
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
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()
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)
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
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
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()
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)
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
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
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