예제 #1
0
def get_pickup_dates(start, stop, month_start=False):
    if isinstance(start, datetime.date):
        start = pytz.utc.localize(
            datetime.datetime(start.year, start.month, start.day, 12)
        )
    bw = get_billing_week(start)
    if month_start:
        while bw.week != 1:
            next_start = bw.start - timedelta(days=6, hours=1)
            bw = get_billing_week(next_start)
    counter = 1
    result = OrderedDict()
    result[datetime.date(bw.year, bw.month, 1)] = [bw]
    while counter < 1000:
        if isinstance(stop, int):
            if counter == stop:
                return result
        else:
            if bw.start > stop:
                return result
        bw = get_billing_week(bw.end + timedelta(hours=1))
        d = datetime.date(bw.year, bw.month, 1)
        if d not in result:
            result[d] = []
        result[d].append(bw)
        counter += 1
    raise Exception(
        'Found more than 1000 or so pickup dates, perhaps there is a problem?'
    )
예제 #2
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,
            }
        )
예제 #3
0
 def _set_bag_quantities(self, bag_quantities, reason='CHANGE'):
     now = timezone.now()
     bw = get_billing_week(now)
     customer_order_change = CustomerOrderChange(
         changed=now,
         changed_in_billing_week=str(bw),
         customer=self,
         reason=reason,
     )
     customer_order_change.save()
     for bag_type, quantity in bag_quantities.items():
         if not isinstance(bag_type, BagType):
             bag_quantity = CustomerOrderChangeBagQuantity(
                 customer_order_change=customer_order_change,
                 bag_type_id=int(bag_type),
                 quantity=quantity,
             )
         else:
             bag_quantity = CustomerOrderChangeBagQuantity(
                 customer_order_change=customer_order_change,
                 bag_type=bag_type,
                 quantity=quantity,
             )
         # XXX Do we need the save?
         bag_quantity.save()
예제 #4
0
 def save_model(self, request, line_item, form, change):
     now = timezone.now()
     bw = get_billing_week(now)
     line_item.reason = LineItem.MANUAL
     line_item.created = now
     line_item.created_in_billing_week = str(bw)
     line_item.save()
예제 #5
0
 def _set_bag_quantities(self, bag_quantities, reason='CHANGE'):
     now = timezone.now()
     bw = get_billing_week(now)
     customer_order_change = CustomerOrderChange(
         changed=now,
         changed_in_billing_week=str(bw),
         customer=self,
         reason=reason,
     )
     customer_order_change.save()
     for bag_type, quantity in bag_quantities.items():
         if not isinstance(bag_type, BagType):
             bag_quantity = CustomerOrderChangeBagQuantity(
                 customer_order_change=customer_order_change,
                 bag_type_id=int(bag_type),
                 quantity=quantity,
             )
         else:
             bag_quantity = CustomerOrderChangeBagQuantity(
                 customer_order_change=customer_order_change,
                 bag_type=bag_type,
                 quantity=quantity,
             )
         # XXX Do we need the save?
         bag_quantity.save()
예제 #6
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,
         })
예제 #7
0
 def save_model(self, request, line_item, form, change):
     now = timezone.now()
     bw = get_billing_week(now)
     line_item.reason = LineItem.MANUAL
     line_item.created = now
     line_item.created_in_billing_week = str(bw)
     line_item.save()
예제 #8
0
 def _get_skipped(self):
     skipped_dates = []
     now = timezone.now()
     bw = get_billing_week(now)
     bwstr = str(bw)
     if Skip.objects.filter(customer=self, billing_week=str(bwstr)).all():
         return True
     return False
예제 #9
0
 def _get_skipped(self):
     skipped_dates = []
     now = timezone.now()
     bw = get_billing_week(now)
     bwstr = str(bw)
     if Skip.objects.filter(customer=self, billing_week=str(bwstr)).all():
         return True
     return False
예제 #10
0
 def create_now(self, **p):
     assert 'created' not in p
     assert 'created_in_billing_week' not in p
     if 'now' not in p:
         p['now'] = timezone.now()
     now = p['now']
     del p['now']
     p['created'] = now
     p['created_in_billing_week'] = str(get_billing_week(now))
     return self.create(**p)
예제 #11
0
 def create_now(self, **p):
     assert 'created' not in p
     assert 'created_in_billing_week' not in p
     if 'now' not in p:
         p['now'] = timezone.now()
     now = p['now']
     del p['now']
     p['created'] = now
     p['created_in_billing_week'] = str(get_billing_week(now))
     return self.create(**p)
예제 #12
0
def gocardless_events_webhook(request):
    """
    POST and GET are empty: <QueryDict: {}>
    Body is a list of events: e.g. {"events":[{"id":"EVTESTWZXEMZMJ","created_at":"2016-08-08T13:33:07.528Z","resource_type":"payments","action":"created","links":{"payment":"index_ID_123"},"details":{"origin":"api","cause":"payment_created","description":"Payment created via the API."},"metadata":{}}]}
    """
    print('Here!')
    # Documentation is: https://developer.gocardless.com/2015-07-06/#webhooks-examples
    # 1.  Check the signature
    dig = hmac.new(settings.GOCARDLESS_WEBHOOK_SECRET.encode('utf8'),
                   msg=request.body,
                   digestmod=hashlib.sha256).hexdigest()
    if request.META['HTTP_WEBHOOK_SIGNATURE'] != dig:
        print(request.META['HTTP_WEBHOOK_SIGNATURE'], '!=', dig)
        return HttpResponse('Invalid Token',
                            status=498,
                            reason='Invalid Token')
    data = request.body.decode('utf-8')
    print(data)
    now = timezone.now()
    bw = get_billing_week(now)
    payload = json.loads(data)
    for event in payload['events']:
        # 2. Check that you have not already processed this event when
        #    receiving the same webhook for a different webhook endpoint
        if len(list(
                GoCardlessEvent.objects.filter(event_id=event['id']))) != 0:
            print('Already got this event: {}'.format(event))
            continue
        webhook = GoCardlessEvent(event=event, event_id=event['id'])
        webhook.save()
        # 3. Fetch the updated resource, using the ID supplied, and check that
        #    it has not changed further since the webhook was sent (since
        #    webhooks may arrive out of order)
        if event["resource_type"] == "payments":
            payment_id = event['links']['payment']
            payment_response = settings.GOCARDLESS_CLIENT.payments.get(
                payment_id)
            # 4. Act on the event, e.g. shipping goods, extending subscription
            payment = Payment.objects.filter(
                gocardless_payment_id=payment_id).get()
            payment_status_change = PaymentStatusChange(
                changed=now,
                changed_in_billing_week=str(bw),
                status=payment_response.status,
                payment=payment,
            )
            payment_status_change.save()
            # Let's treat 'confirmed' as paid, 'paid_out' is when we receive the payment
            if payment_response.status == 'confirmed':  # XXX Is this what we want?
                payment.completed = now
                payment.completed_in_billing_week = bw
                payment.save()
        # XXX Can send email alerts for other conditions?
    return HttpResponse('ok', status=200, reason='OK')
