def ensure_full_coverage(self, subscriptions): plan_version = DefaultProductPlan.get_default_plan_by_domain( self.domain, edition=SoftwarePlanEdition.COMMUNITY).plan.get_version() if not plan_version.feature_charges_exist_for_domain(self.domain): return community_ranges = self.get_community_ranges(subscriptions) if not community_ranges: return do_not_invoice = any([s.do_not_invoice for s in subscriptions]) account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, created_by_invoicing=True)[0] if account.date_confirmed_extra_charges is None: subject = "[%s] Invoice Generation Issue" % self.domain.name email_content = render_to_string( 'accounting/invoice_error_email.html', { 'project': self.domain.name, 'error_msg': "This project is incurring charges on their " "Community subscription, but they haven't " "agreed to the charges yet. Someone should " "follow up with this project to see if everything " "is configured correctly or if communication " "needs to happen between Dimagi and the project's" "admins. For now, the invoices generated are " "marked as Do Not Invoice.", }) send_HTML_email(subject, settings.BILLING_EMAIL, email_content, email_from="Dimagi Billing Bot <%s>" % settings.DEFAULT_FROM_EMAIL) do_not_invoice = True if not BillingContactInfo.objects.filter(account=account).exists(): # No contact information exists for this account. # This shouldn't happen, but if it does, we can't continue # with the invoice generation. raise BillingContactInfoError( "Project %s has incurred charges, but does not have their " "Billing Contact Info filled out. Someone should follow up " "on this." % self.domain.name) # First check to make sure none of the existing subscriptions is set # to do not invoice. Let's be on the safe side and not send a # community invoice out, if that's the case. for c in community_ranges: # create a new community subscription for each # date range that the domain did not have a subscription community_subscription = Subscription( account=account, plan_version=plan_version, subscriber=self.subscriber, date_start=c[0], date_end=c[1], do_not_invoice=do_not_invoice, ) community_subscription.save() subscriptions.append(community_subscription)
def generate_domain_subscription_from_date(date_start, billing_account, domain, min_num_months=None, is_immediately_active=False, delay_invoicing_until=None, save=True): # make sure the first month is never a full month (for testing) date_start = date_start.replace(day=max(2, date_start.day)) subscription_length = random.randint(min_num_months or 3, 25) date_end_year, date_end_month = add_months(date_start.year, date_start.month, subscription_length) date_end_last_day = calendar.monthrange(date_end_year, date_end_month)[1] # make sure that the last month is never a full month (for testing) date_end = datetime.date(date_end_year, date_end_month, min(date_end_last_day - 1, date_start.day + 1)) subscriber, _ = Subscriber.objects.get_or_create(domain=domain, organization=None) subscription = Subscription( account=billing_account, plan_version=arbitrary_subscribable_plan(), subscriber=subscriber, salesforce_contract_id=data_gen.arbitrary_unique_name("SFC")[:80], date_start=date_start, date_end=date_end, is_active=is_immediately_active, date_delay_invoicing=delay_invoicing_until, ) if save: subscription.save() return subscription, subscription_length
def generate_domain_subscription_from_date(date_start, billing_account, domain, min_num_months=None, is_immediately_active=False, delay_invoicing_until=None, save=True): # make sure the first month is never a full month (for testing) date_start = date_start.replace(day=max(2, date_start.day)) subscription_length = random.randint(min_num_months or 3, 25) date_end_year, date_end_month = add_months(date_start.year, date_start.month, subscription_length) date_end_last_day = calendar.monthrange(date_end_year, date_end_month)[1] # make sure that the last month is never a full month (for testing) date_end = datetime.date(date_end_year, date_end_month, min(date_end_last_day - 1, date_start.day + 1)) subscriber, _ = Subscriber.objects.get_or_create(domain=domain, organization=None) subscription = Subscription( account=billing_account, plan_version=arbitrary_subscribable_plan(), subscriber=subscriber, salesforce_contract_id=data_gen.arbitrary_unique_name("SFC")[:80], date_start=date_start, date_end=date_end, is_active=is_immediately_active, date_delay_invoicing=delay_invoicing_until, ) if save: subscription.save() return subscription, subscription_length
def ensure_full_coverage(self, subscriptions): plan_version = DefaultProductPlan.get_default_plan_by_domain( self.domain, edition=SoftwarePlanEdition.COMMUNITY ).plan.get_version() if not plan_version.feature_charges_exist_for_domain(self.domain): return community_ranges = self.get_community_ranges(subscriptions) if not community_ranges: return do_not_invoice = any([s.do_not_invoice for s in subscriptions]) account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, created_by_invoicing=True)[0] if account.date_confirmed_extra_charges is None: if self.domain.is_active: subject = "[%s] Invoice Generation Issue" % self.domain.name email_content = render_to_string( 'accounting/invoice_error_email.html', { 'project': self.domain.name, 'error_msg': "This project is incurring charges on their " "Community subscription, but they haven't " "agreed to the charges yet. Someone should " "follow up with this project to see if everything " "is configured correctly or if communication " "needs to happen between Dimagi and the project's" "admins. For now, the invoices generated are " "marked as Do Not Invoice.", } ) send_HTML_email( subject, settings.BILLING_EMAIL, email_content, email_from="Dimagi Billing Bot <%s>" % settings.DEFAULT_FROM_EMAIL ) do_not_invoice = True if not BillingContactInfo.objects.filter(account=account).exists(): # No contact information exists for this account. # This shouldn't happen, but if it does, we can't continue # with the invoice generation. raise BillingContactInfoError( "Project %s has incurred charges, but does not have their " "Billing Contact Info filled out. Someone should follow up " "on this." % self.domain.name ) # First check to make sure none of the existing subscriptions is set # to do not invoice. Let's be on the safe side and not send a # community invoice out, if that's the case. for c in community_ranges: # create a new community subscription for each # date range that the domain did not have a subscription community_subscription = Subscription( account=account, plan_version=plan_version, subscriber=self.subscriber, date_start=c[0], date_end=c[1], do_not_invoice=do_not_invoice, ) community_subscription.save() subscriptions.append(community_subscription)
def test_next_subscription_filter_no_end_date(self): next_subscription = Subscription( account=self.subscription.account, plan_version=self.subscription.plan_version, subscriber=self.subscription.subscriber, date_start=self.subscription.date_end, date_end=None, ) next_subscription.save() self.assertEqual(next_subscription, self.subscription.next_subscription)
def generate_domain_subscription(account, domain, date_start, date_end, plan_version=None, service_type=SubscriptionType.NOT_SET): subscriber, _ = Subscriber.objects.get_or_create(domain=domain.name) subscription = Subscription( account=account, plan_version=plan_version or subscribable_plan_version(), subscriber=subscriber, date_start=date_start, date_end=date_end, service_type=service_type, ) subscription.save() return subscription
def subscription(self): """ If we're arriving here, it's because there wasn't a subscription for the period of this invoice, so let's create one. """ subscriber, _ = Subscriber.objects.get_or_create(domain=self.domain.name) subscription = Subscription( account=self.account, subscriber=subscriber, plan_version=self.software_plan_version, date_start=self.date_start, date_end=self.date_end, ) subscription.save() return subscription
def create_subscription(self): account = BillingAccount.objects.get(id=self.cleaned_data['account']) date_start = self.cleaned_data['start_date'] date_end = self.cleaned_data['end_date'] date_delay_invoicing = self.cleaned_data['delay_invoice_until'] plan_version_id = self.cleaned_data['plan_version'] domain = self.cleaned_data['domain'] subscription = Subscription(account=account, date_start=date_start, date_end=date_end, date_delay_invoicing=date_delay_invoicing, plan_version=SoftwarePlanVersion.objects.get(id=plan_version_id), salesforce_contract_id=self.cleaned_data['salesforce_contract_id'], subscriber=Subscriber.objects.get_or_create(domain=domain, organization=None)[0]) subscription.save() return subscription
def ensure_full_coverage(self, subscriptions): plan_version = DefaultProductPlan.get_default_plan_by_domain( self.domain, edition=SoftwarePlanEdition.COMMUNITY ).plan.get_version() if not plan_version.feature_charges_exist_for_domain(self.domain): return community_ranges = self.get_community_ranges(subscriptions) if not community_ranges: return do_not_invoice = any([s.do_not_invoice for s in subscriptions]) account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, created_by_invoicing=True, entry_point=EntryPoint.SELF_STARTED, )[0] if account.date_confirmed_extra_charges is None: logger.info( "Did not generate invoice because date_confirmed_extra_charges " "was null for domain %s" % self.domain.name ) do_not_invoice = True if not BillingContactInfo.objects.filter(account=account).exists(): # No contact information exists for this account. # This shouldn't happen, but if it does, we can't continue # with the invoice generation. raise BillingContactInfoError( "Project %s has incurred charges, but does not have their " "Billing Contact Info filled out." % self.domain.name ) # First check to make sure none of the existing subscriptions is set # to do not invoice. Let's be on the safe side and not send a # community invoice out, if that's the case. for c in community_ranges: # create a new community subscription for each # date range that the domain did not have a subscription community_subscription = Subscription( account=account, plan_version=plan_version, subscriber=self.subscriber, date_start=c[0], date_end=c[1], do_not_invoice=do_not_invoice, ) community_subscription.save() subscriptions.append(community_subscription)
def ensure_full_coverage(self, subscriptions): plan_version = DefaultProductPlan.get_default_plan_by_domain( self.domain, edition=SoftwarePlanEdition.COMMUNITY ).plan.get_version() if not plan_version.feature_charges_exist_for_domain(self.domain): return community_ranges = self.get_community_ranges(subscriptions) if not community_ranges: return do_not_invoice = any([s.do_not_invoice for s in subscriptions]) account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, created_by_invoicing=True)[0] if account.date_confirmed_extra_charges is None: logger.error( "[BILLING] " "Domain '%s' is going to get charged on " "Community, but they haven't formally acknowledged this. " "Someone on ops should reconcile this soon. To be on the " "safe side, we've marked the invoices as Do Not Invoice." % self.domain.name ) do_not_invoice = True if not BillingContactInfo.objects.filter(account=account).exists(): # No contact information exists for this account. # This shouldn't happen, but if it does, we can't continue # with the invoice generation. raise InvoiceError("No Billing Contact Info could be found " "for domain '%s'." % self.domain.name) # First check to make sure none of the existing subscriptions is set # to do not invoice. Let's be on the safe side and not send a # community invoice out, if that's the case. for c in community_ranges: # create a new community subscription for each # date range that the domain did not have a subscription community_subscription = Subscription( account=account, plan_version=plan_version, subscriber=self.subscriber, date_start=c[0], date_end=c[1], do_not_invoice=do_not_invoice, ) community_subscription.save() subscriptions.append(community_subscription)
def ensure_full_coverage(self, subscriptions): plan_version = DefaultProductPlan.get_default_plan_by_domain( self.domain, edition=SoftwarePlanEdition.COMMUNITY).plan.get_version() if not plan_version.feature_charges_exist_for_domain(self.domain): return community_ranges = self.get_community_ranges(subscriptions) if not community_ranges: return do_not_invoice = any([s.do_not_invoice for s in subscriptions]) account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, created_by_invoicing=True, entry_point=EntryPoint.SELF_STARTED, )[0] if account.date_confirmed_extra_charges is None: logger.info( "Did not generate invoice because date_confirmed_extra_charges " "was null for domain %s" % self.domain.name) do_not_invoice = True if not BillingContactInfo.objects.filter(account=account).exists(): # No contact information exists for this account. # This shouldn't happen, but if it does, we can't continue # with the invoice generation. raise BillingContactInfoError( "Project %s has incurred charges, but does not have their " "Billing Contact Info filled out." % self.domain.name) # First check to make sure none of the existing subscriptions is set # to do not invoice. Let's be on the safe side and not send a # community invoice out, if that's the case. for c in community_ranges: # create a new community subscription for each # date range that the domain did not have a subscription community_subscription = Subscription( account=account, plan_version=plan_version, subscriber=self.subscriber, date_start=c[0], date_end=c[1], do_not_invoice=do_not_invoice, ) community_subscription.save() subscriptions.append(community_subscription)
def _ensure_full_coverage(self, subscriptions): plan_version = DefaultProductPlan.get_default_plan_by_domain( self.domain, edition=SoftwarePlanEdition.COMMUNITY ).plan.get_version() if not plan_version.feature_charges_exist_for_domain(self.domain): return community_ranges = self._get_community_ranges(subscriptions) if not community_ranges: return # First check to make sure none of the existing subscriptions is set # to do not invoice. Let's be on the safe side and not send a # community invoice out, if that's the case. do_not_invoice = any([s.do_not_invoice for s in subscriptions]) account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, entry_point=EntryPoint.SELF_STARTED, )[0] if account.date_confirmed_extra_charges is None: log_accounting_info( "Did not generate invoice because date_confirmed_extra_charges " "was null for domain %s" % self.domain.name ) do_not_invoice = True for start_date, end_date in community_ranges: # create a new community subscription for each # date range that the domain did not have a subscription community_subscription = Subscription( account=account, plan_version=plan_version, subscriber=self.subscriber, date_start=start_date, date_end=end_date, do_not_invoice=do_not_invoice, ) community_subscription.save() subscriptions.append(community_subscription)
def _ensure_full_coverage(self, subscriptions): plan_version = DefaultProductPlan.get_default_plan( edition=SoftwarePlanEdition.COMMUNITY).plan.get_version() if not plan_version.feature_charges_exist_for_domain(self.domain): return community_ranges = self._get_community_ranges(subscriptions) if not community_ranges: return # First check to make sure none of the existing subscriptions is set # to do not invoice. Let's be on the safe side and not send a # community invoice out, if that's the case. do_not_invoice = any([s.do_not_invoice for s in subscriptions]) account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, entry_point=EntryPoint.SELF_STARTED, )[0] if account.date_confirmed_extra_charges is None: log_accounting_info( "Did not generate invoice because date_confirmed_extra_charges " "was null for domain %s" % self.domain.name) do_not_invoice = True for start_date, end_date in community_ranges: # create a new community subscription for each # date range that the domain did not have a subscription community_subscription = Subscription( account=account, plan_version=plan_version, subscriber=self.subscriber, date_start=start_date, date_end=end_date, do_not_invoice=do_not_invoice, ) community_subscription.save() subscriptions.append(community_subscription)
def setUpClass(cls): super().setUpClass() cls.domain1, subscriber1 = generator.arbitrary_domain_and_subscriber() cls.domain2, subscriber2 = generator.arbitrary_domain_and_subscriber() cls.admin_web_user = generator.create_arbitrary_web_user_name() account = generator.billing_account(cls.admin_web_user, cls.admin_web_user) account.is_customer_billing_account = True account.save() enterprise_plan = SoftwarePlan.objects.create( name="Helping Earth INGO Enterprise Plan", description="Enterprise plan for Helping Earth", edition=SoftwarePlanEdition.ENTERPRISE, visibility=SoftwarePlanVisibility.INTERNAL, is_customer_software_plan=True, ) first_product_rate = SoftwareProductRate.objects.create( monthly_fee=3000, name="HQ Enterprise") cls.first_version = SoftwarePlanVersion.objects.create( plan=enterprise_plan, role=Role.objects.first(), product_rate=first_product_rate) cls.first_version.save() today = datetime.date.today() two_months_ago = today - datetime.timedelta(days=60) next_month = today + datetime.timedelta(days=30) subscription1 = Subscription( account=account, plan_version=cls.first_version, subscriber=subscriber1, date_start=two_months_ago, date_end=None, service_type=SubscriptionType.IMPLEMENTATION, ) subscription1.is_active = True subscription1.save() subscription2 = Subscription( account=account, plan_version=cls.first_version, subscriber=subscriber2, date_start=two_months_ago, date_end=next_month, service_type=SubscriptionType.IMPLEMENTATION, ) subscription2.is_active = True subscription2.save() new_product_rate = SoftwareProductRate.objects.create( monthly_fee=5000, name="HQ Enterprise") cls.newest_version = SoftwarePlanVersion.objects.create( plan=enterprise_plan, role=Role.objects.first(), product_rate=new_product_rate) cls.newest_version.save()