def get(self, request, subscriber_type, **kwargs): queryset_view = self.queryset_view_map[subscriber_type] class APIViewProxy(queryset_view): def __init__(self, provider): self.provider = provider view_proxy = APIViewProxy(self.get_organization()) view_proxy.get_range_queryset = MethodType( queryset_view.get_range_queryset, view_proxy) start_date = datetime_or_now( parse_datetime(request.GET.get('start_date', None))) end_date = datetime_or_now( parse_datetime(request.GET.get('end_date', None))) content = StringIO() csv_writer = csv.writer(content) csv_writer.writerow(['Name', 'Email', 'Registration Date']) for org in view_proxy.get_range_queryset(start_date, end_date): csv_writer.writerow([ org.full_name.encode('utf-8'), org.email.encode('utf-8'), org.created_at]) content.seek(0) resp = HttpResponse(content, content_type='text/csv') resp['Content-Disposition'] = \ 'attachment; filename="subscribers-{}-{}.csv"'.format( subscriber_type, datetime.now().strftime('%Y%m%d')) return resp
def get(self, request, *args, **kwargs): #pylint: disable=no-member,unused-argument self.provider = self.get_organization() start_at = request.GET.get('start_at', None) if start_at: start_at = parse_datetime(start_at) start_at = datetime_or_now(start_at) ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) queryset = self.get_range_queryset(start_at, ends_at) page_object_list = self.paginate_queryset(queryset) serializer = self.serializer_class() return Response({ 'start_at': start_at, 'ends_at': ends_at, 'count': queryset.count(), self.queryset_name: [ serializer.to_representation(organization) for organization in page_object_list ], })
def cache_fields(self, request): self.ends_at = datetime_or_now(parse_datetime(request.GET.get("ends_at", "").strip('"'))) self.start_at = request.GET.get("start_at", None) if self.start_at: self.start_at = datetime_or_now(parse_datetime(self.start_at.strip('"'))) else: self.start_at = self.ends_at + dateutil.relativedelta.relativedelta(months=-1)
def get(self, request, subscriber_type, **kwargs): queryset_view = self.queryset_view_map[subscriber_type] class APIViewProxy(queryset_view): def __init__(self, provider): self.provider = provider view_proxy = APIViewProxy(self.get_organization()) view_proxy.get_range_queryset = MethodType( queryset_view.get_range_queryset, view_proxy) start_date = datetime_or_now( parse_datetime(request.GET.get('start_date', None))) end_date = datetime_or_now( parse_datetime(request.GET.get('end_date', None))) content = StringIO() csv_writer = csv.writer(content) csv_writer.writerow(['Name', 'Email', 'Registration Date']) for org in view_proxy.get_range_queryset(start_date, end_date): csv_writer.writerow([ org.full_name.encode('utf-8'), org.email.encode('utf-8'), org.created_at ]) content.seek(0) resp = HttpResponse(content, content_type='text/csv') resp['Content-Disposition'] = \ 'attachment; filename="subscribers-{}-{}.csv"'.format( subscriber_type, datetime.now().strftime('%Y%m%d')) return resp
def get(self, request, *args, **kwargs): self.provider = self.get_organization() self.start_date = datetime_or_now( parse_datetime(request.GET.get('start_date', None).strip('"'))) self.end_date = datetime_or_now( parse_datetime(request.GET.get('end_date', None).strip('"'))) return super(AbstractSubscriberPipelineDownloadView, self).get( request, *args, **kwargs)
def cache_fields(self, request): self.ends_at = datetime_or_now( parse_datetime(request.GET.get('ends_at', '').strip('"'))) self.start_at = request.GET.get('start_at', None) if self.start_at: self.start_at = datetime_or_now( parse_datetime(self.start_at.strip('"'))) else: self.start_at = (self.ends_at + dateutil.relativedelta.relativedelta(months=-1))
def get_queryset(self): ''' Implement date range filtering ''' qs = super(SmartTransactionListMixin, self).get_queryset() start = datetime_or_now( parse_datetime(self.request.GET.get('start_at', '').strip('"'))) end = datetime_or_now( parse_datetime(self.request.GET.get('ends_at', '').strip('"'))) return qs.filter(created_at__gte=start, created_at__lte=end)
def cache_fields(self, request): #pylint: disable=unused-argument self.organization = self.get_organization() self.start_at = self.request.GET.get('start_at', None) if self.start_at: self.start_at = parse_datetime(self.start_at) self.start_at = datetime_or_now(self.start_at) self.ends_at = self.request.GET.get('ends_at', None) if self.ends_at: self.ends_at = parse_datetime(self.ends_at) self.ends_at = datetime_or_now(self.ends_at)
def get_queryset(self): kwargs = {} start_at = self.request.GET.get("start_at", None) if start_at: start_at = datetime_or_now(parse_datetime(start_at)) kwargs.update({"created_at__lt": start_at}) ends_at = self.request.GET.get("ends_at", None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) return Subscription.objects.filter( organization__slug=self.kwargs.get("organization"), ends_at__gte=ends_at, **kwargs )
def get_queryset(self): kwargs = {} start_at = self.request.GET.get('start_at', None) if start_at: start_at = datetime_or_now(parse_datetime(start_at)) kwargs.update({'created_at__lt': start_at}) ends_at = self.request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) return Subscription.objects.filter( plan__organization=self.get_organization(), ends_at__gte=ends_at, **kwargs).order_by('-ends_at')
def get_queryset(self): kwargs = {} start_at = self.request.GET.get('start_at', None) if start_at: start_at = datetime_or_now(parse_datetime(start_at)) kwargs.update({'created_at__lt': start_at}) ends_at = self.request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) return User.objects.exclude( Q(manages__subscription__created_at__lt=ends_at) | Q(contributes__subscription__created_at__lt=ends_at)).order_by( '-date_joined', 'last_name').distinct()
def get(self, request, table_key, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) reverse = True account_title = 'Payments' account = Transaction.FUNDS # TODO: refactor to only build the table (customer or amount) # relevant to the request account_table, customer_table, customer_extra = \ aggregate_monthly_transactions(self.get_organization(), account, account_title=account_title, from_date=ends_at, reverse=reverse) data = SortedDict() # By convention, if we have a ``unit``, the table contains # amounts in cents. We thus scale by 0.01 to get a human # readable 'whole dollar' amounts. data['amount'] = { "title": "Amount", "unit": "$", "scale": 0.01, "table": account_table } data['customers'] = { "title": "Customers", "table": customer_table, "extra": customer_extra } return Response({"title": "Revenue Metrics", "data": data[table_key]})
def create_charges_for_balance(until=None): """ Create charges for all accounts payable. """ until = datetime_or_now(until) for organization in Organization.objects.all(): charges = Charge.objects.filter(customer=organization).exclude( state=Charge.DONE).aggregate(Sum('amount')) inflight_charges = charges['amount__sum'] # We will create charges only when we have no charges # already in flight for this customer. if not inflight_charges: balance_t = Transaction.objects.get_organization_payable( organization, until=until) if balance_t.dest_amount > 50: LOGGER.info('CHARGE %dc to %s', balance_t.dest_amount, balance_t.dest_organization) # Stripe will not processed charges less than 50 cents. try: balance_t.save() Charge.objects.charge_card(balance_t.dest_organization, balance_t) except: raise else: LOGGER.info('SKIP %s (less than 50c)', balance_t.dest_organization) else: LOGGER.info('SKIP %s (one charge already in flight)', balance_t.dest_organization)
def get(self, request, table_key, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) reverse = True account_title = 'Payments' account = Transaction.FUNDS # TODO: refactor to only build the table (customer or amount) # relevant to the request account_table, customer_table, customer_extra = \ aggregate_monthly_transactions(self.get_organization(), account, account_title=account_title, from_date=ends_at, reverse=reverse) data = SortedDict() # By convention, if we have a ``unit``, the table contains # amounts in cents. We thus scale by 0.01 to get a human # readable 'whole dollar' amounts. data['amount'] = {"title": "Amount", "unit": "$", "scale": 0.01, "table": account_table} data['customers'] = {"title": "Customers", "table": customer_table, "extra": customer_extra} return Response( {"title": "Revenue Metrics", "data": data[table_key]})
def get(self, request, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) # XXX to fix: returns payments in customer currency account_table, _, _ = \ aggregate_monthly_transactions(self.get_organization(), Transaction.FUNDS, account_title='Payments', from_date=ends_at, orig='dest', dest='orig') _, refund_amount = aggregate_monthly(self.get_organization(), Transaction.REFUND, from_date=ends_at, orig='dest', dest='dest') account_table += [{"key": "Refunds", "values": refund_amount}] return Response({ "title": "Amount", "unit": "$", "scale": 0.01, "table": account_table })
def get(self, request, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) organization = self.get_organization() table = [] for plan in Plan.objects.filter(organization=organization): values = active_subscribers(plan, from_date=self.kwargs.get('from_date')) table.append({ "key": plan.slug, "values": values, "is_active": plan.is_active }) extra = [{ "key": "churn", "values": churn_subscribers(from_date=self.kwargs.get('from_date')) }] return Response({ "title": "Active Subscribers", "table": table, "extra": extra })
def create_charge_on_card(card, amount, unit, descr=None, stmt_descr=None): #pylint: disable=too-many-arguments,unused-argument LOGGER.debug('create_charge_on_card(amount=%s, unit=%s, descr=%s)', amount, unit, descr) created_at = datetime_or_now() return (generate_random_slug(), created_at, '1234', created_at + datetime.timedelta(days=365))
def create_transfer(organization, amount, descr=None): """ Transfer *amount* into the organization bank account. """ LOGGER.debug("create_transfer(organization=%s, amount=%s, descr=%s)", organization, amount, descr) created_at = datetime_or_now() return (generate_random_slug(), created_at)
def get_queryset(self): """ GET displays the balance due by a subscriber. Template: To edit the layout of this page, create a local \ ``saas/billing/balance.html`` (`example <https://github.com/djaodjin\ /djaodjin-saas/tree/master/saas/templates/saas/billing/balance.html>`__). Template context: - ``STRIPE_PUB_KEY`` Public key to send to stripe.com - ``invoicables`` List of items to be invoiced (with options) - ``organization`` The provider of the product - ``request`` The HTTP request object POST attempts to charge the card for the balance due. """ self.customer = self.get_organization() invoicables = [] created_at = datetime_or_now() for subscription in Subscription.objects.active_for(self.customer): options = self.get_invoicable_options(subscription, created_at) if len(options) > 0: invoicables += [{ 'subscription': subscription, 'name': 'cart-%s' % subscription.plan.slug, 'lines': [], 'options': options }] return invoicables
def create_charges_for_balance(until=None): """ Create charges for all accounts payable. """ until = datetime_or_now(until) for organization in Organization.objects.all(): charges = Charge.objects.filter(customer=organization).exclude( state=Charge.DONE).aggregate(Sum('amount')) inflight_charges = charges['amount__sum'] # We will create charges only when we have no charges # already in flight for this customer. if not inflight_charges: balance_t = Transaction.objects.get_organization_payable( organization, until=until) if balance_t.dest_amount > 50: LOGGER.info('CHARGE %dc to %s', balance_t.dest_amount, balance_t.dest_organization) # Stripe will not processed charges less than 50 cents. try: balance_t.save() Charge.objects.charge_card( balance_t.dest_organization, balance_t) except: raise else: LOGGER.info('SKIP %s (less than 50c)', balance_t.dest_organization) else: LOGGER.info('SKIP %s (one charge already in flight)', balance_t.dest_organization)
def month_periods(nb_months=12, from_date=None): """constructs a list of (nb_months + 1) dates in the past that fall on the first of each month until *from_date* which is the last entry of the list returned.""" dates = [] if from_date and isinstance(from_date, basestring): from_date = parse_datetime(from_date) from_date = datetime_or_now(from_date) dates.append(from_date) last = datetime( day=from_date.day, month=from_date.month, year=from_date.year, tzinfo=utc) if last.day != 1: last = datetime(day=1, month=last.month, year=last.year, tzinfo=utc) dates.append(last) nb_months = nb_months - 1 for _ in range(0, nb_months): year = last.year month = last.month - 1 if month < 1: year = last.year - month / 12 - 1 month = 12 - (month % 12) last = datetime(day=1, month=month, year=year, tzinfo=utc) dates.append(last) dates.reverse() return dates
def month_periods(nb_months=12, from_date=None): """constructs a list of (nb_months + 1) dates in the past that fall on the first of each month until *from_date* which is the last entry of the list returned.""" dates = [] if from_date and isinstance(from_date, basestring): from_date = parse_datetime(from_date) from_date = datetime_or_now(from_date) dates.append(from_date) last = datetime(day=from_date.day, month=from_date.month, year=from_date.year, tzinfo=utc) if last.day != 1: last = datetime(day=1, month=last.month, year=last.year, tzinfo=utc) dates.append(last) nb_months = nb_months - 1 for _ in range(0, nb_months): year = last.year month = last.month - 1 if month < 1: year = last.year - month / 12 - 1 month = 12 - (month % 12) last = datetime(day=1, month=month, year=year, tzinfo=utc) dates.append(last) dates.reverse() return dates
def get_queryset(self): """ GET displays the balance due by a subscriber. Template: To edit the layout of this page, create a local \ ``saas/billing/balance.html`` (`example <https://github.com/djaodjin\ /djaodjin-saas/tree/master/saas/templates/saas/billing/balance.html>`__). Template context: - ``STRIPE_PUB_KEY`` Public key to send to stripe.com - ``invoicables`` List of items to be invoiced (with options) - ``organization`` The provider of the product - ``request`` The HTTP request object POST attempts to charge the card for the balance due. """ self.customer = self.get_organization() invoicables = [] created_at = datetime_or_now() for subscription in Subscription.objects.active_for(self.customer): options = self.get_invoicable_options(subscription, created_at) if len(options) > 0: invoicables += [{ 'subscription': subscription, 'name': 'cart-%s' % subscription.plan.slug, 'lines': [], 'options': options}] return invoicables
def get_queryset(self): """ GET displays the balance due by a subscriber. Template: To edit the layout of this page, create a local \ ``saas/billing/balance.html`` (`example <https://github.com/djaodjin\ /djaodjin-saas/tree/master/saas/templates/saas/billing/balance.html>`__). Template context: - organization - request POST attempts to charge the card for the balance due. """ self.customer = self.get_organization() invoicables = [] created_at = datetime_or_now() for subscription in Subscription.objects.active_for(self.customer): options = self.get_invoicable_options(subscription, created_at) if len(options) > 0: invoicables += [{ 'subscription': subscription, 'name': 'cart-%s' % subscription.plan.slug, 'lines': [], 'options': options}] return invoicables
def get(self, request, *args, **kwargs): #pylint: disable=unused-argument organization = self.get_organization() at_date = datetime_or_now(request.DATA.get('at', None)) return Response([{ 'key': Transaction.INCOME, 'values': monthly_balances( organization, Transaction.INCOME, at_date) }])
def get(self, request, *args, **kwargs): #pylint: disable=unused-argument organization = self.get_organization() start_at = request.GET.get('start_at', None) if start_at: start_at = parse_datetime(start_at) start_at = datetime_or_now(start_at) ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) result = [] for key in Transaction.objects.distinct_accounts(): result += [{ 'key': key, 'values': monthly_balances(organization, key, ends_at) }] return Response(result)
def create_transfer(organization, amount, descr=None): """ Transfer *amount* into the organization bank account. """ LOGGER.debug('create_transfer(organization=%s, amount=%s, descr=%s)', organization, amount, descr) created_at = datetime_or_now() return (generate_random_slug(), created_at)
def get_queryset(self): self.customer = self.get_organization() created_at = datetime_or_now() prorate_to_billing = False prorate_to = None if prorate_to_billing: # XXX First we add enough periods to get the next billing date later # than created_at but no more than one period in the future. prorate_to = self.customer.billing_start invoicables = [] for cart_item in CartItem.objects.get_cart(user=self.request.user): if cart_item.email: full_name = ' '.join( [cart_item.first_name, cart_item.last_name]).strip() for_descr = ', for %s (%s)' % (full_name, cart_item.email) organization_queryset = Organization.objects.filter( email=cart_item.email) if organization_queryset.exists(): organization = organization_queryset.get() else: organization = Organization( full_name='%s %s' % (cart_item.first_name, cart_item.last_name), email=cart_item.email) else: for_descr = '' organization = self.customer try: subscription = Subscription.objects.get( organization=organization, plan=cart_item.plan) except Subscription.DoesNotExist: ends_at = prorate_to if not ends_at: ends_at = created_at subscription = Subscription.objects.new_instance( organization, cart_item.plan, ends_at=ends_at) lines = [] options = self.get_invoicable_options(subscription, created_at, prorate_to=prorate_to, coupon=cart_item.coupon) if cart_item.nb_periods > 0: # The number of periods was already selected so we generate # a line instead. for line in options: if line.orig_amount == cart_item.nb_periods: line.descr += for_descr lines += [line] options = [] break invoicables += [{ 'name': cart_item.name, 'descr': cart_item.descr, 'subscription': subscription, "lines": lines, "options": options }] return invoicables
def get(self, request, *args, **kwargs): #pylint: disable=unused-argument organization = self.get_organization() start_at = request.GET.get('start_at', None) if start_at: start_at = parse_datetime(start_at) start_at = datetime_or_now(start_at) ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) result = [] for key in [Transaction.INCOME, Transaction.BACKLOG, Transaction.RECEIVABLE]: result += [{ 'key': key, 'values': monthly_balances(organization, key, ends_at) }] return Response({'title': "Balances", 'unit': "$", 'scale': 0.01, 'table': result})
def get(self, request, *args, **kwargs): #pylint: disable=no-member,unused-argument self.provider = self.get_organization() start_at = request.GET.get('start_at', None) if start_at: start_at = parse_datetime(start_at) start_at = datetime_or_now(start_at) ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) queryset = self.get_queryset(start_at, ends_at) serializer = self.serializer_class() return Response({ 'start_at': start_at, 'ends_at': ends_at, 'count': queryset.count(), self.queryset_name: [serializer.to_native(organization) for organization in queryset], })
def get_queryset(self): self.customer = self.get_organization() created_at = datetime_or_now() prorate_to_billing = False prorate_to = None if prorate_to_billing: # XXX First we add enough periods to get the next billing date later # than created_at but no more than one period in the future. prorate_to = self.customer.billing_start invoicables = [] for cart_item in CartItem.objects.get_cart(user=self.request.user): if cart_item.email: full_name = ' '.join([ cart_item.first_name, cart_item.last_name]).strip() for_descr = ', for %s (%s)' % (full_name, cart_item.email) organization_queryset = Organization.objects.filter( email=cart_item.email) if organization_queryset.exists(): organization = organization_queryset.get() else: organization = Organization( full_name='%s %s' % ( cart_item.first_name, cart_item.last_name), email=cart_item.email) else: for_descr = '' organization = self.customer try: subscription = Subscription.objects.get( organization=organization, plan=cart_item.plan) except Subscription.DoesNotExist: ends_at = prorate_to if not ends_at: ends_at = created_at subscription = Subscription.objects.new_instance( organization, cart_item.plan, ends_at=ends_at) lines = [] options = self.get_invoicable_options(subscription, created_at, prorate_to=prorate_to, coupon=cart_item.coupon) if cart_item.nb_periods > 0: # The number of periods was already selected so we generate # a line instead. for line in options: if line.orig_amount == cart_item.nb_periods: line.descr += for_descr lines += [line] options = [] break invoicables += [{ 'name': cart_item.name, 'descr': cart_item.descr, 'subscription': subscription, "lines": lines, "options": options}] return invoicables
def recognize_income(until=None): """ Create all ``Transaction`` necessary to recognize revenue on each ``Subscription`` until date specified. """ until = datetime_or_now(until) for subscription in Subscription.objects.filter(created_at__lte=until, ends_at__gt=until): with transaction.atomic(): # [``recognize_start``, ``recognize_end``[ is one period over which # revenue is recognized. It will slide over the subscription # lifetime from ``created_at`` to ``until``. to_recognize_amount = 0 recognize_period = relativedelta(months=1) order_subscribe_beg = subscription.created_at recognize_start = subscription.created_at recognize_end = recognize_start + recognize_period for order in Transaction.objects.get_subscription_receivable(subscription): # [``order_subscribe_beg``, ``order_subscribe_end``[ is # the subset of the subscription lifetime the order paid for. # It covers ``total_periods`` plan periods. total_periods = order.get_event().plan.period_number(order.descr) order_subscribe_end = subscription.plan.end_of_period(order_subscribe_beg, nb_periods=total_periods) min_end = min(order_subscribe_end, until) while recognize_end <= min_end: # we use ``<=`` here because we compare that bounds # are equal instead of searching for points within # the interval. nb_periods = subscription.nb_periods(recognize_start, recognize_end) to_recognize_amount = (nb_periods * order.dest_amount) / total_periods recognized_amount, _ = Transaction.objects.get_subscription_income_balance( subscription, starts_at=recognize_start, ends_at=recognize_end ) # We are not computing a balance sheet here but looking for # a positive amount to compare with the revenue that should # have been recognized. recognized_amount = abs(recognized_amount) if to_recognize_amount > recognized_amount: # We have some amount of revenue to recognize here. # ``at_time`` is set just before ``recognize_end`` # so we do not include the newly created transaction # in the subsequent period. Transaction.objects.create_income_recognized( subscription, amount=to_recognize_amount - recognized_amount, at_time=recognize_end - relativedelta(seconds=1), descr=DESCRIBE_RECOGNIZE_INCOME % {"period_start": recognize_start, "period_end": recognize_end}, ) recognize_start = recognize_end recognize_end += recognize_period order_subscribe_beg = order_subscribe_end if recognize_end >= until: break
def get_queryset(self): self.customer = self.get_organization() invoicables = [] created_at = datetime_or_now() for subscription in Subscription.objects.active_for(self.customer): options = self.get_invoicable_options(subscription, created_at) if len(options) > 0: invoicables += [{ 'subscription': subscription, 'name': 'cart-%s' % subscription.plan.slug, 'lines': [], 'options': options}] return invoicables
def get_queryset(self): self.customer = self.get_organization() invoicables = [] created_at = datetime_or_now() for subscription in Subscription.objects.active_for(self.customer): options = self.get_invoicable_options(subscription, created_at) if len(options) > 0: invoicables += [{ 'subscription': subscription, 'name': 'cart-%s' % subscription.plan.slug, 'lines': [], 'options': options }] return invoicables
def redeem(request, coupon_code): now = datetime_or_now() coupon_applied = False for item in CartItem.objects.get_cart(request.user): coupon = Coupon.objects.filter( Q(ends_at__isnull=True) | Q(ends_at__gt=now), code__iexact=coupon_code, # case incensitive search. organization=item.plan.organization).first() if coupon and (not coupon.plan or (coupon.plan == item.plan)): # Coupon can be restricted to a plan or apply to all plans # of an organization. coupon_applied = True item.coupon = coupon item.save() return coupon_applied
def get(self, request, *args, **kwargs): #pylint: disable=unused-argument organization = self.get_organization() start_at = request.GET.get('start_at', None) if start_at: start_at = parse_datetime(start_at) start_at = datetime_or_now(start_at) ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) result = [] for key in [ Transaction.INCOME, Transaction.BACKLOG, Transaction.RECEIVABLE ]: result += [{ 'key': key, 'values': monthly_balances(organization, key, ends_at) }] return Response({ 'title': "Balances", 'unit': "$", 'scale': 0.01, 'table': result })
def pass_paid_subscription(request, organization=None, plan=None): #pylint: disable=unused-argument if organization and not isinstance(organization, Organization): organization = get_object_or_404(Organization, slug=organization) if plan and not isinstance(plan, Plan): plan = get_object_or_404(Plan, slug=plan) subscribed_at = datetime_or_now() subscriptions = Subscription.objects.filter( organization=organization, plan=plan, ends_at__gt=subscribed_at) if not subscriptions.exists(): raise Http404("%(organization)s has no subscription to %(plan)s'\ ' as of %(date)s" % {'organization': organization, 'plan': plan, 'date': subscribed_at}) return not subscriptions.first().is_locked
def reconcile_transfers(self, provider): if provider.processor_deposit_key: balance = provider.withdraw_available() timestamp = datetime_to_timestamp(balance['created_at']) try: kwargs = self._prepare_request(provider=None) transfers = stripe.Transfer.all(created={'gt': timestamp}, recipient=provider.processor_deposit_key, **kwargs) with transaction.atomic(): for transfer in transfers.data: created_at = datetime_or_now( datetime.datetime.fromtimestamp(transfer.created)) provider.create_withdraw_transactions( transfer.id, transfer.amount, transfer.currency, transfer.description, created_at=created_at) except stripe.error.InvalidRequestError as err: LOGGER.exception(err)
def pass_paid_subscription(request, organization=None, plan=None): #pylint: disable=unused-argument if organization and not isinstance(organization, Organization): organization = get_object_or_404(Organization, slug=organization) if plan and not isinstance(plan, Plan): plan = get_object_or_404(Plan, slug=plan) subscribed_at = datetime_or_now() subscriptions = Subscription.objects.filter(organization=organization, plan=plan, ends_at__gt=subscribed_at) if not subscriptions.exists(): raise Http404("%(organization)s has no subscription to %(plan)s'\ ' as of %(date)s" % { 'organization': organization, 'plan': plan, 'date': subscribed_at }) return not subscriptions.first().is_locked
def generate_subscriptions(self, subscriber, nb_subscriptions=None): at_time = datetime_or_now() if nb_subscriptions is None: nb_subscriptions = settings.REST_FRAMEWORK['PAGE_SIZE'] * 4 self.stdout.write("%d subscriptions\n" % nb_subscriptions) nb_plans = Plan.objects.count() fake = Faker() for _ in range(0, nb_subscriptions): rank = random.randint(1, nb_plans - 1) plan = Plan.objects.all().order_by('pk')[rank] created_at = fake.date_time_between_dates( datetime_start=at_time - datetime.timedelta(365), datetime_end=at_time + datetime.timedelta(365)) Subscription.objects.create(organization=subscriber, plan=plan, created_at=created_at, ends_at=created_at + datetime.timedelta(30))
def is_testing(site): if site.slug in [settings.APP_NAME, '%s-master' % settings.APP_NAME]: return False if site.db_name == settings.DB_TEST: return True # We only want to automatically switch to testing if a subscription isn't # paid for mallspace. It is necessary for the on-boarding business logic. # In streetside, we prefer to avoid an extra query. # Implementation note: # We use ``organization_id=site.account_id`` such that Django does not # start generating queries with our djaodjin-master organization ids # into the streetside database. if not is_streetside(site): subscriptions = Subscription.objects.db_manager( using='default').filter( organization_id=site.account_id, ends_at__gt=datetime_or_now()).exclude(plan__slug='streetside') return not subscriptions.exists() return False
def get(self, request, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) account_title = 'Payments' account = Transaction.RECEIVABLE # We use ``Transaction.RECEIVABLE`` which technically counts the number # or orders, not the number of payments. _, customer_table, customer_extra = \ aggregate_monthly_transactions(self.get_organization(), account, account_title=account_title, from_date=ends_at) return Response( {"title": "Customers", "table": customer_table, "extra": customer_extra})
def get(self, request, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) organization = self.get_organization() table = [] for plan in Plan.objects.filter(organization=organization): values = active_subscribers( plan, from_date=self.kwargs.get('from_date')) table.append({"key": plan.slug, "values": values, "is_active": plan.is_active}) extra = [{"key": "churn", "values": churn_subscribers( from_date=self.kwargs.get('from_date'))}] return Response( {"title": "Active Subscribers", "table": table, "extra": extra})
def get(self, request, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) account_title = 'Payments' account = Transaction.RECEIVABLE # We use ``Transaction.RECEIVABLE`` which technically counts the number # or orders, not the number of payments. _, customer_table, customer_extra = \ aggregate_monthly_transactions(self.get_organization(), account, account_title=account_title, from_date=ends_at) return Response({ "title": "Customers", "table": customer_table, "extra": customer_extra })
def get(self, request, *args, **kwargs): ends_at = request.GET.get('ends_at', None) if ends_at: ends_at = parse_datetime(ends_at) ends_at = datetime_or_now(ends_at) # XXX to fix: returns payments in customer currency account_table, _, _ = \ aggregate_monthly_transactions(self.get_organization(), Transaction.FUNDS, account_title='Payments', from_date=ends_at, orig='dest', dest='orig') _, refund_amount = aggregate_monthly( self.get_organization(), Transaction.REFUND, from_date=ends_at, orig='dest', dest='dest') account_table += [{"key": "Refunds", "values": refund_amount}] return Response( {"title": "Amount", "unit": "$", "scale": 0.01, "table": account_table})
def _handle(self, *args, **options): #pylint: disable=too-many-locals,too-many-statements from saas.managers.metrics import month_periods # avoid import loop from saas.models import (Charge, ChargeItem, Organization, Plan, Subscription) if 'database' in options: db_name = options['database'] set_current_site(get_site_model().objects.get( slug=options['provider'], db_name=db_name), path_prefix='') else: db_name = 'default' now = datetime.datetime.utcnow().replace(tzinfo=utc) from_date = now from_date = datetime.datetime(year=from_date.year, month=from_date.month, day=1) if args: from_date = datetime.datetime.strptime(args[0], '%Y-%m-%d') # Create Income transactions that represents a growing bussiness. provider = Organization.objects.get(slug=options['provider']) processor = Organization.objects.get(pk=PROCESSOR_ID) for end_period in month_periods(from_date=from_date): nb_new_customers = random.randint(0, 9) for _ in range(nb_new_customers): queryset = Plan.objects.filter(organization=provider, period_amount__gt=0) plan = queryset[random.randint(0, queryset.count() - 1)] created = False trials = 0 while not created: try: first_name = self.FIRST_NAMES[random.randint( 0, len(self.FIRST_NAMES) - 1)] last_name = self.LAST_NAMES[random.randint( 0, len(self.LAST_NAMES) - 1)] full_name = '%s %s' % (first_name, last_name) slug = slugify('demo%d' % random.randint(1, 1000)) customer, created = Organization.objects.get_or_create( slug=slug, full_name=full_name) #pylint: disable=catching-non-exception except IntegrityError: trials = trials + 1 if trials > 10: raise RuntimeError( 'impossible to create a new customer after 10 trials.' ) Organization.objects.filter(pk=customer.id).update( created_at=end_period) subscription = Subscription.objects.create( organization=customer, plan=plan, ends_at=now + datetime.timedelta(days=31)) Subscription.objects.filter(pk=subscription.id).update( created_at=end_period) # Insert some churn in % churn_rate = 2 all_subscriptions = Subscription.objects.filter( plan__organization=provider) nb_churn_customers = (all_subscriptions.count() * churn_rate // 100) subscriptions = random.sample( list(all_subscriptions), all_subscriptions.count() - nb_churn_customers) for subscription in subscriptions: nb_periods = random.randint(1, 6) transaction_item = Transaction.objects.new_subscription_order( subscription, nb_natural_periods=nb_periods, created_at=end_period) if transaction_item.dest_amount < 50: continue transaction_item.orig_amount = transaction_item.dest_amount transaction_item.orig_unit = transaction_item.dest_unit transaction_item.save() charge = Charge.objects.create( created_at=transaction_item.created_at, amount=transaction_item.dest_amount, customer=subscription.organization, description='Charge for %d periods' % nb_periods, last4=1241, exp_date=datetime_or_now(), processor=processor, processor_key=transaction_item.pk, # XXX We can't do that yet because of # ``processor_backend.charge_distribution(self)`` # unit=transaction_item.dest_unit, state=Charge.CREATED) charge.created_at = transaction_item.created_at charge.save() ChargeItem.objects.create(invoiced=transaction_item, charge=charge) charge.payment_successful() churned = all_subscriptions.exclude( pk__in=[subscription.pk for subscription in subscriptions]) for subscription in churned: subscription.ends_at = end_period subscription.save() self.stdout.write( "%d new and %d churned customers at %s\n" % (nb_new_customers, nb_churn_customers, end_period))
def generate_transactions(self, provider, processor, from_date, ends_at, fake=None, profile_pictures_dir=None): """ Create Income transactions that represents a growing bussiness. """ #pylint: disable=too-many-locals from saas.metrics.base import month_periods # avoid import loop if not fake: fake = Faker() user_model = get_user_model() # Load list of profile pcitures profile_pictures_males = [] profile_pictures_females = [] if profile_pictures_dir: for picture_name in os.listdir(profile_pictures_dir): if picture_name.startswith("1"): profile_pictures_males += [ "/media/livedemo/profiles/%s" % picture_name ] else: profile_pictures_females += [ "/media/livedemo/profiles/%s" % picture_name ] for end_period in month_periods(from_date=from_date): nb_new_customers = random.randint(0, 9) for _ in range(nb_new_customers): queryset = Plan.objects.filter(organization=provider, period_amount__gt=0) plan = queryset[random.randint(0, queryset.count() - 1)] created = False trials = 0 while not created: try: picture = None if random.randint(0, 1): full_name = fake.name_male() if profile_pictures_males: picture = profile_pictures_males[ random.randint( 0, len(profile_pictures_males) - 1)] else: full_name = fake.name_female() if profile_pictures_females: picture = profile_pictures_females[ random.randint( 0, len(profile_pictures_females) - 1)] slug = slugify('demo%d' % random.randint(1, 1000)) email = "%s@%s" % (slug, fake.domain_name()) first_name, mid_name, last_name = \ full_name_natural_split(full_name) customer, created = Organization.objects.get_or_create( slug=slug, full_name=full_name, email=email, phone=fake.phone_number(), street_address=fake.street_address(), locality=fake.city(), postal_code=fake.postcode(), region=fake.state_abbr(), country=fake.country_code(), picture=picture) user, created = user_model.objects.get_or_create( username=slug, email=email, first_name=first_name, last_name=last_name) customer.add_manager(user, at_time=end_period) #pylint: disable=catching-non-exception except IntegrityError: trials = trials + 1 if trials > 10: raise RuntimeError( 'impossible to create a new customer after 10 trials.' ) Organization.objects.filter(pk=customer.id).update( created_at=end_period) subscription = Subscription.objects.create( organization=customer, plan=plan, ends_at=ends_at + datetime.timedelta(days=31)) Subscription.objects.filter(pk=subscription.id).update( created_at=end_period) # Insert some churn in % churn_rate = 2 all_subscriptions = Subscription.objects.filter( plan__organization=provider) nb_churn_customers = (all_subscriptions.count() * churn_rate // 100) subscriptions = random.sample( list(all_subscriptions), all_subscriptions.count() - nb_churn_customers) for subscription in subscriptions: nb_periods = random.randint(1, 6) subscription.ends_at = subscription.plan.end_of_period( subscription.ends_at, nb_periods=nb_periods) transaction_item = Transaction.objects.new_subscription_order( subscription, amount=subscription.plan.period_amount * nb_periods, descr=humanize.describe_buy_periods( subscription.plan, subscription.ends_at, nb_periods), created_at=end_period) if transaction_item.dest_amount < 50: continue subscription.save() transaction_item.orig_amount = transaction_item.dest_amount transaction_item.orig_unit = transaction_item.dest_unit transaction_item.save() charge = Charge.objects.create( created_at=transaction_item.created_at, amount=transaction_item.dest_amount, customer=subscription.organization, description=humanize.DESCRIBE_CHARGED_CARD % { 'charge': generate_random_slug(prefix='ch_'), 'organization': subscription.organization }, last4=1241, exp_date=datetime_or_now(), processor=processor, processor_key="ch_%s" % str(transaction_item.pk), state=Charge.CREATED) charge.created_at = transaction_item.created_at charge.save() ChargeItem.objects.create(invoiced=transaction_item, charge=charge) charge.payment_successful() churned = all_subscriptions.exclude( pk__in=[subscription.pk for subscription in subscriptions]) for subscription in churned: subscription.ends_at = end_period subscription.save() self.stdout.write( "%d new and %d churned customers at %s\n" % (nb_new_customers, nb_churn_customers, end_period))
def get_filename(self): return 'subscribers-{}-{}.csv'.format( self.subscriber_type, datetime_or_now().strftime('%Y%m%d'))
def handle(self, *args, **options): #pylint: disable=too-many-locals,too-many-statements from saas.managers.metrics import month_periods # avoid import loop from saas.models import (Charge, ChargeItem, Organization, Plan, Subscription) now = datetime.datetime.utcnow().replace(tzinfo=utc) from_date = now from_date = datetime.datetime( year=from_date.year, month=from_date.month, day=1) if len(args) > 0: from_date = datetime.datetime.strptime( args[0], '%Y-%m-%d') # Create Income transactions that represents a growing bussiness. provider = Organization.objects.get(pk=2) processor = Organization.objects.get(pk=PROCESSOR_ID) for end_period in month_periods(from_date=from_date): nb_new_customers = random.randint(0, 9) for _ in range(nb_new_customers): queryset = Plan.objects.filter( organization=provider, period_amount__gt=0) plan = queryset[random.randint(0, queryset.count() - 1)] created = False trials = 0 while not created: try: first_name = self.FIRST_NAMES[random.randint( 0, len(self.FIRST_NAMES)-1)] last_name = self.LAST_NAMES[random.randint( 0, len(self.LAST_NAMES)-1)] full_name = '%s %s' % (first_name, last_name) slug = slugify('demo%d' % random.randint(1, 1000)) customer, created = Organization.objects.get_or_create( slug=slug, full_name=full_name) #pylint: disable=catching-non-exception except IntegrityError: trials = trials + 1 if trials > 10: raise RuntimeError( 'impossible to create a new customer after 10 trials.') Organization.objects.filter(pk=customer.id).update( created_at=end_period) subscription = Subscription.objects.new_instance( customer, plan, ends_at=now + datetime.timedelta(days=31)) subscription.save() Subscription.objects.filter( pk=subscription.id).update(created_at=end_period) # Insert some churn in % churn_rate = 2 all_subscriptions = Subscription.objects.filter( plan__organization=provider) nb_churn_customers = (all_subscriptions.count() * churn_rate / 100) subscriptions = random.sample(all_subscriptions, all_subscriptions.count() - nb_churn_customers) for subscription in subscriptions: nb_periods = random.randint(1, 6) transaction_item = Transaction.objects.new_subscription_order( subscription, nb_natural_periods=nb_periods, created_at=end_period) transaction_item.orig_amount = transaction_item.dest_amount transaction_item.orig_unit = transaction_item.dest_unit transaction_item.save() charge = Charge.objects.create( created_at=transaction_item.created_at, amount=transaction_item.dest_amount, customer=subscription.organization, description='Charge for %d periods' % nb_periods, last4=1241, exp_date=datetime_or_now(), processor=processor, processor_key=transaction_item.pk, # XXX We can't do that yet because of # ``PROCESSOR_BACKEND.charge_distribution(self)`` # unit=transaction_item.dest_unit, state=Charge.CREATED) charge.created_at = transaction_item.created_at charge.save() ChargeItem.objects.create( invoiced=transaction_item, charge=charge) charge.payment_successful() churned = all_subscriptions.exclude( pk__in=[subscription.pk for subscription in subscriptions]) for subscription in churned: subscription.ends_at = end_period subscription.save() print "%d new and %d churned customers at %s" % ( nb_new_customers, nb_churn_customers, end_period)
def get_queryset(self): #pylint: disable=too-many-locals self.customer = self.get_organization() created_at = datetime_or_now() prorate_to_billing = False prorate_to = None if prorate_to_billing: # XXX First we add enough periods to get the next billing date later # than created_at but no more than one period in the future. prorate_to = self.customer.billing_start invoicables = [] for cart_item in CartItem.objects.get_cart(user=self.request.user): if cart_item.email: full_name = ' '.join([ cart_item.first_name, cart_item.last_name]).strip() for_descr = ', for %s (%s)' % (full_name, cart_item.email) organization_queryset = Organization.objects.filter( email=cart_item.email) if organization_queryset.exists(): organization = organization_queryset.get() else: organization = Organization( full_name='%s %s' % ( cart_item.first_name, cart_item.last_name), email=cart_item.email) else: for_descr = '' organization = self.customer try: # If we can extend a current ``Subscription`` we will. # XXX For each (organization, plan) there should not # be overlapping timeframe [created_at, ends_at[, # None-the-less, it might be a good idea to catch # and throw a nice error message in case. subscription = Subscription.objects.get( organization=organization, plan=cart_item.plan, ends_at__gt=datetime_or_now()) except Subscription.DoesNotExist: ends_at = prorate_to if not ends_at: ends_at = created_at subscription = Subscription.objects.new_instance( organization, cart_item.plan, ends_at=ends_at) lines = [] options = self.get_invoicable_options(subscription, created_at, prorate_to=prorate_to, coupon=cart_item.coupon) if cart_item.nb_periods > 0: # The number of periods was already selected so we generate # a line instead. for line in options: plan = subscription.plan nb_periods = plan.period_number(line.descr) if nb_periods == cart_item.nb_periods: # ``TransactionManager.new_subscription_order`` # will have created a ``Transaction`` # with the ultimate subscriber # as payee. Overriding ``dest_organization`` here # insures in all cases (bulk and direct buying), # the transaction is recorded (in ``execute_order``) # on behalf of the customer on the checkout page. line.dest_organization = self.customer line.descr += for_descr lines += [line] options = [] break invoicables += [{ 'name': cart_item.name, 'descr': cart_item.descr, 'subscription': subscription, "lines": lines, "options": options}] return invoicables
def handle(self, *args, **options): #pylint: disable=too-many-locals,too-many-statements from saas.metrics.base import month_periods # avoid import loop RazorpayBackend.bypass_api = True now = datetime.datetime.utcnow().replace(tzinfo=utc) from_date = now from_date = datetime.datetime(year=from_date.year, month=from_date.month, day=1) if args: from_date = datetime.datetime.strptime(args[0], '%Y-%m-%d') # Create a set of 3 plans broker = get_broker() plan, _ = Plan.objects.get_or_create(slug='basic', defaults={ 'title': "Basic", 'description': "Basic Plan", 'period_amount': 24900, 'broker_fee_percent': 0, 'period_type': 4, 'organization': broker, 'is_active': True }) advance_discount = AdvanceDiscount.objects.get_or_create( plan=plan, discount_type=AdvanceDiscount.PERCENTAGE, amount=1000, length=12) Plan.objects.get_or_create(slug='medium', defaults={ 'title': "Medium", 'description': "Medium Plan", 'period_amount': 24900, 'broker_fee_percent': 0, 'period_type': 4, 'organization': broker, 'is_active': True }) plan, _ = Plan.objects.get_or_create(slug='premium', defaults={ 'title': "Premium", 'description': "Premium Plan", 'period_amount': 18900, 'broker_fee_percent': 0, 'period_type': 4, 'organization': broker, 'is_active': True }) advance_discount = AdvanceDiscount.objects.get_or_create( plan=plan, discount_type=AdvanceDiscount.PERCENTAGE, amount=81, length=12) # Create Income transactions that represents a growing bussiness. provider = Organization.objects.get(slug=options['provider']) processor = Organization.objects.get(pk=PROCESSOR_ID) for end_period in month_periods(from_date=from_date): nb_new_customers = random.randint(0, 9) for _ in range(nb_new_customers): queryset = Plan.objects.filter(organization=provider, period_amount__gt=0) plan = queryset[random.randint(0, queryset.count() - 1)] created = False trials = 0 while not created: try: first_name = self.FIRST_NAMES[random.randint( 0, len(self.FIRST_NAMES) - 1)] last_name = self.LAST_NAMES[random.randint( 0, len(self.LAST_NAMES) - 1)] full_name = '%s %s' % (first_name, last_name) slug = slugify('demo%d' % random.randint(1, 1000)) customer, created = Organization.objects.get_or_create( slug=slug, full_name=full_name) #pylint: disable=catching-non-exception except IntegrityError: trials = trials + 1 if trials > 10: raise RuntimeError( 'impossible to create a new customer after 10 trials.' ) Organization.objects.filter(pk=customer.id).update( created_at=end_period) subscription = Subscription.objects.create( organization=customer, plan=plan, ends_at=now + datetime.timedelta(days=31)) Subscription.objects.filter(pk=subscription.id).update( created_at=end_period) # Insert some churn in % churn_rate = 2 all_subscriptions = Subscription.objects.filter( plan__organization=provider) nb_churn_customers = (all_subscriptions.count() * churn_rate // 100) subscriptions = random.sample( list(all_subscriptions), all_subscriptions.count() - nb_churn_customers) for subscription in subscriptions: nb_periods = random.randint(1, 6) amount = nb_periods * subscription.plan.period_amount ends_at = subscription.plan.end_of_period( subscription.ends_at, nb_periods) transaction_item = Transaction.objects.new_subscription_order( subscription, amount=amount, descr=humanize.describe_buy_periods( subscription.plan, ends_at, nb_periods), created_at=end_period) if transaction_item.dest_amount < 50: continue transaction_item.orig_amount = transaction_item.dest_amount transaction_item.orig_unit = transaction_item.dest_unit transaction_item.save() charge = Charge.objects.create( created_at=transaction_item.created_at, amount=transaction_item.dest_amount, customer=subscription.organization, description='Charge for %d periods' % nb_periods, last4=1241, exp_date=datetime_or_now(), processor=processor, processor_key=str(transaction_item.pk), # XXX We can't do that yet because of # ``PROCESSOR_BACKEND.charge_distribution(self)`` # unit=transaction_item.dest_unit, state=Charge.CREATED) charge.created_at = transaction_item.created_at charge.save() ChargeItem.objects.create(invoiced=transaction_item, charge=charge) charge.payment_successful() churned = all_subscriptions.exclude( pk__in=[subscription.pk for subscription in subscriptions]) for subscription in churned: subscription.ends_at = end_period subscription.save() self.stdout.write( "%d new and %d churned customers at %s" % (nb_new_customers, nb_churn_customers, end_period))
def get_filename(self): return 'registered-{}.csv'.format(datetime_or_now().strftime('%Y%m%d'))
def generate_transactions(self, provider, processor, from_date, ends_at): """ Create Income transactions that represents a growing bussiness. """ #pylint: disable=too-many-locals from saas.managers.metrics import month_periods # avoid import loop for end_period in month_periods(from_date=from_date): nb_new_customers = random.randint(0, 9) for _ in range(nb_new_customers): queryset = Plan.objects.filter(organization=provider, period_amount__gt=0) plan = queryset[random.randint(0, queryset.count() - 1)] created = False trials = 0 while not created: try: first_name = self.FIRST_NAMES[random.randint( 0, len(self.FIRST_NAMES) - 1)] last_name = self.LAST_NAMES[random.randint( 0, len(self.LAST_NAMES) - 1)] full_name = '%s %s' % (first_name, last_name) slug = slugify('demo%d' % random.randint(1, 1000)) customer, created = Organization.objects.get_or_create( slug=slug, full_name=full_name) #pylint: disable=catching-non-exception except IntegrityError: trials = trials + 1 if trials > 10: raise RuntimeError( 'impossible to create a new customer after 10 trials.' ) Organization.objects.filter(pk=customer.id).update( created_at=end_period) subscription = Subscription.objects.create( organization=customer, plan=plan, ends_at=ends_at + datetime.timedelta(days=31)) Subscription.objects.filter(pk=subscription.id).update( created_at=end_period) # Insert some churn in % churn_rate = 2 all_subscriptions = Subscription.objects.filter( plan__organization=provider) nb_churn_customers = (all_subscriptions.count() * churn_rate // 100) subscriptions = random.sample( list(all_subscriptions), all_subscriptions.count() - nb_churn_customers) for subscription in subscriptions: nb_periods = random.randint(1, 6) subscription.ends_at = subscription.plan.end_of_period( subscription.ends_at, nb_periods=nb_periods) transaction_item = Transaction.objects.new_subscription_order( subscription, amount=subscription.plan.period_amount * nb_periods, descr=humanize.describe_buy_periods( subscription.plan, subscription.ends_at, nb_periods), created_at=end_period) if transaction_item.dest_amount < 50: continue subscription.save() transaction_item.orig_amount = transaction_item.dest_amount transaction_item.orig_unit = transaction_item.dest_unit transaction_item.save() charge = Charge.objects.create( created_at=transaction_item.created_at, amount=transaction_item.dest_amount, customer=subscription.organization, description='Charge for %d periods' % nb_periods, last4=1241, exp_date=datetime_or_now(), processor=processor, processor_key=transaction_item.pk, # XXX We can't do that yet because of # ``processor_backend.charge_distribution(self)`` # unit=transaction_item.dest_unit, state=Charge.CREATED) charge.created_at = transaction_item.created_at charge.save() ChargeItem.objects.create(invoiced=transaction_item, charge=charge) charge.payment_successful() churned = all_subscriptions.exclude( pk__in=[subscription.pk for subscription in subscriptions]) for subscription in churned: subscription.ends_at = end_period subscription.save() self.stdout.write( "%d new and %d churned customers at %s\n" % (nb_new_customers, nb_churn_customers, end_period))