예제 #13
0
def dashboard_leave(request):
    if request.method == 'POST' and request.POST.get('cancel'):
        messages.add_message(
            request, messages.INFO,
            'You are still part of the scheme, and haven\'t left.')
        return redirect(reverse("dashboard"))
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = LeaveReasonForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            reason = dict(
                form.fields['reason'].choices)[form.cleaned_data['reason']]
            a = send_mail(
                '[BlueWorld] Leaver Notification',
                '''
                Hello from BlueWorldLite,

                {} has decided to leave the scheme. Here are the details:

                Reason: {}\n
                Comments:
                {}

                Thanks,

                The BlueWorldLite system
                '''.format(
                    request.user.customer.full_name,
                    reason,
                    form.cleaned_data['comments'],
                ),
                settings.DEFAULT_FROM_EMAIL,
                settings.LEAVER_EMAIL_TO,
                fail_silently=False,
            )
            # Only save changes now in case there is a problem with the email
            now = timezone.now()
            bw = get_billing_week(now)
            account_status_change = AccountStatusChange(
                changed=now,
                changed_in_billing_week=str(bw),
                customer=request.user.customer,
                status=AccountStatusChange.LEFT,
            )
            account_status_change.save()
            return redirect(reverse('dashboard_bye'))
    # if a GET (or any other method) we'll create a blank form
    else:
        form = LeaveReasonForm()
    return render(request, 'dashboard/leave.html', {
        'form': form,
    })
예제 #14
0
    def handle(self, *args, **options):
        json_path = options['path']

        self.stdout.write(self.style.NOTICE("looking up this path: {}".format(json_path)))
        with open(json_path) as data_file:
            blob_o_stuff = json.load(data_file)

            customers = [item for item in blob_o_stuff if item['model'] == 'customers.customer']
            bag_choices = [item for item in blob_o_stuff if item['model'] == 'customers.bagchoice']
            gc_subs = [item for item in blob_o_stuff if item['model'] == 'customers.gcsubscription']

            self.stdout.write(self.style.NOTICE("Found {} customer entries".format(len(customers))))
            self.stdout.write(self.style.NOTICE("Found {} bagchoice entries".format(len(bag_choices))))
            self.stdout.write(self.style.NOTICE("Found {} gc_sub entries".format(len(gc_subs))))

            total_customers = len(customers)
            for (index, c) in enumerate(customers):
                user = self._make_user(c)

                now = timezone.now()
                bw = get_billing_week(now)
                cf = c['fields']

                customer = Customer(
                    created=now,
                    created_in_billing_week=str(bw),
                    full_name="{} {}".format(cf['first_name'], cf['surname']),
                    nickname=cf['first_name'],
                    mobile=cf['telephone_1'],
                    user=user,
                    id=c['pk']
                )

                customer.save()

                account_status_change = AccountStatusChange(
                    changed=now,
                    changed_in_billing_week=str(bw),
                    customer=customer,
                    status=AccountStatusChange.ACTIVE,
                )
                account_status_change.save()

                old_bag_choices = [b
                                   for b in bag_choices
                                   if b['fields']['customer'] == c['pk']]

                customer.bag_quantities = self._convert_bag_choices(old_bag_choices)

                customer.collection_point = cf['pickup']
                self.stdout.write(self.style.SUCCESS("Imported {} of {}: {}".format(
                    index,
                    total_customers,
                    customer.full_name)))
예제 #15
0
 def _set_collection_point(self, collection_point_id):
     if not isinstance(collection_point_id, int):
         collection_point_id = collection_point_id.id
     now = timezone.now()
     bw = get_billing_week(now)
     collection_point_change = CustomerCollectionPointChange(
         changed=now,
         changed_in_billing_week=str(bw),
         customer=self,
         collection_point_id=collection_point_id,
     )
     collection_point_change.save()
예제 #16
0
 def _set_collection_point(self, collection_point_id):
     if not isinstance(collection_point_id, int):
         collection_point_id = collection_point_id.id
     now = timezone.now()
     bw = get_billing_week(now)
     collection_point_change = CustomerCollectionPointChange(
         changed=now,
         changed_in_billing_week=str(bw),
         customer=self,
         collection_point_id=collection_point_id,
     )
     collection_point_change.save()
