def setUp(self): super(TestNewDomainSubscription, self).setUp() self.domain = Domain( name="test-domain-sub", is_active=True, ) self.domain.save() self.domain2 = Domain( name="test-domain-sub2", is_active=True, ) self.domain2.save() self.admin_user = generator.arbitrary_web_user() self.admin_user.add_domain_membership(self.domain.name, is_admin=True) self.admin_user.save() self.account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.admin_user.username)[0] self.account2 = BillingAccount.get_or_create_account_by_domain( self.domain2.name, created_by=self.admin_user.username)[0] self.standard_plan = DefaultProductPlan.get_default_plan( edition=SoftwarePlanEdition.STANDARD) self.advanced_plan = DefaultProductPlan.get_default_plan( edition=SoftwarePlanEdition.ADVANCED)
def setUp(self): super(TestSubscriptionForm, self).setUp() self.domain = Domain( name="test-sub-form", is_active=True ) self.domain.save() self.domain2 = Domain( name="test-sub-form-2", is_active=True ) self.domain2.save() self.web_user = WebUser.create( self.domain.name, generator.create_arbitrary_web_user_name(), 'testpwd' ) self.account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.web_user.username )[0] self.account.save() self.customer_account = BillingAccount.get_or_create_account_by_domain( self.domain2.name, created_by=self.web_user.username )[0] self.customer_account.is_customer_billing_account = True self.customer_account.save() self.plan = DefaultProductPlan.get_default_plan_version(edition=SoftwarePlanEdition.ADVANCED) self.customer_plan = DefaultProductPlan.get_default_plan_version(edition=SoftwarePlanEdition.ADVANCED) self.customer_plan.plan.is_customer_software_plan = True
def setUp(self): super(TestNewDomainSubscription, self).setUp() self.domain = Domain( name="test-domain-sub", is_active=True, ) self.domain.save() self.domain2 = Domain( name="test-domain-sub2", is_active=True, ) self.domain2.save() self.admin_user = generator.arbitrary_web_user() self.admin_user.add_domain_membership(self.domain.name, is_admin=True) self.admin_user.save() self.account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.admin_user.username)[0] self.account2 = BillingAccount.get_or_create_account_by_domain( self.domain2.name, created_by=self.admin_user.username)[0] self.standard_plan = DefaultProductPlan.get_default_plan_by_domain( self.domain.name, edition=SoftwarePlanEdition.STANDARD) self.advanced_plan = DefaultProductPlan.get_default_plan_by_domain( self.domain.name, edition=SoftwarePlanEdition.ADVANCED)
def create_wire_invoice(self, balance): # Gather relevant invoices invoices = Invoice.objects.filter( subscription__subscriber__domain=self.domain, is_hidden=False, date_paid__exact=None, ).order_by('-date_start') BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, entry_point=EntryPoint.SELF_STARTED, )[0] # If no start date supplied, default earliest start date of unpaid invoices if self.date_start: date_start = self.date_start else: date_start = invoices.aggregate( Min('date_start'))['date_start__min'] # If no end date supplied, default latest end date of unpaid invoices if self.date_end: date_end = self.date_end else: date_end = invoices.aggregate(Max('date_end'))['date_end__max'] if not date_end: date_end = datetime.datetime.today() date_due = date_end + datetime.timedelta(DEFAULT_DAYS_UNTIL_DUE) wire_invoice = WireInvoice.objects.create( domain=self.domain.name, date_start=date_start, date_end=date_end, date_due=date_due, balance=balance, ) record = WireBillingRecord.generate_record(wire_invoice) if record.should_send_email: try: for email in self.contact_emails: record.send_email(contact_email=email) except InvoiceEmailThrottledError as e: # Currently wire invoices are never throttled if not self.logged_throttle_error: log_accounting_error(e.message) self.logged_throttle_error = True else: record.skipped_email = True record.save() return wire_invoice
def billing_account(web_user_creator, web_user_contact, currency=None, save=True): account_name = data_gen.arbitrary_unique_name(prefix="BA")[:40] currency = currency or Currency.objects.get(code=settings.DEFAULT_CURRENCY) billing_account = BillingAccount(name=account_name, created_by=web_user_creator.username, currency=currency) if save: billing_account.save() billing_contact = arbitrary_contact_info(billing_account, web_user_contact) billing_contact.save() return billing_account
def create_account(self): name = self.cleaned_data['name'] salesforce_account_id = self.cleaned_data['salesforce_account_id'] currency, _ = Currency.objects.get_or_create(code=self.cleaned_data['currency']) account = BillingAccount(name=name, salesforce_account_id=salesforce_account_id, currency=currency) account.save() return account
def create_account(self): name = self.cleaned_data["name"] salesforce_account_id = self.cleaned_data["salesforce_account_id"] currency, _ = Currency.objects.get_or_create(code=self.cleaned_data["currency"]) account = BillingAccount(name=name, salesforce_account_id=salesforce_account_id, currency=currency) account.save() contact_info, _ = BillingContactInfo.objects.get_or_create(account=account) contact_info.emails = self.cleaned_data["contact_emails"] contact_info.save() return account
def account(self): account = BillingAccount.get_or_create_account_by_domain( self.domain, created_by=self.couch_user.username, account_type=BillingAccountType.USER_CREATED, )[0] return account
def handle(self, *args, **options): if len(args) != 1: print "Invalid arguments: %s" % str(args) return domain = Domain.get_by_name(args[0]) if not domain: print "Invalid domain name: %s" % args[0] return account, _ = BillingAccount.get_or_create_account_by_domain( domain.name, account_type=BillingAccountType.CONTRACT, created_by="management command", ) enterprise_plan_version = SoftwarePlanVersion.objects.filter( plan__edition=SoftwarePlanEdition.ENTERPRISE )[0] try: subscription = Subscription.new_domain_subscription( account, domain.name, enterprise_plan_version ) except NewSubscriptionError as e: print e.message return subscription.is_active = True subscription.save() print 'Domain %s has been upgraded to enterprise level.' % domain.name
def assign_explicit_community_subscription(domain_name, start_date): future_subscriptions = Subscription.objects.filter( CONSISTENT_DATES_CHECK ).filter( date_start__gt=start_date, subscriber__domain=domain_name, ) if future_subscriptions.exists(): end_date = future_subscriptions.latest('date_start').date_start else: end_date = None return Subscription.new_domain_subscription( account=BillingAccount.get_or_create_account_by_domain( domain_name, created_by='assign_explicit_community_subscriptions', entry_point=EntryPoint.SELF_STARTED, )[0], domain=domain_name, plan_version=DefaultProductPlan.get_default_plan_version(), date_start=start_date, date_end=end_date, skip_invoicing_if_no_feature_charges=True, adjustment_method=SubscriptionAdjustmentMethod.TASK, internal_change=True, service_type=SubscriptionType.PRODUCT, )
def create_wire_credits_invoice(domain_name, account_created_by, account_entry_point, amount, invoice_items, contact_emails): account = BillingAccount.get_or_create_account_by_domain( domain_name, created_by=account_created_by, entry_point=account_entry_point )[0] wire_invoice = WirePrepaymentInvoice.objects.create( domain=domain_name, date_start=datetime.datetime.utcnow(), date_end=datetime.datetime.utcnow(), date_due=None, balance=amount, account=account, ) wire_invoice.items = invoice_items record = WirePrepaymentBillingRecord.generate_record(wire_invoice) if record.should_send_email: try: record.send_email(contact_emails=contact_emails) except Exception as e: log_accounting_error( "Error sending email for WirePrepaymentBillingRecord %d: %s" % (record.id, e.message), show_stack_trace=True, ) else: record.skipped_email = True record.save()
def email_enterprise_report(domain, slug, couch_user): account = BillingAccount.get_account_by_domain(domain) report = EnterpriseReport.create(slug, account.id, couch_user) # Generate file csv_file = io.StringIO() writer = csv.writer(csv_file) writer.writerow(report.headers) writer.writerows(report.rows) # Store file in redis hash_id = uuid.uuid4().hex redis = get_redis_client() redis.set(hash_id, csv_file.getvalue()) redis.expire(hash_id, 60 * 60 * 24) csv_file.close() # Send email url = absolute_reverse("enterprise_dashboard_download", args=[domain, report.slug, str(hash_id)]) link = "<a href='{}'>{}</a>".format(url, url) subject = _("Enterprise Dashboard: {}").format(report.title) body = "The enterprise report you requested for the account {} is ready.<br>" \ "You can download the data at the following link: {}<br><br>" \ "Please remember that this link will only be active for 24 hours.".format(account.name, link) send_html_email_async(subject, couch_user.username, body)
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 create_wire_credits_invoice(domain_name, account_created_by, account_entry_point, amount, invoice_items, contact_emails): account = BillingAccount.get_or_create_account_by_domain( domain_name, created_by=account_created_by, created_by_invoicing=True, entry_point=account_entry_point )[0] wire_invoice = WirePrepaymentInvoice.objects.create( domain=domain_name, date_start=datetime.datetime.utcnow(), date_end=datetime.datetime.utcnow(), date_due=None, balance=amount, account=account, ) wire_invoice.items = invoice_items record = WirePrepaymentBillingRecord.generate_record(wire_invoice) try: record.send_email(contact_emails=contact_emails) except Exception as e: logger.error("[BILLING] %s" % e)
def migrate_mirrors(apps, schema_editor): from corehq.apps.enterprise.models import EnterprisePermissions DomainPermissionsMirror = apps.get_model('users', 'DomainPermissionsMirror') sources = {o.source for o in DomainPermissionsMirror.objects.all()} for source in sources: account = BillingAccount.get_account_by_domain(source) account_domains = set(account.get_domains()) mirror_domains = { o.mirror for o in DomainPermissionsMirror.objects.filter(source=source) } if EnterprisePermissions.objects.filter(account=account).exists(): print(f""" Found a pre-existing enterprise permissions configuration for account {account.id}. Enterprise permissions no longer supports multiple configurations in the same account. Please delete one of the DomainPermissionsMirror source domains in this account. """) sys.exit(1) EnterprisePermissions( account=account, is_enabled=True, source_domain=source, domains=list(account_domains & mirror_domains - {source}), ).save()
def update_credits(self, payment_record): amount = payment_record.amount for invoice in self.invoices: deduct_amount = min(amount, invoice.balance) amount -= deduct_amount if deduct_amount > 0: if self.account and self.account.is_customer_billing_account: customer_invoice = invoice subscription_invoice = None account = self.account else: customer_invoice = None subscription_invoice = invoice account = invoice.subscription.account # TODO - refactor duplicated functionality CreditLine.add_credit( deduct_amount, account=account, payment_record=payment_record, ) CreditLine.add_credit( -deduct_amount, account=account, invoice=subscription_invoice, customer_invoice=customer_invoice ) invoice.update_balance() invoice.save() if amount: account = BillingAccount.get_or_create_account_by_domain(self.domain)[0] CreditLine.add_credit( amount, account=account, payment_record=payment_record, )
def setUp(self): super(TestCreditTransfers, self).setUp() self.product_credit_amt = Decimal("500.00") self.feature_credit_amt = Decimal("200.00") self.subscription_credit_amt = Decimal("600.00") self.domain = generator.arbitrary_domain() self.account = BillingAccount.get_or_create_account_by_domain(self.domain, created_by="*****@*****.**")[0]
def _deactivate_subscription(subscription): subscription.is_active = False subscription.save() next_subscription = subscription.next_subscription activate_next_subscription = next_subscription and next_subscription.date_start == subscription.date_end if activate_next_subscription: new_plan_version = next_subscription.plan_version next_subscription.is_active = True next_subscription.save() else: domain = subscription.subscriber.domain if not subscription.account.is_customer_billing_account: account = subscription.account else: account = BillingAccount.create_account_for_domain( domain, created_by='default_community_after_customer_level' ) next_subscription = assign_explicit_community_subscription( domain, subscription.date_end, SubscriptionAdjustmentMethod.DEFAULT_COMMUNITY, account=account ) new_plan_version = next_subscription.plan_version _, downgraded_privs, upgraded_privs = get_change_status(subscription.plan_version, new_plan_version) if subscription.account == next_subscription.account: subscription.transfer_credits(subscription=next_subscription) else: subscription.transfer_credits() subscription.subscriber.deactivate_subscription( downgraded_privileges=downgraded_privs, upgraded_privileges=upgraded_privs, old_subscription=subscription, new_subscription=next_subscription if activate_next_subscription else None, )
def prepare_domain(domain_name): from corehq.apps.commtrack.tests import bootstrap_domain domain = bootstrap_domain(domain_name) previous = None for name, administrative in [("MOHSW", True), ("MSDZONE", True), ("REGION", True), ("DISTRICT", True), ("FACILITY", False)]: previous, _ = LocationType.objects.get_or_create( domain=domain_name, name=name, parent_type=previous, administrative=administrative, ) generator.instantiate_accounting_for_tests() account = BillingAccount.get_or_create_account_by_domain( domain.name, created_by="automated-test", )[0] plan = DefaultProductPlan.get_default_plan_by_domain( domain, edition=SoftwarePlanEdition.ADVANCED) commtrack = domain.commtrack_settings commtrack.actions.append( CommtrackActionConfig(action='receipts', keyword='delivered', caption='Delivered')) commtrack.save() subscription = Subscription.new_domain_subscription( account, domain.name, plan) subscription.is_active = True subscription.save() ils_config = ILSGatewayConfig(enabled=True, domain=domain.name) ils_config.save() return domain
def account(self): """ First try to grab the account used for the last subscription. If an account is not found, create it. """ account, _ = BillingAccount.get_or_create_account_by_domain(self.domain.name, self.__class__.__name__) return account
def sms_billables(self): datespan = DateSpan(DateSentFilter.get_start_date(self.request), DateSentFilter.get_end_date(self.request)) selected_billables = SmsBillable.get_billables_sent_between(datespan) if DateCreatedFilter.use_filter(self.request): date_span = DateSpan( DateCreatedFilter.get_start_date(self.request), DateCreatedFilter.get_end_date(self.request)) selected_billables = SmsBillable.filter_selected_billables_by_date( selected_billables, date_span) domain = EnterpriseDomainFilter.get_value(self.request, self.domain) if domain: selected_billables = selected_billables.filter(domain=domain, ) else: account = BillingAccount.get_account_by_domain(self.request.domain) domains = Subscription.get_active_domains_for_account(account) selected_billables = selected_billables.filter(domain__in=domains) has_gateway_fee = HasGatewayFeeFilter.get_value( self.request, self.domain) if has_gateway_fee: if has_gateway_fee == HasGatewayFeeFilter.YES: selected_billables = selected_billables.exclude( gateway_fee=None) else: selected_billables = selected_billables.filter( gateway_fee=None) gateway_type = GatewayTypeFilter.get_value(self.request, self.domain) if gateway_type: selected_billables = selected_billables.filter( gateway_fee__criteria__backend_api_id=gateway_type, ) return selected_billables
def _get_account_name(domain): from corehq.apps.accounting.models import BillingAccount account = BillingAccount.get_account_by_domain(domain) if account is not None: return f'account:{account.name}' else: return f'no_account:{domain}'
def assign_explicit_community_subscription(domain_name, start_date, method, account=None, web_user=None): future_subscriptions = Subscription.visible_objects.filter( date_start__gt=start_date, subscriber__domain=domain_name, ) if future_subscriptions.exists(): end_date = future_subscriptions.earliest('date_start').date_start else: end_date = None if account is None: account = BillingAccount.get_or_create_account_by_domain( domain_name, created_by='assign_explicit_community_subscriptions', entry_point=EntryPoint.SELF_STARTED, )[0] return Subscription.new_domain_subscription( account=account, domain=domain_name, plan_version=DefaultProductPlan.get_default_plan_version(), date_start=start_date, date_end=end_date, skip_invoicing_if_no_feature_charges=True, adjustment_method=method, internal_change=True, service_type=SubscriptionType.PRODUCT, web_user=web_user, )
def check_username_availability(self, data): email = data['email'].strip() duplicate = CouchUser.get_by_username(email) is_existing = User.objects.filter(username__iexact=email).count() > 0 or duplicate message = None restricted_by_domain = None if is_existing: message = _("There is already a user with this email.") else: domain = email[email.find("@") + 1:] for account in BillingAccount.get_enterprise_restricted_signup_accounts(): if domain in account.enterprise_restricted_signup_domains: restricted_by_domain = domain message = account.restrict_signup_message message += _(""" <br>Please contact <a href='mailto:{}?subject={}'>{}</a> to register for an account. """).format(account.restrict_signup_email, _("CommCareHQ account request"), account.restrict_signup_email) break return { 'isValid': message is None, 'restrictedByDomain': restricted_by_domain, 'message': message, }
def setUp(self): super(OptTestCase, self).setUp() self.domain = "opt-test" self.domain_obj = Domain(name=self.domain) self.domain_obj.save() generator.instantiate_accounting_for_tests() self.account = BillingAccount.get_or_create_account_by_domain( self.domain_obj.name, created_by="automated-test", )[0] plan = DefaultProductPlan.get_default_plan_by_domain( self.domain_obj, edition=SoftwarePlanEdition.ADVANCED ) self.subscription = Subscription.new_domain_subscription( self.account, self.domain_obj.name, plan ) self.subscription.is_active = True self.subscription.save() self.backend = TestSMSBackend(is_global=True) self.backend.save() self.backend_mapping = BackendMapping( is_global=True, prefix="*", backend_id=self.backend._id, ) self.backend_mapping.save()
def setUp(self): super(OptTestCase, self).setUp() self.domain = "opt-test" self.domain_obj = Domain(name=self.domain) self.domain_obj.save() generator.instantiate_accounting_for_tests() self.account = BillingAccount.get_or_create_account_by_domain( self.domain_obj.name, created_by="automated-test", )[0] plan = DefaultProductPlan.get_default_plan_by_domain( self.domain_obj, edition=SoftwarePlanEdition.ADVANCED) self.subscription = Subscription.new_domain_subscription( self.account, self.domain_obj.name, plan) self.subscription.is_active = True self.subscription.save() self.backend = TestSMSBackend(is_global=True) self.backend.save() self.backend_mapping = BackendMapping( is_global=True, prefix="*", backend_id=self.backend._id, ) self.backend_mapping.save()
def _deactivate_subscription(subscription): subscription.is_active = False subscription.save() next_subscription = subscription.next_subscription activate_next_subscription = next_subscription and next_subscription.date_start == subscription.date_end if activate_next_subscription: new_plan_version = next_subscription.plan_version next_subscription.is_active = True next_subscription.save() else: domain = subscription.subscriber.domain if not subscription.account.is_customer_billing_account: account = subscription.account else: account = BillingAccount.create_account_for_domain( domain, created_by='default_community_after_customer_level') next_subscription = assign_explicit_community_subscription( domain, subscription.date_end, SubscriptionAdjustmentMethod.DEFAULT_COMMUNITY, account=account) new_plan_version = next_subscription.plan_version _, downgraded_privs, upgraded_privs = get_change_status( subscription.plan_version, new_plan_version) if subscription.account == next_subscription.account: subscription.transfer_credits(subscription=next_subscription) else: subscription.transfer_credits() subscription.subscriber.deactivate_subscription( downgraded_privileges=downgraded_privs, upgraded_privileges=upgraded_privs, old_subscription=subscription, new_subscription=next_subscription if activate_next_subscription else None, )
def test_community_over_limit(self): """ For a domain under community (no subscription) with users over the community limit, make sure that: - base_description is None - base_cost is 0.0 - unit_description is not None - unit_cost is equal to the per_excess_fee on the user rate - quantity is equal to number of commcare users in that domain minus the monthly_limit on the user rate - total and subtotals are equal to number of extra users * per_excess_fee """ domain = generator.arbitrary_domain() num_active = generator.create_excess_community_users(domain) account = BillingAccount.get_or_create_account_by_domain( domain, created_by=self.dimagi_user)[0] billing_contact = generator.arbitrary_contact_info(account, self.dimagi_user) billing_contact.save() account.date_confirmed_extra_charges = datetime.date.today() account.save() tasks.generate_invoices() subscriber = Subscriber.objects.get(domain=domain.name) invoice = Invoice.objects.filter(subscription__subscriber=subscriber).get() user_line_item = invoice.lineitem_set.get_feature_by_type(FeatureType.USER).get() self.assertIsNone(user_line_item.base_description) self.assertEqual(user_line_item.base_cost, Decimal('0.0000')) num_to_charge = num_active - self.community_plan.user_limit self.assertIsNotNone(user_line_item.unit_description) self.assertEqual(user_line_item.quantity, num_to_charge) self.assertEqual(user_line_item.unit_cost, self.user_rate.per_excess_fee) self.assertEqual(user_line_item.subtotal, num_to_charge * self.user_rate.per_excess_fee) self.assertEqual(user_line_item.total, num_to_charge * self.user_rate.per_excess_fee) domain.delete()
def setUp(self): super(BaseReminderTestCase, self).setUp() self.domain_obj = Domain(name="test") self.domain_obj.save() # Prevent resource conflict self.domain_obj = Domain.get(self.domain_obj._id) self.account, _ = BillingAccount.get_or_create_account_by_domain( self.domain_obj.name, created_by="tests" ) advanced_plan_version = DefaultProductPlan.get_default_plan_by_domain( self.domain_obj, edition=SoftwarePlanEdition.ADVANCED) self.subscription = Subscription.new_domain_subscription( self.account, self.domain_obj.name, advanced_plan_version ) self.subscription.is_active = True self.subscription.save() self.sms_backend = TestSMSBackend(named="MOBILE_BACKEND_TEST", is_global=True) self.sms_backend.save() self.sms_backend_mapping = BackendMapping(is_global=True,prefix="*",backend_id=self.sms_backend._id) self.sms_backend_mapping.save()
def setUp(self): super(TestRenewSubscriptions, self).setUp() self.domain = Domain( name="test-domain-sub", is_active=True, ) self.domain.save() self.admin_user = generator.arbitrary_web_user() self.admin_user.add_domain_membership(self.domain.name, is_admin=True) self.admin_user.save() self.account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.admin_user.username)[0] self.standard_plan = DefaultProductPlan.get_default_plan_version( edition=SoftwarePlanEdition.STANDARD) today = datetime.date.today() yesterday = today + datetime.timedelta(days=-1) tomorrow = today + datetime.timedelta(days=1) self.subscription = Subscription.new_domain_subscription( self.account, self.domain.name, self.standard_plan, web_user=self.admin_user.username, date_start=yesterday, date_end=tomorrow, ) self.subscription.save()
def setUp(self): super(TestUserRoleSubscriptionChanges, self).setUp() self.domain = generator.arbitrary_domain() UserRole.init_domain_with_presets(self.domain.name) self.user_roles = UserRole.by_domain(self.domain.name) self.custom_role = UserRole.get_or_create_with_permissions( self.domain.name, Permissions(edit_apps=True, edit_web_users=True), "Custom Role" ) self.custom_role.save() self.read_only_role = UserRole.get_read_only_role_by_domain(self.domain.name) self.admin_user = generator.arbitrary_web_user() self.admin_user.add_domain_membership(self.domain.name, is_admin=True) self.admin_user.save() self.web_users = [] self.commcare_users = [] for role in [self.custom_role] + self.user_roles: web_user = generator.arbitrary_web_user() web_user.add_domain_membership(self.domain.name, role_id=role.get_id) web_user.save() self.web_users.append(web_user) commcare_user = generator.arbitrary_commcare_user( domain=self.domain.name) commcare_user.set_role(self.domain.name, role.get_qualified_id()) commcare_user.save() self.commcare_users.append(commcare_user) self.account = BillingAccount.get_or_create_account_by_domain( self.domain.name,created_by=self.admin_user.username)[0] self.advanced_plan = DefaultProductPlan.get_default_plan_by_domain( self.domain.name,edition=SoftwarePlanEdition.ADVANCED)
def setUp(self): super(TestRenewSubscriptions, self).setUp() self.domain = Domain( name="test-domain-sub", is_active=True, ) self.domain.save() self.admin_user = generator.arbitrary_web_user() self.admin_user.add_domain_membership(self.domain.name, is_admin=True) self.admin_user.save() self.account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.admin_user.username)[0] self.standard_plan = DefaultProductPlan.get_default_plan_by_domain( self.domain.name, edition=SoftwarePlanEdition.STANDARD) today = datetime.date.today() yesterday = today + datetime.timedelta(days=-1) tomorrow = today + datetime.timedelta(days=1) self.subscription = Subscription.new_domain_subscription( self.account, self.domain.name, self.standard_plan, web_user=self.admin_user.username, date_start=yesterday, date_end=tomorrow, ) self.subscription.save()
def test_community_invoice(self): """ For an unsubscribed domain with any charges over the community limit for the month of invoicing, make sure that an invoice is generated in addition to a subscription for that month to the community plan. """ domain = generator.arbitrary_domain() generator.create_excess_community_users(domain) account = BillingAccount.get_or_create_account_by_domain( domain, created_by=self.dimagi_user)[0] billing_contact = generator.arbitrary_contact_info(account, self.dimagi_user) account.date_confirmed_extra_charges = datetime.date.today() account.save() tasks.generate_invoices() subscriber = Subscriber.objects.get(domain=domain.name) invoices = Invoice.objects.filter(subscription__subscriber=subscriber) self.assertEqual(invoices.count(), 1) invoice = invoices.get() self.assertEqual(invoice.subscription.subscriber.domain, domain.name) self.assertEqual(invoice.subscription.date_start, invoice.date_start) self.assertEqual( invoice.subscription.date_end - datetime.timedelta(days=1), invoice.date_end ) domain.delete()
def test_community_over_limit(self): """ For a domain under community (no subscription) with users over the community limit, make sure that: - base_description is None - base_cost is 0.0 - unit_description is not None - unit_cost is equal to the per_excess_fee on the user rate - quantity is equal to number of commcare users in that domain minus the monthly_limit on the user rate - total and subtotals are equal to number of extra users * per_excess_fee """ domain = generator.arbitrary_domain() num_active = generator.create_excess_community_users(domain) account = BillingAccount.get_or_create_account_by_domain( domain, created_by=self.dimagi_user)[0] billing_contact = generator.arbitrary_contact_info(account, self.dimagi_user) account.date_confirmed_extra_charges = datetime.date.today() account.save() tasks.generate_invoices() subscriber = Subscriber.objects.get(domain=domain.name) invoice = Invoice.objects.filter(subscription__subscriber=subscriber).get() user_line_item = invoice.lineitem_set.get_feature_by_type(FeatureType.USER).get() self.assertIsNone(user_line_item.base_description) self.assertEqual(user_line_item.base_cost, Decimal('0.0000')) community_plan = DefaultProductPlan.get_default_plan_version() num_to_charge = num_active - community_plan.user_limit self.assertIsNotNone(user_line_item.unit_description) self.assertEqual(user_line_item.quantity, num_to_charge) self.assertEqual(user_line_item.unit_cost, self.user_rate.per_excess_fee) self.assertEqual(user_line_item.subtotal, num_to_charge * self.user_rate.per_excess_fee) self.assertEqual(user_line_item.total, num_to_charge * self.user_rate.per_excess_fee) domain.delete()
def test_wrong_domain(self): ''' If correct credentials for a user in a different domain are submitted, the response is forbidden ''' wrong_domain = Domain.get_or_create_with_name('dvorak', is_active=True) self.addCleanup(wrong_domain.delete) # have to set up subscription for the bad domain or it will fail on authorization new_account = BillingAccount.get_or_create_account_by_domain( wrong_domain.name, created_by="automated-test")[0] plan = DefaultProductPlan.get_default_plan_version( edition=SoftwarePlanEdition.ADVANCED) new_subscription = Subscription.new_domain_subscription( new_account, wrong_domain.name, plan) new_subscription.is_active = True new_subscription.save() wrong_list_endpoint = reverse( 'api_dispatch_list', kwargs=dict(domain=wrong_domain.name, api_name=self.api_name, resource_name=self.resource.Meta.resource_name)) response = self.client.post(wrong_list_endpoint, { 'username': self.username, 'password': self.password }) self.assertEqual(response.status_code, 403)
def check_username_availability(self, data): email = data['email'].strip() duplicate = CouchUser.get_by_username(email) is_existing = User.objects.filter( username__iexact=email).count() > 0 or duplicate message = None restricted_by_domain = None if is_existing: message = _("There is already a user with this email.") else: domain = email[email.find("@") + 1:] for account in BillingAccount.get_enterprise_restricted_signup_accounts( ): if domain in account.enterprise_restricted_signup_domains: restricted_by_domain = domain message = account.restrict_signup_message regex = r'(\b[a-zA-Z0-9_.+%-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+\b)' subject = _("CommCareHQ account request") message = re.sub( regex, "<a href='mailto:\\1?subject={}'>\\1</a>".format( subject), message) break return { 'isValid': message is None, 'restrictedByDomain': restricted_by_domain, 'message': message, }
def _setup_accounting(cls): cls.account, _ = BillingAccount.get_or_create_account_by_domain( cls.domain.name, created_by='') plan_version = DefaultProductPlan.get_default_plan_version( SoftwarePlanEdition.STANDARD) cls.subscription = Subscription.new_domain_subscription( cls.account, cls.domain.name, plan_version)
def setUp(self): super(TestDeleteDomain, self).setUp() self.domain = Domain(name="test", is_active=True) self.domain.save() self.domain.convert_to_commtrack() self.current_subscription = Subscription.new_domain_subscription( BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by='tests')[0], self.domain.name, DefaultProductPlan.get_default_plan_version( SoftwarePlanEdition.ADVANCED), date_start=date.today() - relativedelta(days=1), ) self.domain2 = Domain(name="test2", is_active=True) self.domain2.save() self.domain2.convert_to_commtrack() LocationType.objects.create( domain='test', name='facility', ) LocationType.objects.create( domain='test2', name='facility', ) LocationType.objects.create( domain='test', name='facility2', ) LocationType.objects.create( domain='test2', name='facility2', )
def create_domain(self, domain): domain_obj = Domain(name=domain) domain_obj.use_default_sms_response = True domain_obj.default_sms_response = "Default SMS Response" domain_obj.save() # I tried making this class inherit from BaseSMSTest, but somehow # the multiple inheritance was causing the postgres connection to # get in a weird state where it wasn't commiting any changes. So # for now, keeping this subscription setup code as is. generator.instantiate_accounting_for_tests() self.account = BillingAccount.get_or_create_account_by_domain( domain_obj.name, created_by="automated-test", )[0] plan = DefaultProductPlan.get_default_plan_by_domain( domain_obj, edition=SoftwarePlanEdition.ADVANCED ) self.subscription = Subscription.new_domain_subscription( self.account, domain_obj.name, plan ) self.subscription.is_active = True self.subscription.save() return domain_obj
def test_community_invoice(self): """ For an unsubscribed domain with any charges over the community limit for the month of invoicing, make sure that an invoice is generated in addition to a subscription for that month to the community plan. """ domain = generator.arbitrary_domain() generator.create_excess_community_users(domain) account = BillingAccount.get_or_create_account_by_domain( domain, created_by=self.dimagi_user)[0] billing_contact = generator.arbitrary_contact_info(account, self.dimagi_user) billing_contact.save() account.date_confirmed_extra_charges = datetime.date.today() account.save() tasks.generate_invoices() subscriber = Subscriber.objects.get(domain=domain.name) invoices = Invoice.objects.filter(subscription__subscriber=subscriber) self.assertEqual(invoices.count(), 1) invoice = invoices.get() self.assertEqual(invoice.subscription.subscriber.domain, domain.name) self.assertEqual(invoice.subscription.date_start, invoice.date_start) self.assertEqual( invoice.subscription.date_end - datetime.timedelta(days=1), invoice.date_end ) domain.delete()
def update_credits(self, payment_record): amount = payment_record.amount for invoice in self.invoices: deduct_amount = min(amount, invoice.balance) amount -= deduct_amount if deduct_amount > 0: # TODO - refactor duplicated functionality CreditLine.add_credit( deduct_amount, account=invoice.subscription.account, payment_record=payment_record, ) CreditLine.add_credit( -deduct_amount, account=invoice.subscription.account, invoice=invoice, ) invoice.update_balance() invoice.save() if amount: account = BillingAccount.get_or_create_account_by_domain( self.domain) CreditLine.add_credit( amount, account=account, payment_record=payment_record, )
def setUpClass(cls): super(TestZapierIntegration, cls).setUpClass() generator.instantiate_accounting() cls.domain_object = Domain.get_or_create_with_name(TEST_DOMAIN, is_active=True) cls.domain = cls.domain_object.name account = BillingAccount.get_or_create_account_by_domain( cls.domain, created_by="automated-test")[0] plan = DefaultProductPlan.get_default_plan_version( edition=SoftwarePlanEdition.STANDARD) subscription = Subscription.new_domain_subscription( account, cls.domain, plan) subscription.is_active = True subscription.save() cls.web_user = WebUser.create(cls.domain, 'test', '******') api_key_object, _ = ApiKey.objects.get_or_create( user=cls.web_user.get_django_user()) cls.api_key = api_key_object.key cls.application = Application.new_app(cls.domain, 'Test App') cls.application.save() module = cls.application.add_module(Module.new_module( "Module 1", "en")) cls.application.new_form(module.id, name="Form1", attachment=XFORM, lang="en") cls.application.save()
def update_credits(self, payment_record): amount = payment_record.amount for invoice in self.invoices: deduct_amount = min(amount, invoice.balance) amount -= deduct_amount if deduct_amount > 0: if self.account and self.account.is_customer_billing_account: customer_invoice = invoice subscription_invoice = None account = self.account else: customer_invoice = None subscription_invoice = invoice account = invoice.subscription.account # TODO - refactor duplicated functionality CreditLine.add_credit( deduct_amount, account=account, payment_record=payment_record, ) CreditLine.add_credit(-deduct_amount, account=account, invoice=subscription_invoice, customer_invoice=customer_invoice) invoice.update_balance() invoice.save() if amount: account = BillingAccount.get_or_create_account_by_domain( self.domain)[0] CreditLine.add_credit( amount, account=account, payment_record=payment_record, )
def setUpClass(cls): super(APIResourceTest, cls).setUpClass() Role.get_cache().clear() cls.domain = Domain.get_or_create_with_name('qwerty', is_active=True) cls.list_endpoint = cls._get_list_endpoint() cls.username = '******' cls.password = '******' cls.user = WebUser.create(cls.domain.name, cls.username, cls.password, None, None, email='*****@*****.**', first_name='rudolph', last_name='commcare') cls.user.set_role(cls.domain.name, 'admin') cls.user.save() cls.account = BillingAccount.get_or_create_account_by_domain( cls.domain.name, created_by="automated-test")[0] plan = DefaultProductPlan.get_default_plan_version( edition=SoftwarePlanEdition.ADVANCED) cls.subscription = Subscription.new_domain_subscription( cls.account, cls.domain.name, plan) cls.subscription.is_active = True cls.subscription.save() cls.api_key, _ = HQApiKey.objects.get_or_create( user=WebUser.get_django_user(cls.user))
def create_wire_credits_invoice(domain_name, account_created_by, account_entry_point, amount, invoice_items, contact_emails): account = BillingAccount.get_or_create_account_by_domain( domain_name, created_by=account_created_by, entry_point=account_entry_point)[0] wire_invoice = WirePrepaymentInvoice.objects.create( domain=domain_name, date_start=datetime.datetime.utcnow(), date_end=datetime.datetime.utcnow(), date_due=None, balance=amount, account=account, ) wire_invoice.items = invoice_items record = WirePrepaymentBillingRecord.generate_record(wire_invoice) if record.should_send_email: try: record.send_email(contact_emails=contact_emails) except Exception as e: log_accounting_error( "Error sending email for WirePrepaymentBillingRecord %d: %s" % (record.id, e.message), show_stack_trace=True, ) else: record.skipped_email = True record.save()
def assign_explicit_community_subscription(domain_name, start_date): future_subscriptions = Subscription.objects.filter( CONSISTENT_DATES_CHECK).filter( date_start__gt=start_date, subscriber__domain=domain_name, ) if future_subscriptions.exists(): end_date = future_subscriptions.latest('date_start').date_start else: end_date = None return Subscription.new_domain_subscription( account=BillingAccount.get_or_create_account_by_domain( domain_name, created_by='assign_explicit_community_subscriptions', entry_point=EntryPoint.SELF_STARTED, )[0], domain=domain_name, plan_version=DefaultProductPlan.get_default_plan_version(), date_start=start_date, date_end=end_date, skip_invoicing_if_no_feature_charges=True, adjustment_method=SubscriptionAdjustmentMethod.TASK, internal_change=True, service_type=SubscriptionType.PRODUCT, )
def setUp(self): super(TestDeleteDomain, self).setUp() self.domain = Domain(name="test", is_active=True) self.domain.save() self.domain.convert_to_commtrack() self.current_subscription = Subscription.new_domain_subscription( BillingAccount.get_or_create_account_by_domain(self.domain.name, created_by='tests')[0], self.domain.name, DefaultProductPlan.get_default_plan_version(SoftwarePlanEdition.ADVANCED), date_start=date.today() - relativedelta(days=1), ) self.domain2 = Domain(name="test2", is_active=True) self.domain2.save() self.domain2.convert_to_commtrack() LocationType.objects.create( domain='test', name='facility', ) LocationType.objects.create( domain='test2', name='facility', ) LocationType.objects.create( domain='test', name='facility2', ) LocationType.objects.create( domain='test2', name='facility2', )
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 _setup_accounting(cls): call_command('cchq_prbac_bootstrap') cls.account, _ = BillingAccount.get_or_create_account_by_domain( cls.domain.name, created_by='') plan_version = DefaultProductPlan.get_default_plan_version( SoftwarePlanEdition.ADVANCED) cls.subscription = Subscription.new_domain_subscription( cls.account, cls.domain.name, plan_version)
def setUp(self): super(TestCreditStripePaymentHandler, self).setUp() self.domain = Domain(name='test-domain') self.domain.save() self.payment_method = PaymentMethod() self.payment_method.save() self.account, _ = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by='*****@*****.**')
def account(self): account = BillingAccount.get_or_create_account_by_domain( self.domain, created_by=self.couch_user.username, account_type=BillingAccountType.USER_CREATED, entry_point=EntryPoint.SELF_STARTED, )[0] return account
def setUp(self): self.domain = Domain(name='test-domain') self.domain.save() self.payment_method = PaymentMethod() self.payment_method.save() self.account, _ = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by='*****@*****.**' )
def create_wire_invoice(self, balance): # Gather relevant invoices if self.account and self.account.is_customer_billing_account: invoices = CustomerInvoice.objects.filter(account=self.account) else: invoices = Invoice.objects.filter( subscription__subscriber__domain=self.domain.name, is_hidden=False, date_paid__exact=None ).order_by('-date_start') self.account = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by=self.__class__.__name__, entry_point=EntryPoint.SELF_STARTED )[0] # If no start date supplied, default earliest start date of unpaid invoices if self.date_start: date_start = self.date_start else: date_start = invoices.aggregate(Min('date_start'))['date_start__min'] # If no end date supplied, default latest end date of unpaid invoices if self.date_end: date_end = self.date_end else: date_end = invoices.aggregate(Max('date_end'))['date_end__max'] if not date_end: date_end = datetime.datetime.today() date_due = date_end + datetime.timedelta(DEFAULT_DAYS_UNTIL_DUE) wire_invoice = WireInvoice.objects.create( domain=self.domain.name, date_start=date_start, date_end=date_end, date_due=date_due, balance=balance, ) record = WireBillingRecord.generate_record(wire_invoice) if record.should_send_email: try: for email in self.contact_emails: record.send_email(contact_email=email) except InvoiceEmailThrottledError as e: # Currently wire invoices are never throttled if not self.logged_throttle_error: log_accounting_error(six.text_type(e)) self.logged_throttle_error = True else: record.skipped_email = True record.save() return wire_invoice
def setUpClass(cls): super(TestCreditStripePaymentHandler, cls).setUpClass() cls.domain = Domain(name='test-domain') cls.domain.save() cls.payment_method = PaymentMethod() cls.payment_method.save() cls.account, _ = BillingAccount.get_or_create_account_by_domain( cls.domain.name, created_by='*****@*****.**' )
def account(self): """ First try to grab the account used for the last subscription. If an account is not found, create it. """ account, _ = BillingAccount.get_or_create_account_by_domain(self.domain.name, self.__class__.__name__) # todo: fix so that contact_emails gets correctly populated. BillingContactInfo.objects.get_or_create(account=account) return account
def setUp(self): super(TestCreditStripePaymentHandler, self).setUp() self.domain = Domain(name='test-domain') self.domain.save() self.payment_method = PaymentMethod() self.payment_method.save() self.account, _ = BillingAccount.get_or_create_account_by_domain( self.domain.name, created_by='*****@*****.**' )