예제 #17
0
def dashboard_gocardless(request):
    now = timezone.now()
    bw = get_billing_week(now)
    if request.method == 'POST':
        assert request.user.username
        # from django.contrib.sites.models import Site
        # current_site = Site.objects.get_current()
        # success_redirect_url = 'http://{}{}'.format(
        #     current_site.domain,
        #     reverse('gocardless_callback'),
        # )
        session_token = str(uuid.uuid4())
        success_redirect_url = '{}://{}{}'.format(
            request.scheme,
            request.META['HTTP_HOST'],
            reverse('gocardless_callback'),
        )
        if not settings.SKIP_GOCARDLESS:
            redirect_flow = settings.GOCARDLESS_CLIENT.redirect_flows.create(params={
                'description': 'Your Growing Communities Veg Box Order',
                'session_token': session_token,
                'success_redirect_url': success_redirect_url,
                # 'scheme': ... DD scheme.
            })
        else:
            redirect_flow = _RF()
            redirect_flow.id = str(uuid.uuid4())
        mandate = BillingGoCardlessMandate(
            session_token=session_token,
            gocardless_redirect_flow_id=redirect_flow.id,
            customer=request.user.customer,
            created=now,
            created_in_billing_week=str(bw),
            amount_notified=Decimal(request.POST['amount_notified']),
        )
        mandate.save()
        if settings.SKIP_GOCARDLESS:
            return redirect(reverse('gocardless_callback')+'?skip=true')
        else:
            return redirect(redirect_flow.redirect_url)
    else:
        number, amount, amount_per_week, first_bw = _get_cost_for_billing_week(request.user.customer, bw)
        return render(
            request,
            'dashboard/set-up-go-cardless.html',
            {
                'amount': amount,
                'number': number,
                'first_bw': first_bw,
            }
        )
예제 #18
0
 def _set_weekly_cost(self, weekly_cost):
     assert isinstance(weekly_cost, Decimal), \
         'Expecting a decimal, not {!r}'.format(weekly_cost)
     # XXX Potential race condition here
     cost_changes = BagTypeCostChange.objects.order_by('-changed').filter(
         bag_type=self)[:1]
     if not len(cost_changes) or cost_changes[0].weekly_cost != weekly_cost:
         now = timezone.now()
         bw = get_billing_week(now)
         cost_change = BagTypeCostChange(changed=now,
                                         changed_in_billing_week=str(bw),
                                         weekly_cost=weekly_cost,
                                         bag_type=self)
         cost_change.save()
예제 #19
0
def dashboard_gocardless(request):
    now = timezone.now()
    bw = get_billing_week(now)
    if request.method == 'POST':
        assert request.user.username
        # from django.contrib.sites.models import Site
        # current_site = Site.objects.get_current()
        # success_redirect_url = 'http://{}{}'.format(
        #     current_site.domain,
        #     reverse('gocardless_callback'),
        # )
        session_token = str(uuid.uuid4())
        success_redirect_url = '{}://{}{}'.format(
            request.scheme,
            request.META['HTTP_HOST'],
            reverse('gocardless_callback'),
        )
        if not settings.SKIP_GOCARDLESS:
            redirect_flow = settings.GOCARDLESS_CLIENT.redirect_flows.create(
                params={
                    'description': 'Your Growing Communities Veg Box Order',
                    'session_token': session_token,
                    'success_redirect_url': success_redirect_url,
                    # 'scheme': ... DD scheme.
                })
        else:
            redirect_flow = _RF()
            redirect_flow.id = str(uuid.uuid4())
        mandate = BillingGoCardlessMandate(
            session_token=session_token,
            gocardless_redirect_flow_id=redirect_flow.id,
            customer=request.user.customer,
            created=now,
            created_in_billing_week=str(bw),
            amount_notified=Decimal(request.POST['amount_notified']),
        )
        mandate.save()
        if settings.SKIP_GOCARDLESS:
            return redirect(reverse('gocardless_callback') + '?skip=true')
        else:
            return redirect(redirect_flow.redirect_url)
    else:
        number, amount, amount_per_week, first_bw = _get_cost_for_billing_week(
            request.user.customer, bw)
        return render(request, 'dashboard/set-up-go-cardless.html', {
            'amount': amount,
            'number': number,
            'first_bw': first_bw,
        })
예제 #20
0
    def queryset(self, request, queryset):
        if self.value() is None:
            return queryset

        # Check for people on holiday
        if self.value() == "HOLIDAY":
            # TODO check if we should move this into a separate filter
            customer_ids = Skip.objects.filter(billing_week=get_billing_week(
                timezone.now())).only('customer_id').values_list('customer_id')

        else:
            # otherwise
            customer_ids = self._by_status(self.value())

        return queryset.filter(pk__in=customer_ids)
예제 #21
0
    def queryset(self, request, queryset):
        if self.value() is None:
            return queryset

        # Check for people on holiday
        if self.value() == "HOLIDAY":
            # TODO check if we should move this into a separate filter
            customer_ids = Skip.objects.filter(
                billing_week=get_billing_week(timezone.now())
            ).only('customer_id').values_list('customer_id')

        else:
            # otherwise
            customer_ids = self._by_status(self.value())

        return queryset.filter(pk__in=customer_ids)
예제 #22
0
def gocardless_events_webhook(request):
    """
    POST and GET are empty: <QueryDict: {}>
    Body is a list of events: e.g. {"events":[{"id":"EVTESTWZXEMZMJ","created_at":"2016-08-08T13:33:07.528Z","resource_type":"payments","action":"created","links":{"payment":"index_ID_123"},"details":{"origin":"api","cause":"payment_created","description":"Payment created via the API."},"metadata":{}}]}
    """
    print('Here!')
    # Documentation is: https://developer.gocardless.com/2015-07-06/#webhooks-examples
    # 1.  Check the signature
    dig = hmac.new(settings.GOCARDLESS_WEBHOOK_SECRET.encode('utf8'), msg=request.body, digestmod=hashlib.sha256).hexdigest()
    if request.META['HTTP_WEBHOOK_SIGNATURE'] != dig:
        print(request.META['HTTP_WEBHOOK_SIGNATURE'], '!=', dig)
        return HttpResponse('Invalid Token', status=498, reason='Invalid Token')
    data = request.body.decode('utf-8')
    print(data)
    now = timezone.now()
    bw = get_billing_week(now)
    payload = json.loads(data)
    for event in payload['events']:
        # 2. Check that you have not already processed this event when
        #    receiving the same webhook for a different webhook endpoint
        if len(list(GoCardlessEvent.objects.filter(event_id=event['id']))) != 0:
            print('Already got this event: {}'.format(event))
            continue
        webhook = GoCardlessEvent(event=event, event_id=event['id'])
        webhook.save()
        # 3. Fetch the updated resource, using the ID supplied, and check that
        #    it has not changed further since the webhook was sent (since
        #    webhooks may arrive out of order)
        if event["resource_type"] == "payments":
            payment_id = event['links']['payment']
            payment_response = settings.GOCARDLESS_CLIENT.payments.get(payment_id)
            # 4. Act on the event, e.g. shipping goods, extending subscription
            payment = Payment.objects.filter(gocardless_payment_id=payment_id).get()
            payment_status_change = PaymentStatusChange(
                changed=now,
                changed_in_billing_week=str(bw),
                status=payment_response.status,
                payment=payment,
            )
            payment_status_change.save()
            # Let's treat 'confirmed' as paid, 'paid_out' is when we receive the payment
            if payment_response.status == 'confirmed':  # XXX Is this what we want?
                payment.completed = now
                payment.completed_in_billing_week = bw
                payment.save()
        # XXX Can send email alerts for other conditions?
    return HttpResponse('ok', status=200, reason='OK')
예제 #23
0
def billing_dates(request):
    pickup_dates = get_pickup_dates(start_of_the_month(timezone.now().year,
                                                       timezone.now().month),
                                    52,
                                    month_start=True)
    billing_dates = OrderedDict()
    bw_today = get_billing_week(timezone.now())
    for month in pickup_dates:
        billing_dates[month] = pickup_dates[month][0].start
    return render(
        request, 'billing-dates.html', {
            'pickup_dates': pickup_dates,
            'billing_dates': billing_dates,
            'billing_weeks_left': billing_weeks_left_in_the_month(
                str(bw_today)),
            'current_billing_week': bw_today
        })
예제 #24
0
 def _set_weekly_cost(self, weekly_cost):
     assert isinstance(weekly_cost, Decimal), \
         'Expecting a decimal, not {!r}'.format(weekly_cost)
     # XXX Potential race condition here
     cost_changes = BagTypeCostChange.objects.order_by(
         '-changed'
     ).filter(bag_type=self)[:1]
     if not len(cost_changes) or cost_changes[0].weekly_cost != weekly_cost:
         now = timezone.now()
         bw = get_billing_week(now)
         cost_change = BagTypeCostChange(
             changed=now,
             changed_in_billing_week=str(bw),
             weekly_cost=weekly_cost,
             bag_type=self
         )
         cost_change.save()
예제 #25
0
def dashboard_rejoin_scheme(request):
    if request.method == 'POST':
        now = timezone.now()
        bw = get_billing_week(now)
        account_status_change = AccountStatusChange(
            changed=now,
            changed_in_billing_week=str(bw),
            customer=request.user.customer,
            status=AccountStatusChange.ACTIVE,
        )
        account_status_change.save()
        messages.add_message(
            request,
            messages.INFO,
            'Successfully re-activated your account',
        )
        return redirect(reverse("dashboard"))
    else:
        return HttpResponseBadRequest()
예제 #26
0
def dashboard_rejoin_scheme(request):
    if request.method == 'POST':
        now = timezone.now()
        bw = get_billing_week(now)
        account_status_change = AccountStatusChange(
            changed=now,
            changed_in_billing_week=str(bw),
            customer=request.user.customer,
            status=AccountStatusChange.ACTIVE,
        )
        account_status_change.save()
        messages.add_message(
            request,
            messages.INFO,
            'Successfully re-activated your account',
        )
        return redirect(reverse("dashboard"))
    else:
        return HttpResponseBadRequest()
예제 #27
0
def billing_dates(request):
    pickup_dates = get_pickup_dates(
        start_of_the_month(timezone.now().year, timezone.now().month),
        52,
        month_start=True
    )
    billing_dates = OrderedDict()
    bw_today = get_billing_week(timezone.now())
    for month in pickup_dates:
        billing_dates[month] = pickup_dates[month][0].start
    return render(
        request,
        'billing-dates.html',
        {
            'pickup_dates': pickup_dates,
            'billing_dates': billing_dates,
            'billing_weeks_left': billing_weeks_left_in_the_month(str(bw_today)),
            'current_billing_week': bw_today
        }
    )
예제 #28
0
 def save_model(self, request, payment, form, change):
     mandate_id = payment.customer.gocardless_current_mandate.gocardless_mandate_id
     if not settings.SKIP_GOCARDLESS:
         payment_response_id, payment_response_status = payment.send_to_gocardless(mandate_id, payment.amount)
     else:
         payment_response_id = 'none'
         payment_response_status = 'skipped'
     now = timezone.now()
     bw = get_billing_week(now)
     payment.created = now
     payment.created_in_billing_week = str(bw)
     payment.gocardless_mandate_id = mandate_id
     payment.gocardless_payment_id = payment_response_id
     payment.reason = Payment.MANUAL
     payment.save()
     payment_status_change = PaymentStatusChange(
         changed=now,
         changed_in_billing_week=str(bw),
         status=payment_response_status,
         payment=payment,
     )
     payment_status_change.save()
예제 #29
0
 def signup(self, request, user):
     now = timezone.now()
     bw = get_billing_week(now)
     customer = Customer(
         created=now,
         created_in_billing_week=str(bw),
         full_name=self.cleaned_data['full_name'],
         nickname=self.cleaned_data['nickname'],
         mobile=self.cleaned_data['mobile'],
         user=user,
     )
     customer.save()
     account_status_change = AccountStatusChange(
         changed=now,
         changed_in_billing_week=str(bw),
         customer=customer,
         status=AccountStatusChange.AWAITING_DIRECT_DEBIT,
     )
     account_status_change.save()
     customer.collection_point = request.session['collection_point']
     customer._set_bag_quantities(request.session['bag_type'],
                                  reason='JOIN')
예제 #30
0
 def save_model(self, request, payment, form, change):
     mandate_id = payment.customer.gocardless_current_mandate.gocardless_mandate_id
     if not settings.SKIP_GOCARDLESS:
         payment_response_id, payment_response_status = payment.send_to_gocardless(
             mandate_id, payment.amount)
     else:
         payment_response_id = 'none'
         payment_response_status = 'skipped'
     now = timezone.now()
     bw = get_billing_week(now)
     payment.created = now
     payment.created_in_billing_week = str(bw)
     payment.gocardless_mandate_id = mandate_id
     payment.gocardless_payment_id = payment_response_id
     payment.reason = Payment.MANUAL
     payment.save()
     payment_status_change = PaymentStatusChange(
         changed=now,
         changed_in_billing_week=str(bw),
         status=payment_response_status,
         payment=payment,
     )
     payment_status_change.save()
예제 #31
0
def dashboard_skip_weeks(request):
    now = timezone.now()
    bw = get_billing_week(now)
    if request.method == 'POST' and request.POST.get('cancel'):
        messages.add_message(request, messages.INFO,
                             'Your skip weeks have not been changed.')
        return redirect(reverse("dashboard"))

    SkipFormSet = formset_factory(
        SkipForm,
        extra=0,
        formset=BaseSkipFormSet,
    )

    # We want to see the next 9 skip weeks we can change
    # We can't change things in the current billing week but
    # but we can change things in the next billing week
    skipped_billing_weeks = []
    if settings.ALLOW_SKIP_CURRENT_WEEK:
        startbw = bw
        bwqstr = str(bw)
    else:
        startbw = bw.next()
        bwqstr = str(startbw)
    for skip in Skip.objects.order_by('billing_week').filter(
            customer=request.user.customer,
            billing_week__gte=bwqstr,
    ):
        skipped_billing_weeks.append(skip.billing_week)

    valid_dates = {}
    initial_skips = []
    # Offer dates this month and for the next two.
    for month, pickup_dates in get_pickup_dates(startbw.wed, 9).items():
        for skipbw in pickup_dates:
            pickup_date = skipbw.wed
            skip_choice = {
                'id': str(skipbw),
                'skipbw': skipbw,
                'skipped': str(skipbw) in skipped_billing_weeks,
            }
            initial_skips.append(skip_choice)
            valid_dates[str(skipbw)] = skip_choice
    if request.method == 'POST':
        formset = SkipFormSet(
            request.POST,
            request.FILES,
            initial=initial_skips,
        )
        if formset.is_valid():
            to_skip = []
            to_unskip = []
            for row in formset.cleaned_data:
                skipbw = row['id']
                if row['skipped'] != valid_dates[skipbw]['skipped']:
                    if row['skipped'] is True:
                        to_skip.append(skipbw)
                    else:
                        to_unskip.append(skipbw)
            if not to_skip and not to_unskip:
                messages.add_message(
                    request, messages.ERROR, '''
                    You haven't made any changes to your skip weeks.
                    You can click Cancel if you are happy with your skip
                    weeks as they are.
                    ''')
                return redirect(reverse("dashboard_skip_weeks"))
            for skipbw in to_skip:
                assert skipbw not in to_unskip
                skip = Skip(
                    created=now,
                    created_in_billing_week=str(bw),
                    billing_week=skipbw,
                    customer=request.user.customer,
                )
                skip.save()
            for skipbw in to_unskip:
                skip = Skip.objects.filter(customer=request.user.customer,
                                           billing_week=skipbw).get()
                skip.delete()
            messages.add_message(
                request, messages.SUCCESS,
                'Your skip weeks have been updated successfully')
            return redirect(reverse("dashboard"))
    else:
        formset = SkipFormSet(initial=initial_skips)
    return render(request, 'dashboard/skip-weeks.html', {'formset': formset})
예제 #32
0
def gocardless_callback(request):
    if settings.GOCARDLESS_ENVIRONMENT == 'sandbox' and \
       request.GET.get('skip', '').lower() == 'true':
        assert settings.SKIP_GOCARDLESS
        mandate = list(
            BillingGoCardlessMandate.objects.filter(
                customer=request.user.customer, ).all())[-1]
        mandate.gocardless_mandate_id = str(uuid.uuid4())
    else:
        mandate = BillingGoCardlessMandate.objects.filter(
            customer=request.user.customer,
            gocardless_redirect_flow_id=request.GET['redirect_flow_id'],
        ).get()
        complete_redirect_flow = settings.GOCARDLESS_CLIENT.redirect_flows.complete(
            mandate.gocardless_redirect_flow_id,
            {'session_token': mandate.session_token})
        assert complete_redirect_flow.id == mandate.gocardless_redirect_flow_id
        mandate.gocardless_mandate_id = complete_redirect_flow.links.mandate
    now = timezone.now()
    bw = get_billing_week(now)
    mandate.in_use_for_customer = request.user.customer
    mandate.completed = now
    mandate.completed_in_billing_week = str(bw)
    mandate.save()
    tags = CustomerTag.objects.filter(tag='Starter').all()
    if tags:
        request.user.customer.tags.add(tags[0])
    request.user.customer.save()

    # Now, take the first payment if it is needed
    # Get the amount and date from the session.
    number, amount_pounds, amount_per_week, first_bw_of_next_month = _get_cost_for_billing_week(
        request.user.customer, bw)
    # If this isn't true, it is because it took over a week to complete GoCardless and this has to be dealt with manually.
    assert amount_pounds <= mandate.amount_notified
    assert int(amount_pounds * 100) == (amount_pounds * 100)

    if amount_pounds:
        # We don't want to make a payment unless there is something to pay.
        mandate_id = request.user.customer.gocardless_current_mandate.gocardless_mandate_id
        if settings.SKIP_GOCARDLESS:
            payment_response_id = str(uuid.uuid4())
            payment_response_status = 'skipped'
        else:
            payment_response_id, payment_response_status = Payment.send_to_gocardless(
                mandate_id, amount_pounds)
        payment = Payment(
            customer=request.user.customer,
            gocardless_mandate_id=mandate_id,
            amount=amount_pounds,
            reason=Payment.JOIN_WITH_COLLECTIONS_AVAILABLE,
            gocardless_payment_id=payment_response_id,
            created=now,
            created_in_billing_week=str(bw),
        )
        payment.save()
        payment_status_change = PaymentStatusChange(
            changed=now,
            changed_in_billing_week=str(bw),
            status=payment_response_status,
            payment=payment,
        )
        payment_status_change.save()
        bill_against = bw.next()
        for x in range(number):
            li = LineItem(payment=payment,
                          created=now,
                          created_in_billing_week=bw,
                          bill_against=str(bill_against),
                          customer=request.user.customer,
                          amount=amount_per_week,
                          reason=LineItem.NEW_JOINER)
            li.save()
            bill_against = bw.next()
    account_status_change = AccountStatusChange(
        changed=now,
        changed_in_billing_week=str(bw),
        customer=request.user.customer,
        status=AccountStatusChange.ACTIVE,
    )
    account_status_change.save()
    messages.add_message(request, messages.INFO,
                         'Successfully set up Go Cardless.')
    return redirect(reverse("dashboard"))
예제 #33
0
def dashboard(request):
    if request.user.customer.account_status != AccountStatusChange.LEFT:
        latest_cp_change = CustomerCollectionPointChange.objects.order_by(
            '-changed').filter(customer=request.user.customer)[:1]
        latest_customer_order_change = CustomerOrderChange.objects.order_by(
            '-changed').filter(customer=request.user.customer)[:1]
        collection_point = latest_cp_change[0].collection_point
        now = timezone.now()
        bw = get_billing_week(now)
        weekday = now.weekday()
        skipped_billing_weeks = []
        skipped = len(
            Skip.objects.order_by('billing_week').filter(
                customer=request.user.customer,
                billing_week=str(bw),
            ).all()) > 0

        if weekday == 6:  # Sunday
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday'
            else:
                collection_date = 'Wednesday and Thursday'
            if timezone.now().hour < bw.end.hour:
                deadline = '3pm today'
                changes_affect = "next week's collection"
            else:
                deadline = '3pm next Sunday'
                changes_affect = "the collection after next"
        elif weekday == 0:  # Monday
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday'
            else:
                collection_date = 'Wednesday and Thursday'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        elif weekday == 1:  # Tuesday
            if collection_point.collection_day == 'WED':
                collection_date = 'tomorrow'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday'
            else:
                collection_date = 'tomorrow and Thursday'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        elif weekday == 2:  # Wednesday
            if collection_point.collection_day == 'WED':
                collection_date = 'today'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'tomorrow'
            else:
                collection_date = 'today and tomorrow'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        elif weekday == 3:  # Thurs
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday next week'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'today'
            else:
                collection_date = 'today'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        else:
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday next week'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday next week'
            else:
                collection_date = 'Wednesday and Thursday next week'
            if weekday == 4:  # Friday
                deadline = '3pm this Sunday'
            else:  # Saturday
                deadline = '3pm tomorrow'
            changes_affect = "next week's collection"

        # fall back for the case when we have a user just starting this week
        if request.user.customer.created_in_billing_week == bw:
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday next week'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday next week'
            else:
                collection_date = 'Wednesday and Thursday next week'
            if weekday == 4:  # Friday
                deadline = '3pm this Sunday'
            else:  # Saturday
                deadline = '3pm tomorrow'
            changes_affect = "next week's collection"

        bag_quantities = CustomerOrderChangeBagQuantity.objects.filter(
            customer_order_change=latest_customer_order_change).all()
        if skipped:
            collection_date = collection_date.replace(' and ', ' or ')
            if not (collection_date.startswith('today')
                    or collection_date.startswith('tomorrow')):
                collection_date = 'on ' + collection_date
        return render(
            request, 'dashboard/index.html', {
                'bag_quantities': bag_quantities,
                'collection_point': collection_point,
                'collection_date': collection_date,
                'deadline': deadline,
                'changes_affect': changes_affect,
                'skipped': skipped,
            })
    else:
        return render(
            request,
            'dashboard/re-join-scheme.html',
        )
예제 #34
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
예제 #35
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
예제 #36
0
def dashboard_leave(request):
    if request.method == 'POST' and request.POST.get('cancel'):
        messages.add_message(
            request,
            messages.INFO,
            'You are still part of the scheme, and haven\'t left.'
        )
        return redirect(reverse("dashboard"))
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = LeaveReasonForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            reason = dict(form.fields['reason'].choices)[
                form.cleaned_data['reason']
            ]
            a = send_mail(
                '[BlueWorld] Leaver Notification',
                '''
                Hello from BlueWorldLite,

                {} has decided to leave the scheme. Here are the details:

                Reason: {}\n
                Comments:
                {}

                Thanks,

                The BlueWorldLite system
                '''.format(
                    request.user.customer.full_name,
                    reason,
                    form.cleaned_data['comments'],
                ),
                settings.DEFAULT_FROM_EMAIL,
                settings.LEAVER_EMAIL_TO,
                fail_silently=False,
            )
            # Only save changes now in case there is a problem with the email
            now = timezone.now()
            bw = get_billing_week(now)
            account_status_change = AccountStatusChange(
                changed=now,
                changed_in_billing_week=str(bw),
                customer=request.user.customer,
                status=AccountStatusChange.LEFT,
            )
            account_status_change.save()
            return redirect(reverse('dashboard_bye'))
    # if a GET (or any other method) we'll create a blank form
    else:
        form = LeaveReasonForm()
    return render(
        request,
        'dashboard/leave.html',
        {
            'form': form,
        }
    )
예제 #37
0
def dashboard_skip_weeks(request):
    now = timezone.now()
    bw = get_billing_week(now)
    if request.method == 'POST' and request.POST.get('cancel'):
        messages.add_message(
            request,
            messages.INFO,
            'Your skip weeks have not been changed.'
        )
        return redirect(reverse("dashboard"))

    SkipFormSet = formset_factory(
        SkipForm,
        extra=0,
        formset=BaseSkipFormSet,
    )

    # We want to see the next 9 skip weeks we can change
    # We can't change things in the current billing week but
    # but we can change things in the next billing week
    skipped_billing_weeks = []
    if settings.ALLOW_SKIP_CURRENT_WEEK:
        startbw = bw
        bwqstr = str(bw)
    else:
        startbw = bw.next()
        bwqstr = str(startbw)
    for skip in Skip.objects.order_by(
            'billing_week'
       ).filter(
           customer=request.user.customer,
           billing_week__gte=bwqstr,
       ):
        skipped_billing_weeks.append(skip.billing_week)

    valid_dates = {}
    initial_skips = []
    # Offer dates this month and for the next two.
    for month, pickup_dates in get_pickup_dates(startbw.wed, 9).items():
        for skipbw in pickup_dates:
            pickup_date = skipbw.wed
            skip_choice = {
                'id': str(skipbw),
                'skipbw': skipbw,
                'skipped': str(skipbw) in skipped_billing_weeks,
            }
            initial_skips.append(skip_choice)
            valid_dates[str(skipbw)] = skip_choice
    if request.method == 'POST':
        formset = SkipFormSet(
            request.POST,
            request.FILES,
            initial=initial_skips,
        )
        if formset.is_valid():
            to_skip = []
            to_unskip = []
            for row in formset.cleaned_data:
                skipbw = row['id']
                if row['skipped'] != valid_dates[skipbw]['skipped']:
                    if row['skipped'] is True:
                        to_skip.append(skipbw)
                    else:
                        to_unskip.append(skipbw)
            if not to_skip and not to_unskip:
                messages.add_message(
                    request,
                    messages.ERROR,
                    '''
                    You haven't made any changes to your skip weeks.
                    You can click Cancel if you are happy with your skip
                    weeks as they are.
                    '''
                )
                return redirect(reverse("dashboard_skip_weeks"))
            for skipbw in to_skip:
                assert skipbw not in to_unskip
                skip = Skip(
                    created=now,
                    created_in_billing_week=str(bw),
                    billing_week=skipbw,
                    customer=request.user.customer,
                )
                skip.save()
            for skipbw in to_unskip:
                skip = Skip.objects.filter(
                    customer=request.user.customer,
                    billing_week=skipbw
                ).get()
                skip.delete()
            messages.add_message(
                request,
                messages.SUCCESS,
                'Your skip weeks have been updated successfully'
            )
            return redirect(reverse("dashboard"))
    else:
        formset = SkipFormSet(initial=initial_skips)
    return render(
        request,
        'dashboard/skip-weeks.html',
        {'formset': formset}
    )
예제 #38
0
def gocardless_callback(request):
    if settings.GOCARDLESS_ENVIRONMENT == 'sandbox' and \
       request.GET.get('skip', '').lower() == 'true':
        assert settings.SKIP_GOCARDLESS
        mandate = list(BillingGoCardlessMandate.objects.filter(
            customer=request.user.customer,
        ).all())[-1]
        mandate.gocardless_mandate_id = str(uuid.uuid4())
    else:
        mandate = BillingGoCardlessMandate.objects.filter(
            customer=request.user.customer,
            gocardless_redirect_flow_id=request.GET['redirect_flow_id'],
        ).get()
        complete_redirect_flow = settings.GOCARDLESS_CLIENT.redirect_flows.complete(
            mandate.gocardless_redirect_flow_id,
            {'session_token': mandate.session_token}
        )
        assert complete_redirect_flow.id == mandate.gocardless_redirect_flow_id
        mandate.gocardless_mandate_id = complete_redirect_flow.links.mandate
    now = timezone.now()
    bw = get_billing_week(now)
    mandate.in_use_for_customer = request.user.customer
    mandate.completed = now
    mandate.completed_in_billing_week = str(bw)
    mandate.save()
    tags = CustomerTag.objects.filter(tag='Starter').all()
    if tags:
        request.user.customer.tags.add(tags[0])
    request.user.customer.save()

    # Now, take the first payment if it is needed
    # Get the amount and date from the session.
    number, amount_pounds, amount_per_week, first_bw_of_next_month = _get_cost_for_billing_week(request.user.customer, bw)
    # If this isn't true, it is because it took over a week to complete GoCardless and this has to be dealt with manually.
    assert amount_pounds <= mandate.amount_notified
    assert int(amount_pounds*100) == (amount_pounds*100)

    if amount_pounds:
        # We don't want to make a payment unless there is something to pay.
        mandate_id = request.user.customer.gocardless_current_mandate.gocardless_mandate_id
        if settings.SKIP_GOCARDLESS:
            payment_response_id = str(uuid.uuid4())
            payment_response_status = 'skipped'
        else:
            payment_response_id, payment_response_status = Payment.send_to_gocardless(mandate_id, amount_pounds)
        payment = Payment(
            customer=request.user.customer,
            gocardless_mandate_id=mandate_id,
            amount=amount_pounds,
            reason=Payment.JOIN_WITH_COLLECTIONS_AVAILABLE,
            gocardless_payment_id=payment_response_id,
            created=now,
            created_in_billing_week=str(bw),
        )
        payment.save()
        payment_status_change = PaymentStatusChange(
            changed=now,
            changed_in_billing_week=str(bw),
            status=payment_response_status,
            payment=payment,
        )
        payment_status_change.save()
        bill_against = bw.next()
        for x in range(number):
            li = LineItem(
                payment=payment,
                created=now,
                created_in_billing_week=bw,
                bill_against=str(bill_against),
                customer=request.user.customer,
                amount=amount_per_week,
                reason=LineItem.NEW_JOINER
            )
            li.save()
            bill_against = bw.next()
    account_status_change = AccountStatusChange(
        changed=now,
        changed_in_billing_week=str(bw),
        customer=request.user.customer,
        status=AccountStatusChange.ACTIVE,
    )
    account_status_change.save()
    messages.add_message(
        request,
        messages.INFO,
        'Successfully set up Go Cardless.'
    )
    return redirect(reverse("dashboard"))
예제 #39
0
def dashboard(request):
    if request.user.customer.account_status != AccountStatusChange.LEFT:
        latest_cp_change = CustomerCollectionPointChange.objects.order_by(
            '-changed'
        ).filter(customer=request.user.customer)[:1]
        latest_customer_order_change = CustomerOrderChange.objects.order_by(
            '-changed'
        ).filter(customer=request.user.customer)[:1]
        collection_point = latest_cp_change[0].collection_point
        now = timezone.now()
        bw = get_billing_week(now)
        weekday = now.weekday()
        skipped_billing_weeks = []
        skipped = len(
            Skip.objects.order_by('billing_week').filter(
                customer=request.user.customer,
                billing_week=str(bw),
            ).all()
        ) > 0

        if weekday == 6:  # Sunday
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday'
            else:
                collection_date = 'Wednesday and Thursday'
            if timezone.now().hour < bw.end.hour:
                deadline = '3pm today'
                changes_affect = "next week's collection"
            else:
                deadline = '3pm next Sunday'
                changes_affect = "the collection after next"
        elif weekday == 0:  # Monday
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday'
            else:
                collection_date = 'Wednesday and Thursday'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        elif weekday == 1:  # Tuesday
            if collection_point.collection_day == 'WED':
                collection_date = 'tomorrow'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday'
            else:
                collection_date = 'tomorrow and Thursday'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        elif weekday == 2:  # Wednesday
            if collection_point.collection_day == 'WED':
                collection_date = 'today'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'tomorrow'
            else:
                collection_date = 'today and tomorrow'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        elif weekday == 3:  # Thurs
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday next week'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'today'
            else:
                collection_date = 'today'
            deadline = '3pm this Sunday'
            changes_affect = "next week's collection"
        else:
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday next week'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday next week'
            else:
                collection_date = 'Wednesday and Thursday next week'
            if weekday == 4:  # Friday
                deadline = '3pm this Sunday'
            else:  # Saturday
                deadline = '3pm tomorrow'
            changes_affect = "next week's collection"

        # fall back for the case when we have a user just starting this week
        if request.user.customer.created_in_billing_week == bw:
            if collection_point.collection_day == 'WED':
                collection_date = 'Wednesday next week'
            elif collection_point.collection_day == 'THURS':
                collection_date = 'Thursday next week'
            else:
                collection_date = 'Wednesday and Thursday next week'
            if weekday == 4:  # Friday
                deadline = '3pm this Sunday'
            else:  # Saturday
                deadline = '3pm tomorrow'
            changes_affect = "next week's collection"

        bag_quantities = CustomerOrderChangeBagQuantity.objects.filter(
            customer_order_change=latest_customer_order_change
        ).all()
        if skipped:
            collection_date = collection_date.replace(' and ', ' or ')
            if not (
                collection_date.startswith('today') or
                collection_date.startswith('tomorrow')
            ):
                collection_date = 'on '+ collection_date
        return render(
            request,
            'dashboard/index.html',
            {
                'bag_quantities': bag_quantities,
                'collection_point': collection_point,
                'collection_date': collection_date,
                'deadline': deadline,
                'changes_affect': changes_affect,
                'skipped': skipped,
            }
        )
    else:
        return render(
            request,
            'dashboard/re-join-scheme.html',
        )
예제 #40
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()
예제 #41
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
예제 #42
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()
예제 #43
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
예제 #44
0
    def handle(self, *args, **options):
        json_path = options['path']

        self.stdout.write(
            self.style.NOTICE("looking up this path: {}".format(json_path)))
        with open(json_path) as data_file:
            blob_o_stuff = json.load(data_file)

            customers = [
                item for item in blob_o_stuff
                if item['model'] == 'customers.customer'
            ]
            bag_choices = [
                item for item in blob_o_stuff
                if item['model'] == 'customers.bagchoice'
            ]
            gc_subs = [
                item for item in blob_o_stuff
                if item['model'] == 'customers.gcsubscription'
            ]

            self.stdout.write(
                self.style.NOTICE("Found {} customer entries".format(
                    len(customers))))
            self.stdout.write(
                self.style.NOTICE("Found {} bagchoice entries".format(
                    len(bag_choices))))
            self.stdout.write(
                self.style.NOTICE("Found {} gc_sub entries".format(
                    len(gc_subs))))

            total_customers = len(customers)
            for (index, c) in enumerate(customers):
                user = self._make_user(c)

                now = timezone.now()
                bw = get_billing_week(now)
                cf = c['fields']

                customer = Customer(created=now,
                                    created_in_billing_week=str(bw),
                                    full_name="{} {}".format(
                                        cf['first_name'], cf['surname']),
                                    nickname=cf['first_name'],
                                    mobile=cf['telephone_1'],
                                    user=user,
                                    id=c['pk'])

                customer.save()

                account_status_change = AccountStatusChange(
                    changed=now,
                    changed_in_billing_week=str(bw),
                    customer=customer,
                    status=AccountStatusChange.ACTIVE,
                )
                account_status_change.save()

                old_bag_choices = [
                    b for b in bag_choices
                    if b['fields']['customer'] == c['pk']
                ]

                customer.bag_quantities = self._convert_bag_choices(
                    old_bag_choices)

                customer.collection_point = cf['pickup']
                self.stdout.write(
                    self.style.SUCCESS("Imported {} of {}: {}".format(
                        index, total_customers, customer.full_name)))