def product_set_checkout(request, *args, **kwargs): product_id = request.POST['product_id'] product = Product.objects.get(pk=product_id) member = request.user now = datetime.now() expiry = now + timedelta(days=product.duration) subscription = Subscription.objects.create(member=member, product=product, since=now, expiry=expiry) number = get_next_invoice_number() item = InvoiceItem(label=product.name) entry = InvoiceEntry(item=item, short_description=product.short_description, total=product.cost) months_count = product.duration / 30 invoice = Invoice.objects.create(subscription=subscription, amount=product.cost, number=number, due_date=now, last_reminder=now, is_one_off=product.is_one_off, entries=[entry], months_count=months_count) amount = invoice.amount notification_url = reverse('billing:product_do_checkout', args=(invoice.id, )) cancel_url = request.META['HTTP_REFERER'] return_url = reverse('billing:invoice_detail', args=(invoice.id, )) return invoice, amount, notification_url, return_url, cancel_url
def save_model(self, request, obj, form, change): # Send e-mail for manually generated Invoice upon creation if change: super(InvoiceAdmin, self).save_model(request, obj, form, change) return obj.number = get_next_invoice_number(auto=False) super(InvoiceAdmin, self).save_model(request, obj, form, change) member = obj.subscription.member service = get_service_instance() config = service.config subject, message, sms_text = get_invoice_generated_message(obj) if member.email: add_event(service, NEW_INVOICE_EVENT, member=member, object_id=obj.id) invoice_url = service.url + reverse('billing:invoice_detail', args=(obj.id,)) html_content = get_mail_content(subject, message, template_name='billing/mails/notice.html', extra_context={'invoice_url': invoice_url}) # Sender is simulated as being no-reply@company_name_slug.com to avoid the mail # to be delivered to Spams because of origin check. sender = '%s <no-reply@%s>' % (service.project_name, service.domain) msg = EmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" if msg.send(fail_silently=True): obj.reminders_sent = 1 obj.last_reminder = timezone.now() if sms_text: if member.phone: if config.sms_sending_method == Config.HTTP_API: send_sms(member.phone, sms_text) else: QueuedSMS.objects.create(recipient=member.phone, text=sms_text)
def place_invoice(self, request, *args, **kwargs): school_name = kwargs['school_name'] weblet = Service.objects.get(project_name_slug=school_name) try: db = weblet.database add_database(db) school = SchoolConfig.objects.using(db).get(service=weblet) now = datetime.now() due_date = now + timedelta(days=7) number = get_next_invoice_number() from ikwen.billing.utils import Invoice app = Application.objects.using(UMBRELLA).get(slug='foulassi') cost = 12000 item = IkwenInvoiceItem(label='School website', price=cost, amount=cost) entry = InvoiceEntry(item=item, total=cost) invoice_entries = [entry] try: Invoice.objects.using(UMBRELLA).get(subscription=weblet, months_count=SCHOOL_WEBSITE_MONTH_COUNT) except: invoice = Invoice(subscription=weblet, member=weblet.member, amount=cost, months_count=SCHOOL_WEBSITE_MONTH_COUNT, number=number, due_date=due_date, last_reminder=now, entries=invoice_entries, is_one_off=True) invoice.save() school.has_subscribed_website_service = True school.save() return HttpResponse(json.dumps({'success': True}, 'content-type: text/json')) except: return HttpResponse(json.dumps({'error': True}, 'content-type: text/json'))
def get_context_data(self, **kwargs): context = super(Receipt, self).get_context_data(**kwargs) config = get_service_instance().config receipt_id = self.kwargs.get('receipt_id') try: payment = Payment.objects.select_related( 'pack', 'member').get(pk=receipt_id) except Payment.DoesNotExist: raise Http404("Payment not found") context['currency_symbol'] = config.currency_symbol context['payment'] = payment context['amount'] = payment.amount context['payment_number'] = get_next_invoice_number() return context
def get_context_data(self, **kwargs): context = super(Receipt, self).get_context_data(**kwargs) config = get_service_instance().config receipt_id = self.kwargs.get('receipt_id') try: reservation = Reservation.objects.select_related('member').get( pk=receipt_id) except Reservation.DoesNotExist: raise Http404("Reservation not found") context['currency_symbol'] = config.currency_symbol context['payment'] = reservation context['member'] = reservation.member context['amount'] = int(reservation.post.cost) context['payment_number'] = get_next_invoice_number() return context
def product_set_checkout(request, *args, **kwargs): service = get_service_instance() product_id = request.POST['product_id'] product = Product.objects.get(pk=product_id) member = request.user now = datetime.now() expiry = now + timedelta(days=product.duration) subscription = Subscription.objects.create(member=member, product=product, since=now, expiry=expiry) number = get_next_invoice_number() item = InvoiceItem(label=product.name) entry = InvoiceEntry(item=item, short_description=product.short_description, total=product.cost) months_count = product.duration / 30 invoice = Invoice.objects.create(subscription=subscription, amount=product.cost, number=number, due_date=now, last_reminder=now, is_one_off=True, entries=[entry], months_count=months_count) request.session['amount'] = product.cost request.session['model_name'] = 'billing.Invoice' request.session['object_id'] = invoice.id mean = request.GET.get('mean', MTN_MOMO) request.session['mean'] = mean request.session['notif_url'] = service.url # Orange Money only request.session['cancel_url'] = service.url + reverse('billing:pricing') # Orange Money only request.session['return_url'] = reverse('billing:invoice_detail', args=(invoice.id, ))
def deploy(app, member, business_type, project_name, billing_plan, theme, monthly_cost, invoice_entries, billing_cycle, domain=None, business_category=None, bundle=None, partner_retailer=None): project_name_slug = slugify( project_name) # Eg: slugify('Cool Shop') = 'cool-shop' ikwen_name = project_name_slug.replace('-', '') # Eg: cool-shop --> 'coolshop' pname = ikwen_name i = 0 while True: try: Service.objects.using(UMBRELLA).get(project_name_slug=pname) i += 1 pname = "%s%d" % (ikwen_name, i) except Service.DoesNotExist: ikwen_name = pname break api_signature = generate_random_key(30, alpha_num=True) while True: try: Service.objects.using(UMBRELLA).get(api_signature=api_signature) api_signature = generate_random_key(30, alpha_num=True) except Service.DoesNotExist: break database = ikwen_name domain = 'go.' + pname + '.ikwen.com' domain_type = Service.SUB is_naked_domain = False url = 'http://go.ikwen.com/' + pname if getattr(settings, 'IS_UMBRELLA', False): admin_url = url + '/ikwen' + reverse('ikwen:staff_router') else: # This is a deployment performed by a partner retailer admin_url = url + reverse('ikwen:staff_router') is_pro_version = billing_plan.is_pro_version now = datetime.now() expiry = now + timedelta(days=15) # Create a copy of template application in the Cloud folder app_folder = CLOUD_HOME + '000Tpl/AppSkeleton' website_home_folder = CLOUD_FOLDER + ikwen_name media_root = CLUSTER_MEDIA_ROOT + ikwen_name + '/' media_url = CLUSTER_MEDIA_URL + ikwen_name + '/' default_images_folder = CLOUD_FOLDER + '000Tpl/images/000Default' theme_images_folder = CLOUD_FOLDER + '000Tpl/images/%s/%s' % ( theme.template.slug, theme.slug) if os.path.exists(theme_images_folder): if os.path.exists(media_root): shutil.rmtree(media_root) shutil.copytree(theme_images_folder, media_root) logger.debug("Media folder '%s' successfully created from '%s'" % (media_root, theme_images_folder)) elif os.path.exists(default_images_folder): if os.path.exists(media_root): shutil.rmtree(media_root) shutil.copytree(default_images_folder, media_root) logger.debug("Media folder '%s' successfully created from '%s'" % (media_root, default_images_folder)) elif not os.path.exists(media_root): os.makedirs(media_root) logger.debug("Media folder '%s' successfully created empty" % media_root) favicons_folder = media_root + 'icons' if not os.path.exists(favicons_folder): os.makedirs(favicons_folder) if os.path.exists(website_home_folder): shutil.rmtree(website_home_folder) shutil.copytree(app_folder, website_home_folder) logger.debug("Service folder '%s' successfully created" % website_home_folder) can_manage_delivery_options = False if business_type == OperatorProfile.PROVIDER: settings_template = 'kakocase/cloud_setup/settings.provider.html' can_manage_delivery_options = True elif business_type == OperatorProfile.RETAILER: settings_template = 'kakocase/cloud_setup/settings.retailer.html' elif business_type == OperatorProfile.LOGISTICS: settings_template = 'kakocase/cloud_setup/settings.delivery.html' auth_code = ikwen_name[:4] + now.strftime('%S') elif business_type == OperatorProfile.BANK: settings_template = 'kakocase/cloud_setup/settings.bank.html' service = Service(member=member, app=app, project_name=project_name, project_name_slug=ikwen_name, domain=domain, database=database, url=url, domain_type=domain_type, expiry=expiry, admin_url=admin_url, billing_plan=billing_plan, billing_cycle=billing_cycle, monthly_cost=monthly_cost, version=Service.TRIAL, retailer=partner_retailer, api_signature=api_signature, home_folder=website_home_folder, settings_template=settings_template) service.save(using=UMBRELLA) logger.debug("Service %s successfully created" % pname) if business_type == OperatorProfile.RETAILER: # Copy Categories image to local media folder as the Retailer is allowed to change them pass # Re-create settings.py file as well as apache.conf file for the newly created project secret_key = generate_django_secret_key() if is_naked_domain: allowed_hosts = '"%s", "www.%s"' % (domain, domain) else: allowed_hosts = '"go.ikwen.com"' settings_tpl = get_template(settings_template) settings_context = Context({ 'secret_key': secret_key, 'ikwen_name': ikwen_name, 'service': service, 'static_root': STATIC_ROOT, 'static_url': STATIC_URL, 'media_root': media_root, 'media_url': media_url, 'allowed_hosts': allowed_hosts, 'debug': getattr(settings, 'DEBUG', False) }) settings_file = website_home_folder + '/conf/settings.py' fh = open(settings_file, 'w') fh.write(settings_tpl.render(settings_context)) fh.close() logger.debug("Settings file '%s' successfully created" % settings_file) # Import template database and set it up if business_type == OperatorProfile.BANK: db_folder = CLOUD_FOLDER + '000Tpl/DB/CashFlex' else: db_folder = CLOUD_FOLDER + '000Tpl/DB/000Default' theme_db_folder = CLOUD_FOLDER + '000Tpl/DB/%s/%s' % ( theme.template.slug, theme.slug) if os.path.exists(theme_db_folder): db_folder = theme_db_folder host = getattr(settings, 'DATABASES')['default'].get('HOST', '127.0.0.1') subprocess.call( ['mongorestore', '--host', host, '-d', database, db_folder]) logger.debug("Database %s successfully created on host %s from %s" % (database, host, db_folder)) add_database_to_settings(database) for group in Group.objects.using(database).all(): try: gpl = GroupPermissionList.objects.using(database).get(group=group) group.delete() group.save(using=database ) # Recreate the group in the service DB with a new id. gpl.group = group # And update GroupPermissionList object with the newly re-created group gpl.save(using=database) except GroupPermissionList.DoesNotExist: group.delete() group.save(using=database ) # Re-create the group in the service DB with anyway. new_sudo_group = Group.objects.using(database).get(name=SUDO) for s in member.get_services(): db = s.database add_database_to_settings(db) collaborates_on_fk_list = member.collaborates_on_fk_list + [service.id] customer_on_fk_list = member.customer_on_fk_list + [service.id] group_fk_list = member.group_fk_list + [new_sudo_group.id] Member.objects.using(db).filter(pk=member.id).update( collaborates_on_fk_list=collaborates_on_fk_list, customer_on_fk_list=customer_on_fk_list, group_fk_list=group_fk_list) member.collaborates_on_fk_list = collaborates_on_fk_list member.customer_on_fk_list = customer_on_fk_list member.group_fk_list = group_fk_list member.is_iao = True member.save(using=UMBRELLA) member.is_bao = True member.is_staff = True member.is_superuser = True app.save(using=database) member.save(using=database) logger.debug("Member %s access rights successfully set for service %s" % (member.username, pname)) from ikwen.billing.mtnmomo.views import MTN_MOMO from ikwen.billing.orangemoney.views import ORANGE_MONEY # Copy payment means to local database for mean in PaymentMean.objects.using(UMBRELLA).all(): if mean.slug == 'paypal': mean.action_url_name = 'shopping:paypal_set_checkout' if mean.slug == MTN_MOMO: mean.is_main = True mean.is_active = True elif mean.slug == ORANGE_MONEY: mean.is_main = False mean.is_active = True else: mean.is_main = False mean.is_active = False mean.save(using=database) logger.debug("PaymentMean %s created in database: %s" % (mean.slug, database)) # Copy themes to local database for template in Template.objects.using(UMBRELLA).all(): template.save(using=database) for th in Theme.objects.using(UMBRELLA).all(): th.save(using=database) logger.debug("Template and theme successfully bound for service: %s" % pname) FlatPage.objects.using(database).get_or_create( url=FlatPage.AGREEMENT, title=FlatPage.AGREEMENT.capitalize(), content=_('Agreement goes here')) FlatPage.objects.using(database).get_or_create( url=FlatPage.LEGAL_MENTIONS, title=FlatPage.LEGAL_MENTIONS.capitalize(), content=_('Legal mentions go here')) # Add member to SUDO Group obj_list, created = UserPermissionList.objects.using( database).get_or_create(user=member) obj_list.group_fk_list.append(new_sudo_group.id) obj_list.save(using=database) logger.debug("Member %s successfully added to sudo group for service: %s" % (member.username, pname)) # Create wallets wallet = OperatorWallet.objects.using('wallets').create( nonrel_id=service.id, provider=MTN_MOMO) OperatorWallet.objects.using('wallets').create(nonrel_id=service.id, provider=ORANGE_MONEY) mail_signature = "%s<br>" \ "<a href='%s'>%s</a>" % (project_name, 'http://' + domain, domain) invitation_message = _( "Hey $client<br>" "We are inviting you to join our awesome community on ikwen.") config = OperatorProfile( service=service, rel_id=wallet.id, media_url=media_url, ikwen_share_fixed=billing_plan.tx_share_fixed, ikwen_share_rate=billing_plan.tx_share_rate, can_manage_delivery_options=can_manage_delivery_options, business_type=business_type, is_pro_version=is_pro_version, theme=theme, currency_code='XAF', currency_symbol='XAF', signature=mail_signature, max_products=billing_plan.max_objects, decimal_precision=0, company_name=project_name, contact_email=member.email, contact_phone=member.phone, business_category=business_category, bundle=bundle, sms_api_script_url=SMS_API_URL, invitation_message=invitation_message) config.save(using=UMBRELLA) base_config = config.get_base_config() base_config.save(using=UMBRELLA) from daraja.models import DARAJA if partner_retailer: partner_retailer.save(using=database) try: if partner_retailer.app.slug == DARAJA: partner_db = partner_retailer.database add_database_to_settings(partner_db) ikwen_service_partner = Service.objects.using(partner_db).get( project_name_slug='ikwen') set_counters(ikwen_service_partner) increment_history_field(ikwen_service_partner, 'community_history') except: logger.error( "Could not set Followers count upon Service deployment", exc_info=True) if bundle: # Tsunami bundle from echo.models import Balance token = ''.join( [random.SystemRandom().choice(string.digits) for i in range(6)]) expiry = now + timedelta(days=bundle.support_bundle.duration) SupportCode.objects.using(UMBRELLA).create( service=service, bundle=bundle.support_bundle, token=token, expiry=expiry) Balance.objects.using('wallets').create(service_id=service.id) service.save(using=database) if business_type == OperatorProfile.LOGISTICS: config.auth_code = auth_code theme.save(using=database ) # Causes theme to be routed to the newly created database if business_category: business_category.save(using=database) config.save(using=database) InvoicingConfig.objects.using(database).create() logger.debug("Configuration successfully added for service: %s" % pname) # Copy samples to local database for product in Product.objects.using(database).all(): product.provider = service product.save(using=database) logger.debug("Sample products successfully copied to database %s" % database) # Create delivery options: Pick-up in store and Free home delivery DeliveryOption.objects.using(database).create( company=service, type=DeliveryOption.PICK_UP_IN_STORE, name=_("Pick up in store"), slug='pick-up-in-store', short_description=_("2H after order"), cost=0, max_delay=2) DeliveryOption.objects.using(database).create( company=service, type=DeliveryOption.HOME_DELIVERY, name=_("Home delivery"), slug='home-delivery', short_description=_("Max. 72H after order"), cost=500, max_delay=72) # Apache Server cloud_setup go_apache_tpl = get_template('core/cloud_setup/apache.conf.local.html') apache_context = Context({ 'is_naked_domain': is_naked_domain, 'domain': domain, 'home_folder': website_home_folder, 'ikwen_name': ikwen_name }) if is_naked_domain: apache_tpl = get_template('kakocase/cloud_setup/apache.conf.html') fh = open(website_home_folder + '/apache.conf', 'w') fh.write(apache_tpl.render(apache_context)) fh.close() fh = open(website_home_folder + '/go_apache.conf', 'w') fh.write(go_apache_tpl.render(apache_context)) fh.close() vhost = '/etc/apache2/sites-enabled/go_ikwen/' + pname + '.conf' subprocess.call( ['sudo', 'ln', '-sf', website_home_folder + '/go_apache.conf', vhost]) if is_naked_domain: vhost = '/etc/apache2/sites-enabled/' + domain + '.conf' subprocess.call( ['sudo', 'ln', '-sf', website_home_folder + '/apache.conf', vhost]) logger.debug("Apache Virtual Host '%s' successfully created" % vhost) # Send notification and Invoice to customer number = get_next_invoice_number() now = datetime.now() invoice_total = 0 for entry in invoice_entries: invoice_total += entry.item.amount * entry.quantity invoice = Invoice(subscription=service, member=member, amount=invoice_total, number=number, due_date=expiry, last_reminder=now, reminders_sent=1, is_one_off=True, entries=invoice_entries, months_count=billing_plan.setup_months_count) invoice.save(using=UMBRELLA) vendor = get_service_instance() if member != vendor.member: add_event(vendor, SERVICE_DEPLOYED, member=member, object_id=invoice.id) if partner_retailer and partner_retailer.app.slug != DARAJA: partner_profile = PartnerProfile.objects.using(UMBRELLA).get( service=partner_retailer) try: Member.objects.get(pk=member.id) except Member.DoesNotExist: member.is_iao = False member.is_bao = False member.is_staff = False member.is_superuser = False member.save(using='default') service.save(using='default') config.save(using='default') sender = '%s <no-reply@%s>' % (partner_profile.company_name, partner_retailer.domain) sudo_group = Group.objects.get(name=SUDO) ikwen_sudo_gp = Group.objects.using(UMBRELLA).get(name=SUDO) add_event(vendor, SERVICE_DEPLOYED, group_id=ikwen_sudo_gp.id, object_id=invoice.id) elif partner_retailer and partner_retailer.app.slug == DARAJA: try: dara_member = partner_retailer.member dara = Dara.objects.get(member=dara_member) dara_earnings = invoice_total * dara.share_rate / 100 phone = member.phone if len(phone) == 9 and not phone.startswith('237'): member.phone = '237' + member.phone template_name = 'daraja/mails/remind_referrer.html' activate(dara_member.language) subject = _("Congratulations ! %s CFA is waiting for you." % intcomma(dara_earnings)) try: extra_context = { 'referee': member, 'amount': dara_earnings, 'invoice_total': intcomma(invoice_total), 'deployed_service': service, 'dara_name': dara_member.full_name } html_content = get_mail_content(subject, template_name=template_name, extra_context=extra_context) sender = 'ikwen Daraja <*****@*****.**>' msg = EmailMessage(subject, html_content, sender, [dara_member.email]) msg.content_subtype = "html" Thread(target=lambda m: m.send(), args=(msg, )).start() except: logger.error( "Failed to notify %s Dara after follower deployed Kakocase website." % dara_member.full_name, exc_info=True) except Dara.DoesNotExist: logging.error("%s - Customer %s has not been referred" % (service.project_name, member.username)) else: sender = 'ikwen Tsunami <*****@*****.**>' sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO) add_event(vendor, SERVICE_DEPLOYED, group_id=sudo_group.id, object_id=invoice.id) invoice_url = 'http://www.ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id, )) activate(member.language) subject = _("Your website %s was created" % project_name) html_content = get_mail_content( subject, template_name='core/mails/service_deployed.html', extra_context={ 'service_activated': service, 'invoice': invoice, 'member': member, 'invoice_url': invoice_url }) msg = EmailMessage(subject, html_content, sender, [member.email]) bcc = ['*****@*****.**', '*****@*****.**'] if vendor.config.contact_email: bcc.append(vendor.config.contact_email) msg.bcc = list(set(bcc)) msg.content_subtype = "html" Thread(target=lambda m: m.send(), args=(msg, )).start() logger.debug("Notice email submitted to %s" % member.email) Thread(target=reload_server).start() logger.debug("Apache Scheduled to reload in 5s") return service
def after_save(self, request, obj, *args, **kwargs): object_id = kwargs.get('object_id') if object_id: return number = get_next_invoice_number() months_count = get_billing_cycle_months_count(obj.billing_cycle) try: amount = float(request.POST.get('amount')) except: amount = obj.monthly_cost * months_count product = obj.product if product: short_description = product.name else: short_description = request.POST.get('short_description', '---') obj.details = short_description obj.save() invoice_entries = [] item = InvoiceItem(label=_('Subscription'), amount=amount) entry = InvoiceEntry(item=item, short_description=short_description, quantity=months_count, total=amount) invoice_entries.append(entry) invoice = Invoice.objects.create(number=number, subscription=obj, amount=amount, months_count=months_count, due_date=obj.expiry, entries=invoice_entries, is_one_off=True) email = request.POST.get('email') member_id = request.POST.get('member_id') if member_id: member = Member.objects.get(pk=member_id) if member_id else None elif email: try: member = Member.objects.filter(email=email)[0] except: member = Member.objects.create_user(email, DEFAULT_GHOST_PWD, email=email, is_ghost=True) else: return obj.member = member obj.save() service = get_service_instance() config = service.config with transaction.atomic(using=WALLETS_DB_ALIAS): from echo.models import Balance from echo.utils import notify_for_low_messaging_credit, LOW_MAIL_LIMIT, notify_for_empty_messaging_credit balance, update = Balance.objects.using( WALLETS_DB_ALIAS).get_or_create(service_id=service.id) if 0 < balance.mail_count < LOW_MAIL_LIMIT: try: notify_for_low_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for low messaging credit." % service, exc_info=True) if balance.mail_count == 0 and not getattr(settings, 'UNIT_TESTING', False): try: notify_for_empty_messaging_credit(service, balance) except: logger.error( "Failed to notify %s for empty messaging credit." % service, exc_info=True) return if product: subject = _("Your invoice for subscription to %s" % product.name) else: if short_description != '---': subject = _("Your invoice for " + short_description) else: subject = _("Your invoice for subscription") try: currency = currencies(request)['CURRENCY'].symbol except: currency = config.currency_symbol invoice_url = service.url + reverse('billing:invoice_detail', args=(invoice.id, )) html_content = get_mail_content( subject, template_name='billing/mails/notice.html', extra_context={ 'invoice': invoice, 'member_name': member.first_name, 'invoice_url': invoice_url, 'cta': _("Pay now"), 'currency': currency }) sender = '%s <no-reply@%s>' % (config.company_name, service.domain) msg = XEmailMessage(subject, html_content, sender, [email]) msg.content_subtype = "html" balance.mail_count -= 1 balance.save() if getattr(settings, 'UNIT_TESTING', False): msg.send() else: Thread(target=lambda m: m.send(), args=(msg, )).start()
def set_my_kids_payment(request, *args, **kwargs): school_id = request.POST['school_id'] student_id = request.POST['student_id'] cycle = request.POST['my_kids_cycle'] school = Service.objects.get(pk=school_id) student = Student.objects.get(pk=student_id) school_config = SchoolConfig.objects.get(service=school) Invoice.objects.filter(student=student, is_my_kids=True, status=Invoice.PENDING).delete() max_expiry = datetime(day=31, month=8, year=get_school_year() + 1) if cycle == Service.YEARLY: amount = school_config.my_kids_fees elif cycle == Service.QUARTERLY: amount = school_config.my_kids_fees_term else: amount = school_config.my_kids_fees_month cycle = Service.MONTHLY item = InvoiceItem(label=_("MyKids fees"), amount=amount) days = get_billing_cycle_days_count(cycle) now = datetime.now() expiry = now + timedelta(days=days) expiry = min(expiry, max_expiry) short_description = now.strftime("%Y/%m/%d") + ' - ' + expiry.strftime( "%Y/%m/%d") entry = InvoiceEntry(item=item, short_description=short_description, total=amount, quantity_unit='') number = get_next_invoice_number() member = request.user invoice = Invoice.objects.create(number=number, member=member, student=student, school=school, is_one_off=True, amount=amount, my_kids_cycle=cycle, due_date=now, entries=[entry], is_my_kids=True) foulassi_weblet = get_service_instance() # This is Foulassi service itself # Transaction is hidden from school if ikwen collects 100%. # This is achieved by changing the service_id of transaction tx_service_id = school.id if school_config.my_kids_share_rate < 100 else foulassi_weblet.id model_name = 'billing.Invoice' mean = request.GET.get('mean', MTN_MOMO) signature = ''.join([ random.SystemRandom().choice(string.ascii_letters + string.digits) for i in range(16) ]) MoMoTransaction.objects.using(WALLETS_DB_ALIAS).filter( object_id=invoice.id).delete() tx = MoMoTransaction.objects.using(WALLETS_DB_ALIAS)\ .create(service_id=tx_service_id, type=MoMoTransaction.CASH_OUT, amount=amount, phone='N/A', model=model_name, object_id=invoice.id, task_id=signature, wallet=mean, username=request.user.username, is_running=True) notification_url = foulassi_weblet.url + reverse( 'foulassi:confirm_my_kids_payment', args=(tx.id, signature)) cancel_url = request.META['HTTP_REFERER'] return_url = request.META['HTTP_REFERER'] return invoice, amount, notification_url, return_url, cancel_url
def deploy(member, project_name, billing_plan, theme, monthly_cost, invoice_entries, partner_retailer=None): app = Application.objects.using(UMBRELLA).get(slug='foulassi') project_name_slug = slugify( project_name) # Eg: slugify('Great School') = 'great-school' ikwen_name = project_name_slug.replace( '-', '') # Eg: great-school --> 'greatschool' pname = ikwen_name i = 0 while True: try: Service.objects.using(UMBRELLA).get(project_name_slug=pname) i += 1 pname = "%s%d" % (ikwen_name, i) except Service.DoesNotExist: ikwen_name = pname break api_signature = generate_random_key(30, alpha_num=True) while True: try: Service.objects.using(UMBRELLA).get(api_signature=api_signature) api_signature = generate_random_key(30, alpha_num=True) except Service.DoesNotExist: break database = ikwen_name domain = 'go.' + pname + '.ikwen.com' domain_type = Service.SUB is_naked_domain = False url = 'http://go.ikwen.com/' + pname admin_url = url + '/ikwen/staffRouter/' now = datetime.now() expiry = now + timedelta(days=60) # Create a copy of template application in the Cloud folder app_folder = CLOUD_HOME + '000Tpl/AppSkeleton' website_home_folder = CLOUD_FOLDER + ikwen_name media_root = CLUSTER_MEDIA_ROOT + ikwen_name + '/' media_url = CLUSTER_MEDIA_URL + ikwen_name + '/' images_folder = CLOUD_FOLDER + '000Tpl/images/000Default' if theme: theme_images_folder = CLOUD_FOLDER + '000Tpl/images/%s/%s' % ( theme.template.slug, theme.slug) if os.path.exists(theme_images_folder): images_folder = theme_images_folder if os.path.exists(images_folder): if os.path.exists(media_root): shutil.rmtree(media_root) shutil.copytree(images_folder, media_root) logger.debug("Media folder '%s' successfully created from '%s'" % (media_root, images_folder)) elif not os.path.exists(media_root): os.makedirs(media_root) logger.debug("Media folder '%s' successfully created empty" % media_root) icons_folder = media_root + 'icons' if not os.path.exists(icons_folder): os.makedirs(icons_folder) if os.path.exists(website_home_folder): shutil.rmtree(website_home_folder) shutil.copytree(app_folder, website_home_folder) logger.debug("Service folder '%s' successfully created" % website_home_folder) settings_template = 'foulassi/cloud_setup/settings.html' service = Service(member=member, app=app, project_name=project_name, project_name_slug=ikwen_name, domain=domain, database=database, url=url, domain_type=domain_type, expiry=expiry, admin_url=admin_url, billing_plan=billing_plan, billing_cycle=Service.YEARLY, monthly_cost=monthly_cost, version=Service.TRIAL, api_signature=api_signature, home_folder=website_home_folder, settings_template=settings_template, retailer=partner_retailer) service.save(using=UMBRELLA) logger.debug("Service %s successfully created" % pname) # Re-create settings.py file as well as apache.conf file for the newly created project secret_key = generate_django_secret_key() allowed_hosts = '"go.ikwen.com"' settings_tpl = get_template(settings_template) settings_context = Context({ 'secret_key': secret_key, 'ikwen_name': ikwen_name, 'service': service, 'static_root': STATIC_ROOT, 'static_url': STATIC_URL, 'media_root': media_root, 'media_url': media_url, 'allowed_hosts': allowed_hosts, 'debug': getattr(settings, 'DEBUG', False) }) settings_file = website_home_folder + '/conf/settings.py' fh = open(settings_file, 'w') fh.write(settings_tpl.render(settings_context)) fh.close() logger.debug("Settings file '%s' successfully created" % settings_file) # Import template database and set it up db_folder = CLOUD_FOLDER + '000Tpl/DB/000Default' if theme: theme_db_folder = CLOUD_FOLDER + '000Tpl/DB/%s/%s' % ( theme.template.slug, theme.slug) if os.path.exists(theme_db_folder): db_folder = theme_db_folder host = getattr(settings, 'DATABASES')['default'].get('HOST', '127.0.0.1') from ikwen.core.log import ikwen_error_log_filename eh = open(ikwen_error_log_filename, 'a') subprocess.call( ['mongorestore', '--host', host, '-d', database, db_folder], stderr=eh) logger.debug("Database %s successfully created on host %s from %s" % (database, host, db_folder)) add_database_to_settings(database) for group in Group.objects.using(database).all(): try: gpl = GroupPermissionList.objects.using(database).get(group=group) group.delete() group.save(using=database ) # Recreate the group in the service DB with a new id. gpl.group = group # And update GroupPermissionList object with the newly re-created group gpl.save(using=database) except GroupPermissionList.DoesNotExist: group.delete() group.save(using=database ) # Re-create the group in the service DB with anyway. new_sudo_group = Group.objects.using(database).get(name=SUDO) for s in member.get_services(): db = s.database add_database_to_settings(db) collaborates_on_fk_list = member.collaborates_on_fk_list + [service.id] customer_on_fk_list = member.customer_on_fk_list + [service.id] group_fk_list = member.group_fk_list + [new_sudo_group.id] Member.objects.using(db).filter(pk=member.id).update( collaborates_on_fk_list=collaborates_on_fk_list, customer_on_fk_list=customer_on_fk_list, group_fk_list=group_fk_list) member.collaborates_on_fk_list = collaborates_on_fk_list member.customer_on_fk_list = customer_on_fk_list member.group_fk_list = group_fk_list member.is_iao = True member.save(using=UMBRELLA) member.is_bao = True member.is_staff = True member.is_superuser = True app.save(using=database) member.save(using=database) logger.debug("Member %s access rights successfully set for service %s" % (member.username, pname)) from ikwen.billing.mtnmomo.views import MTN_MOMO # Copy payment means to local database for mean in PaymentMean.objects.using(UMBRELLA).all(): if mean.slug == MTN_MOMO: mean.is_main = True mean.is_active = True else: mean.is_main = False mean.is_active = True mean.save(using=database) logger.debug("PaymentMean %s created in database: %s" % (mean.slug, database)) # Copy themes to local database webnode = Application.objects.using(UMBRELLA).get(slug='webnode') template_list = list(Template.objects.using(UMBRELLA).filter(app=webnode)) for template in template_list: template.save(using=database) for th in Theme.objects.using(UMBRELLA).filter(template__in=template_list): th.save(using=database) logger.debug("Templates and themes successfully saved for service: %s" % pname) FlatPage.objects.using(database).get_or_create(url=FlatPage.AGREEMENT, title=FlatPage.AGREEMENT) FlatPage.objects.using(database).get_or_create( url=FlatPage.LEGAL_MENTIONS, title=FlatPage.LEGAL_MENTIONS) # Add member to SUDO Group obj_list, created = UserPermissionList.objects.using( database).get_or_create(user=member) obj_list.group_fk_list.append(new_sudo_group.id) obj_list.save(using=database) logger.debug("Member %s successfully added to sudo group for service: %s" % (member.username, pname)) mail_signature = "%s<br>" \ "<a href='%s'>%s</a>" % (project_name, 'http://' + domain, domain) config = SchoolConfig(service=service, ikwen_share_rate=billing_plan.tx_share_rate, theme=theme, currency_code='XAF', currency_symbol='XAF', decimal_precision=0, signature=mail_signature, company_name=project_name, contact_email=member.email, contact_phone=member.phone, sms_api_script_url=SMS_API_URL, back_to_school_date=back_to_school_date) config.save(using=UMBRELLA) base_config = config.get_base_config() base_config.save(using=UMBRELLA) service.save(using=database) theme.save(using=database ) # Causes theme to be routed to the newly created database config.save(using=database) logger.debug("Configuration successfully added for service: %s" % pname) # Apache Server cloud_setup go_apache_tpl = get_template('foulassi/cloud_setup/apache.conf.local.html') apache_context = Context({ 'is_naked_domain': is_naked_domain, 'domain': domain, 'ikwen_name': ikwen_name }) fh = open(website_home_folder + '/go_apache.conf', 'w') fh.write(go_apache_tpl.render(apache_context)) fh.close() vhost = '/etc/apache2/sites-enabled/go_ikwen/' + pname + '.conf' subprocess.call( ['sudo', 'ln', '-sf', website_home_folder + '/go_apache.conf', vhost]) logger.debug("Apache Virtual Host '%s' successfully created" % vhost) # Send notification and Invoice to customer number = get_next_invoice_number() now = datetime.now() invoice_total = 0 for entry in invoice_entries: invoice_total += entry.total invoice = Invoice(subscription=service, member=member, amount=invoice_total, number=number, due_date=expiry, last_reminder=now, reminders_sent=1, is_one_off=True, entries=invoice_entries, months_count=billing_plan.setup_months_count) invoice.save(using=UMBRELLA) vendor = get_service_instance() from daraja.models import DARAJA if member != vendor.member: add_event(vendor, SERVICE_DEPLOYED, member=member, object_id=invoice.id) if partner_retailer and partner_retailer.app.slug != DARAJA: partner_profile = PartnerProfile.objects.using(UMBRELLA).get( service=partner_retailer) try: Member.objects.get(pk=member.id) except Member.DoesNotExist: member.is_iao = False member.is_bao = False member.is_staff = False member.is_superuser = False member.save(using='default') service.save(using='default') config.save(using='default') sender = '%s <no-reply@%s>' % (partner_profile.company_name, partner_retailer.domain) sudo_group = Group.objects.get(name=SUDO) ikwen_sudo_gp = Group.objects.using(UMBRELLA).get(name=SUDO) add_event(vendor, SERVICE_DEPLOYED, group_id=ikwen_sudo_gp.id, object_id=invoice.id) else: sender = 'ikwen Foulassi <*****@*****.**>' sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO) add_event(vendor, SERVICE_DEPLOYED, group_id=sudo_group.id, object_id=invoice.id) invoice_url = 'http://www.ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id, )) subject = _("Your platform %s was created" % project_name) html_content = get_mail_content( subject, template_name='core/mails/service_deployed.html', extra_context={ 'service_activated': service, 'invoice': invoice, 'member': member, 'invoice_url': invoice_url }) msg = EmailMessage(subject, html_content, sender, [member.email]) bcc = ['*****@*****.**', '*****@*****.**'] if vendor.config.contact_email: bcc.append(vendor.config.contact_email) msg.bcc = list(set(bcc)) msg.content_subtype = "html" Thread(target=lambda m: m.send(), args=(msg, )).start() logger.debug("Notice email submitted to %s" % member.email) Thread(target=reload_server).start() logger.debug("Apache Scheduled to reload in 5s") return service
def deploy(app, member, project_name, billing_plan, monthly_cost, theme, billing_cycle, invoice_entries, is_pro_version=False): project_name_slug = slugify( project_name) # Eg: slugify('Cool Shop') = 'cool-shop' ikwen_name = project_name_slug.replace('-', '') # Eg: cool-shop --> 'coolshop' pname = ikwen_name i = 0 while True: try: Service.objects.using(UMBRELLA).get(project_name_slug=pname) i += 1 pname = "%s%d" % (ikwen_name, i) except Service.DoesNotExist: ikwen_name = pname break api_signature = generate_random_key(30) while True: try: Service.objects.using(UMBRELLA).get(api_signature=api_signature) api_signature = generate_random_key(30) except Service.DoesNotExist: break database = ikwen_name domain = ikwen_name + '.ikwen.com' domain_type = Service.SUB is_naked_domain = False admin_url = domain + '/ikwen' + reverse('ikwen:staff_router') now = datetime.now() expiry = now + timedelta(days=15) # Create a copy of template application in the Cloud folder app_folder = CLOUD_HOME + '000Tpl/AppSkeleton' website_home_folder = CLOUD_FOLDER + ikwen_name media_root = CLUSTER_MEDIA_ROOT + ikwen_name + '/' media_url = CLUSTER_MEDIA_URL + ikwen_name + '/' default_images_folder = CLOUD_FOLDER + '000Tpl/images/000Default' if os.path.exists(default_images_folder): if os.path.exists(media_root): shutil.rmtree(media_root) shutil.copytree(default_images_folder, media_root) logger.debug("Media folder '%s' successfully created from '%s'" % (media_root, default_images_folder)) elif not os.path.exists(media_root): os.makedirs(media_root) logger.debug("Media folder '%s' successfully created empty" % media_root) favicons_folder = media_root + 'favicons' if not os.path.exists(favicons_folder): os.makedirs(favicons_folder) if os.path.exists(website_home_folder): shutil.rmtree(website_home_folder) shutil.copytree(app_folder, website_home_folder) logger.debug("Service folder '%s' successfully created" % website_home_folder) settings_template = 'partnership/cloud_setup/settings.html' service = Service(member=member, app=app, project_name=project_name, project_name_slug=ikwen_name, domain=domain, database=database, url='http://' + domain, domain_type=domain_type, expiry=expiry, admin_url='http://' + admin_url, api_signature=api_signature, billing_cycle=billing_cycle, monthly_cost=monthly_cost, version=Service.TRIAL, home_folder=website_home_folder, settings_template=settings_template) service.save(using=UMBRELLA) # Re-create settings.py file as well as apache.conf file for the newly created project secret_key = generate_django_secret_key() allowed_hosts = '"%s", "www.%s"' % (domain, domain) settings_tpl = get_template(settings_template) settings_context = Context({ 'secret_key': secret_key, 'ikwen_name': ikwen_name, 'service': service, 'static_root': STATIC_ROOT, 'static_url': STATIC_URL, 'media_root': media_root, 'media_url': media_url, 'allowed_hosts': allowed_hosts, 'debug': getattr(settings, 'DEBUG', False) }) settings_file = website_home_folder + '/conf/settings.py' fh = open(settings_file, 'w') fh.write(settings_tpl.render(settings_context)) fh.close() logger.debug("Settings file '%s' successfully created" % settings_file) # Import template database and set it up host = getattr(settings, 'DATABASES')['default'].get('HOST', '127.0.0.1') subprocess.call([ 'mongorestore', '--host', host, '-d', database, CLOUD_FOLDER + '000Tpl/DB/000Default' ]) add_database_to_settings(database) for group in Group.objects.using(database).all(): try: gpl = GroupPermissionList.objects.using(database).get(group=group) group.delete() group.save(using=database ) # Recreate the group in the service DB with a new id. gpl.group = group # And update GroupPermissionList object with the newly re-created group gpl.save(using=database) except GroupPermissionList.DoesNotExist: group.delete() group.save(using=database ) # Re-create the group in the service DB with anyway. new_sudo_group = Group.objects.using(database).get(name=SUDO) for s in member.get_services(): db = s.database add_database_to_settings(db) collaborates_on_fk_list = member.collaborates_on_fk_list + [service.id] customer_on_fk_list = member.customer_on_fk_list + [service.id] group_fk_list = member.group_fk_list + [new_sudo_group.id] Member.objects.using(db).filter(pk=member.id).update( collaborates_on_fk_list=collaborates_on_fk_list, customer_on_fk_list=customer_on_fk_list, group_fk_list=group_fk_list) member.collaborates_on_fk_list = collaborates_on_fk_list member.customer_on_fk_list = customer_on_fk_list member.group_fk_list = group_fk_list member.is_iao = True member.save(using=UMBRELLA) member.is_bao = True member.is_staff = True member.is_superuser = True app.save(using=database) member.save(using=database) logger.debug("Member %s access rights successfully set for service %s" % (member.username, pname)) # Copy payment means to local database for mean in PaymentMean.objects.using(UMBRELLA).all(): if mean.slug == MTN_MOMO: mean.is_main = True mean.is_active = True elif mean.slug == ORANGE_MONEY: mean.is_main = False mean.is_active = True else: mean.is_main = False mean.is_active = False mean.save(using=database) logger.debug("PaymentMean %s created in database: %s" % (mean.slug, database)) # Copy themes to local database for template in Template.objects.using(UMBRELLA).all(): template.save(using=database) for th in Theme.objects.using(UMBRELLA).all(): th.save(using=database) logger.debug("Template and theme successfully bound for service: %s" % pname) # Create Module Blog module_blog = Module.objects.using(UMBRELLA).get(slug='module_blog') module_blog.is_active = True module_blog.save(using=database) logger.debug("Module Blog created for service: %s" % pname) FlatPage.objects.using(database).create( url=FlatPage.AGREEMENT, title=FlatPage.AGREEMENT.capitalize()) FlatPage.objects.using(database).create( url=FlatPage.LEGAL_MENTIONS, title=FlatPage.LEGAL_MENTIONS.capitalize().replace('-', ' ')) # Add member to SUDO Group obj_list, created = UserPermissionList.objects.using( database).get_or_create(user=member) obj_list.group_fk_list.append(new_sudo_group.id) obj_list.save(using=database) OperatorWallet.objects.using('wallets').create(nonrel_id=service.id, provider=MTN_MOMO) OperatorWallet.objects.using('wallets').create(nonrel_id=service.id, provider=ORANGE_MONEY) mail_signature = "%s<br>" \ "<a href='%s'>%s</a>" % (project_name, 'http://' + domain, domain) config = PartnerProfile(service=service, is_pro_version=is_pro_version, theme=theme, currency_code='XAF', currency_symbol='XAF', signature=mail_signature, company_name=project_name, contact_email=member.email, contact_phone=member.phone) config.save(using=UMBRELLA) base_config = config.get_base_config() base_config.save(using=UMBRELLA) service.save(using=database) theme.save(using=database ) # Causes theme to be routed to the newly created database config.save(using=database) InvoicingConfig.objects.using(database).create() # Apache Server cloud_setup if getattr(settings, 'LOCAL_DEV', False): apache_tpl = get_template( 'partnership/cloud_setup/apache.conf.local.html') else: apache_tpl = get_template('partnership/cloud_setup/apache.conf.html') apache_context = Context({ 'is_naked_domain': is_naked_domain, 'domain': domain, 'ikwen_name': ikwen_name }) fh = open(website_home_folder + '/apache.conf', 'w') fh.write(apache_tpl.render(apache_context)) fh.close() vhost = '/etc/apache2/sites-enabled/' + domain + '.conf' subprocess.call( ['sudo', 'ln', '-sf', website_home_folder + '/apache.conf', vhost]) logger.debug("Apache Virtual Host '%s' successfully created" % vhost) # Send notification and Invoice to customer number = get_next_invoice_number() now = datetime.now() invoice_total = 0 for entry in invoice_entries: invoice_total += entry.item.amount * entry.quantity invoice = Invoice(subscription=service, amount=invoice_total, number=number, due_date=expiry, last_reminder=now, reminders_sent=1, is_one_off=True, entries=invoice_entries, months_count=billing_plan.setup_months_count) invoice.save(using=UMBRELLA) vendor = get_service_instance() sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO) add_event(vendor, SERVICE_DEPLOYED, member=member, object_id=invoice.id) add_event(vendor, SERVICE_DEPLOYED, group_id=sudo_group.id, object_id=invoice.id) invoice_url = 'http://www.ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id, )) sender = 'ikwen <*****@*****.**>' subject = _("Your retailer platform was created") html_content = get_mail_content( subject, '', template_name='core/mails/service_deployed.html', extra_context={ 'service_activated': service, 'invoice': invoice, 'member': member, 'invoice_url': invoice_url }) msg = EmailMessage(subject, html_content, sender, [member.email]) msg.bcc = ['*****@*****.**'] msg.content_subtype = "html" Thread(target=lambda m: m.send(), args=(msg, )).start() logger.debug("Notice email submitted to %s" % member.email) Thread(target=reload_server).start() logger.debug("Apache Scheduled to reload in 5s") return service
def send_invoices(): """ This cron task simply sends the Invoice *invoicing_gap* days before Subscription *expiry* """ vendor, config, invoicing_config, connection = _init_base_vars() now = timezone.now() count, total_amount = 0, 0 reminder_date_time = now + timedelta(days=invoicing_config.gap) subscription_qs = Subscription.objects.filter(status=Subscription.ACTIVE, monthly_cost__gt=0, expiry__lt=reminder_date_time.date()) logger.debug("%d Service candidate for invoice issuance." % subscription_qs.count()) for subscription in subscription_qs: if getattr(settings, 'IS_IKWEN', False): if subscription.version == Service.FREE: continue try: pending_invoice = Invoice.objects.get(subscription=subscription, status=Invoice.PENDING) logger.debug("%s found for %s. Skipping" % (pending_invoice, subscription)) continue # Continue if a Pending invoice for this Subscription is found except Invoice.DoesNotExist: pass member = subscription.member number = get_next_invoice_number() months_count = None if config.__dict__.get('separate_billing_cycle', True): months_count = get_billing_cycle_months_count(subscription.billing_cycle) amount = subscription.monthly_cost * months_count else: amount = subscription.product.cost path_before = getattr(settings, 'BILLING_BEFORE_NEW_INVOICE', None) if path_before: before_new_invoice = import_by_path(path_before) val = before_new_invoice(subscription) if val is not None: # Returning a not None value cancels the generation of a new Invoice for this Service continue entries = [] if type(subscription) is Service: from daraja.models import DARAJA partner = subscription.retailer if partner and partner.app.slug != DARAJA: retail_config = ApplicationRetailConfig.objects.get(partner=partner, app=subscription.app) ikwen_price = retail_config.ikwen_monthly_cost else: ikwen_price = subscription.monthly_cost hosting = IkwenInvoiceItem(label=_('Website hosting'), price=ikwen_price, amount=subscription.monthly_cost) short_description = _("Project %s" % subscription.domain) entry = InvoiceEntry(item=hosting, short_description=short_description, quantity=months_count, total=amount) entries = [entry] try: cr_op_profile = CROperatorProfile.objects.get(service=subscription, is_active=True) if cr_op_profile.monthly_cost > 0: plan = cr_op_profile.plan cr_monthly_cost = cr_op_profile.monthly_cost cr_item = IkwenInvoiceItem(label=_('Continuous Rewarding'), price=cr_monthly_cost, amount=cr_monthly_cost) short_description = plan.name cr_amount = months_count * cr_monthly_cost amount += cr_amount entry = InvoiceEntry(item=cr_item, short_description=short_description, quantity=months_count, total=cr_amount) entries.append(entry) except CROperatorProfile.DoesNotExist: pass invoice = Invoice.objects.create(subscription=subscription, member=subscription.member, amount=amount, number=number, due_date=subscription.expiry, months_count=months_count, entries=entries, last_reminder=now) count += 1 total_amount += amount add_event(vendor, NEW_INVOICE_EVENT, member=member, object_id=invoice.id) paid_by_wallet_debit = False if getattr(settings, 'IS_IKWEN', False) and subscription.balance >= invoice.amount: pay_with_wallet_balance(invoice) paid_by_wallet_debit = True logger.debug("Invoice for %s paid by wallet debit" % subscription.domain) subject, message, sms_text = get_invoice_generated_message(invoice) if member.email: activate(member.language) invoice_url = 'http://ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id,)) if paid_by_wallet_debit: subject = _("Thanks for your payment") invoice_url = 'http://ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id,)) context = {'wallet_debit': True, 'invoice': invoice, 'config': config, 'member_name': member.first_name, 'invoice_url': invoice_url, 'cta': _("View invoice")} html_content = get_mail_content(subject, '', template_name='billing/mails/wallet_debit_notice.html', extra_context=context) else: context = {'invoice_url': invoice_url, 'cta': _("Pay now"), 'currency': config.currency_symbol, 'service': vendor, 'config': config, 'logo': config.logo, 'project_name': vendor.project_name, 'company_name': config.company_name, 'member_name': member.first_name, 'invoice': invoice} html_content = get_mail_content(subject, message, template_name='billing/mails/notice.html', extra_context=context) # Sender is simulated as being no-reply@company_name_slug.com to avoid the mail # to be delivered to Spams because of origin check. sender = '%s <no-reply@%s>' % (config.company_name, vendor.domain) msg = EmailMessage(subject, html_content, sender, [member.email]) try: invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice) msg.attach_file(invoice_pdf_file) except: pass if paid_by_wallet_debit: msg.bcc = ['*****@*****.**'] msg.content_subtype = "html" invoice.last_reminder = timezone.now() try: if msg.send(): logger.debug("1st Invoice reminder for %s sent to %s" % (subscription.domain, member.email)) if not paid_by_wallet_debit: invoice.reminders_sent = 1 invoice.save() else: logger.error(u"Invoice #%s generated but mail not sent to %s" % (number, member.email), exc_info=True) except: logger.error(u"Connexion error on Invoice #%s to %s" % (number, member.email), exc_info=True) if sms_text: if member.phone: if config.sms_sending_method == Config.HTTP_API: send_sms(member.phone, sms_text) else: QueuedSMS.objects.create(recipient=member.phone, text=sms_text) path_after = getattr(settings, 'BILLING_AFTER_NEW_INVOICE', None) if path_after: after_new_invoice = import_by_path(path_after) after_new_invoice(invoice) try: connection.close() finally: if count > 0: report = SendingReport.objects.create(count=count, total_amount=total_amount) sudo_group = Group.objects.get(name=SUDO) add_event(vendor, INVOICES_SENT_EVENT, group_id=sudo_group.id, object_id=report.id)
def send_invoices(): """ This cron task simply sends the Invoice *invoicing_gap* days before Subscription *expiry* """ service = get_service_instance() config = service.config now = datetime.now() invoicing_config = InvoicingConfig.objects.all()[0] connection = mail.get_connection() try: connection.open() except: logger.error(u"Connexion error", exc_info=True) count, total_amount = 0, 0 reminder_date_time = now + timedelta(days=invoicing_config.gap) subscription_qs = Subscription.objects.filter(monthly_cost__gt=0, expiry=reminder_date_time.date()) logger.debug("%d CR Operators candidate for invoice issuance." % subscription_qs.count()) for subscription in subscription_qs: if subscription.plan.raw_monthly_cost == 0: continue cr_service = subscription.service member = cr_service.member number = get_next_invoice_number() months_count = get_billing_cycle_months_count(subscription.billing_cycle) amount = subscription.monthly_cost * months_count ikwen_price = subscription.monthly_cost hosting = IkwenInvoiceItem(label=_('Website hosting'), price=ikwen_price, amount=subscription.monthly_cost) short_description = _("Continuous Rewarding Program for %s" % cr_service.domain) entry = InvoiceEntry(item=hosting, short_description=short_description, quantity=months_count, total=amount) entries = [entry] invoice = Invoice.objects.create(subscription=subscription, amount=amount, number=number, due_date=subscription.expiry, months_count=months_count, entries=entries) count += 1 total_amount += amount add_event(service, NEW_INVOICE_EVENT, member=member, object_id=invoice.id) paid_by_wallet_debit = False if cr_service.balance >= invoice.amount: pay_with_wallet_balance(invoice) paid_by_wallet_debit = True logger.debug("CR Invoice for %s paid by wallet debit" % cr_service.domain) subject, message, sms_text = get_invoice_generated_message(invoice) if member.email: invoice_url = service.url + reverse('billing:invoice_detail', args=(invoice.id,)) if paid_by_wallet_debit: subject = _("Thanks for your payment") invoice_url = service.url + reverse('billing:invoice_detail', args=(invoice.id,)) context = {'wallet_debit': True, 'invoice': invoice, 'config': config, 'invoice_url': invoice_url, 'cta': _("View invoice")} html_content = get_mail_content(subject, '', template_name='billing/mails/notice.html', extra_context=context) else: html_content = get_mail_content(subject, message, template_name='billing/mails/notice.html', extra_context={'invoice_url': invoice_url, 'cta': _("Pay now")}) # Sender is simulated as being no-reply@company_name_slug.com to avoid the mail # to be delivered to Spams because of origin check. sender = '%s <no-reply@%s>' % (config.company_name, service.domain) msg = EmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" invoice.last_reminder = timezone.now() try: if msg.send(): logger.debug("1st Invoice reminder for %s sent to %s" % (cr_service.domain, member.email)) if not paid_by_wallet_debit: invoice.reminders_sent = 1 invoice.save() else: logger.error(u"Invoice #%s generated but mail not sent to %s" % (number, member.email), exc_info=True) except: logger.error(u"Connexion error on Invoice #%s to %s" % (number, member.email), exc_info=True) path_after = getattr(settings, 'BILLING_AFTER_NEW_INVOICE', None) if path_after: after_new_invoice = import_by_path(path_after) after_new_invoice(invoice) try: connection.close() finally: pass
def send_expiry_reminders(): """ This cron task simply sends the Invoice *invoicing_gap* days before Subscription *expiry* """ ikwen_service = get_service_instance() now = datetime.now() invoicing_config = InvoicingConfig.objects.all()[0] connection = mail.get_connection() try: connection.open() except: logger.error(u"Connexion error", exc_info=True) reminder_date_time = now + timedelta(days=invoicing_config.gap) for invoicing_config in InvoicingConfig.objects.exclude( service=ikwen_service): service = invoicing_config.service if service.status != Service.ACTIVE or invoicing_config.pull_invoice: continue db = service.database add_database(db) config = service.basic_config subscription_qs = Subscription.objects.using(db)\ .selected_related('member, product').filter(status=Subscription.ACTIVE, monthly_cost__gt=0, expiry=reminder_date_time.date()) count, total_amount = 0, 0 for subscription in subscription_qs: member = subscription.member number = get_next_invoice_number() months_count = get_billing_cycle_months_count( subscription.billing_cycle) amount = subscription.monthly_cost * months_count path_before = getattr(settings, 'BILLING_BEFORE_NEW_INVOICE', None) if path_before: before_new_invoice = import_by_path(path_before) val = before_new_invoice(subscription) if val is not None: # Returning a not None value cancels the generation of a new Invoice for this Service continue short_description = subscription.product.short_description item = InvoiceItem(label=_('Subscription'), amount=amount) entry = InvoiceEntry(item=item, short_description=short_description, quantity=months_count, total=amount) invoice = Invoice.objects.create(member=member, subscription=subscription, amount=amount, number=number, due_date=subscription.expiry, months_count=months_count, entries=[entry]) count += 1 total_amount += amount subject, message, sms_text = get_invoice_generated_message(invoice) balance, update = Balance.objects.using( WALLETS_DB_ALIAS).get_or_create(service_id=service.id) if member.email: if 0 < balance.mail_count < LOW_MAIL_LIMIT: notify_for_low_messaging_credit(service, balance) if balance.mail_count <= 0 and not getattr( settings, 'UNIT_TESTING', False): notify_for_empty_messaging_credit(service, balance) else: invoice_url = service.url + reverse( 'billing:invoice_detail', args=(invoice.id, )) html_content = get_mail_content( subject, message, service=service, template_name='billing/mails/notice.html', extra_context={ 'invoice_url': invoice_url, 'cta': _("Pay now"), 'currency': config.currency_symbol }) # Sender is simulated as being no-reply@company_name_slug.com to avoid the mail # to be delivered to Spams because of origin check. sender = '%s <no-reply@%s>' % (config.company_name, service.domain) msg = XEmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" msg.service = service invoice.last_reminder = timezone.now() try: with transaction.atomic(using=WALLETS_DB_ALIAS): if msg.send(): balance.mail_count -= 1 balance.save() logger.debug( "1st Invoice reminder for %s sent to %s" % (subscription, member.email)) else: logger.error( u"Invoice #%s generated but mail not sent to %s" % (number, member.email), exc_info=True) except: logger.error(u"Connexion error on Invoice #%s to %s" % (number, member.email), exc_info=True) if sms_text and member.phone: if 0 < balance.sms_count < LOW_SMS_LIMIT: notify_for_low_messaging_credit(service, balance) if balance.sms_count <= 0 and not getattr( settings, 'UNIT_TESTING', False): notify_for_empty_messaging_credit(service, balance) continue try: with transaction.atomic(using=WALLETS_DB_ALIAS): balance.sms_count -= 1 balance.save() phone = member.phone if len( member.phone) > 9 else '237' + member.phone send_sms(phone, sms_text, fail_silently=False) except: logger.error(u"SMS for invoice #%s not sent to %s" % (number, member.email), exc_info=True) path_after = getattr(settings, 'BILLING_AFTER_NEW_INVOICE', None) if path_after: after_new_invoice = import_by_path(path_after) after_new_invoice(invoice) if count > 0: report = SendingReport.objects.using(db).create( count=count, total_amount=total_amount) sudo_group = Group.objects.using(db).get(name=SUDO) add_event(ikwen_service, INVOICES_SENT_EVENT, group_id=sudo_group.id, object_id=report.id) try: connection.close() except: pass
def deploy(app, member, project_name, billing_plan, setup_cost, monthly_cost, invoice_entries, billing_cycle, domain=None, provider=None, partner_retailer=None): project_name_slug = slugify( project_name) # Eg: slugify('Cool Shop') = 'cool-shop' ikwen_name = project_name_slug.replace('-', '') # Eg: cool-shop --> 'coolshop' pname = ikwen_name i = 0 while True: try: Service.objects.using(UMBRELLA).get(project_name_slug=pname) i += 1 pname = "%s%d" % (ikwen_name, i) except Service.DoesNotExist: ikwen_name = pname break database = ikwen_name if not domain: domain = ikwen_name + '.kakocase.com' admin_url = domain + '/ikwen/dashboard' domain_type = Service.SUB if '.kakocase.com' in domain else Service.MAIN is_pro_version = billing_plan.is_pro_version now = datetime.now() expiry = now + timedelta(days=15) service = Service(member=member, app=app, project_name=project_name, project_name_slug=ikwen_name, domain=domain, database=database, url='http://' + domain, domain_type=domain_type, expiry=expiry, admin_url='http://' + admin_url, billing_plan=billing_plan, billing_cycle=billing_cycle, monthly_cost=monthly_cost, version=Service.TRIAL, retailer=partner_retailer) service.save(using=UMBRELLA) # Create a copy of template application in the Cloud folder app_folder = CLOUD_FOLDER + '000Tpl/AppSkeleton' website_home_folder = CLOUD_FOLDER + ikwen_name media_root = MEDIA_ROOT + ikwen_name + '/' if not os.path.exists(media_root): os.makedirs(media_root) media_url = MEDIA_URL + ikwen_name + '/' if os.path.exists(website_home_folder): shutil.rmtree(website_home_folder) shutil.copytree(app_folder, website_home_folder) # Re-create settings.py file as well as apache.conf file for the newly created project secret_key = generate_django_secret_key() api_signature = generate_random_key(20) allowed_hosts = '%s, www.%s' % (domain, domain) settings_tpl = get_template('billing/cloud_setup/settings.html') settings_context = Context({ 'secret_key': secret_key, 'database': database, 'service': service, 'static_root': STATIC_ROOT, 'static_url': STATIC_URL, 'media_root': media_root, 'media_url': media_url, 'app_folder': app_folder, 'allowed_hosts': allowed_hosts, 'debug': getattr(settings, 'DEBUG', False) }) fh = open(website_home_folder + '/conf/settings.py', 'w') fh.write(settings_tpl.render(settings_context)) fh.close() if service.id not in member.collaborates_on_fk_list: member.collaborates_on_fk_list.append(service.id) if service not in member.customer_on_fk_list: member.customer_on.append(service.id) member.is_iao = True member.save(using=UMBRELLA) # Import template database and set it up subprocess.call( ['mongorestore', '-d', database, CLOUD_FOLDER + '000Tpl/DB']) add_database_to_settings(database) member.is_bao = True member.is_staff = True member.is_superuser = True app.save(using=database) member.save(using=database) # Copy payment means to local database for mean in PaymentMean.objects.using(UMBRELLA).all(): if mean.slug == MTN_MOMO: mean.is_main = True mean.is_active = True else: mean.is_main = False if is_pro_version: mean.is_active = True else: mean.is_active = False mean.save(using=database) FlatPage.objects.using(database).create(url=FlatPage.AGREEMENT, title=FlatPage.AGREEMENT) FlatPage.objects.using(database).create(url=FlatPage.LEGAL_MENTIONS, title=FlatPage.LEGAL_MENTIONS) for group in Group.objects.using(database).all(): try: gpl = GroupPermissionList.objects.get(group=group) group.id = None group.save(using=database ) # Recreate the group in the service DB with a new id. gpl.group = group # And update GroupPermissionList object with the newly re-created group gpl.save(using=database) except GroupPermissionList.DoesNotExist: group.id = None group.save(using=database ) # Re-create the group in the service DB with anyway. # Add member to SUDO Group sudo_group = Group.objects.using(database).get(name=SUDO) obj_list, created = UserPermissionList.objects.using( database).get_or_create(user=member) obj_list.group_fk_list.append(sudo_group.id) obj_list.save(using=database) mail_signature = "%s<br>" \ "<a href='%s'>%s</a>" % (project_name, 'http://' + domain, domain) config = OperatorProfile(service=service, api_signature=api_signature, ikwen_share_fixed=billing_plan.tx_share_fixed, ikwen_share_rate=billing_plan.tx_share_rate, is_pro_version=is_pro_version, signature=mail_signature, max_customers=billing_plan.max_objects) config.save(using=UMBRELLA) base_config = config.get_base_config() base_config.save(using=UMBRELLA) if partner_retailer: partner_retailer.save(using=database) service.save(using=database) config.save(using=database) InvoicingConfig.objects.using(database).create() # Apache Server setup if getattr(settings, 'LOCAL_DEV'): apache_tpl = get_template('core/cloud_setup/apache.conf.local.html') else: apache_tpl = get_template('core/cloud_setup/apache.conf.html') apache_context = Context({'ikwen_name': ikwen_name}) fh = open(website_home_folder + '/apache.conf', 'w') fh.write(apache_tpl.render(apache_context)) fh.close() subprocess.call([ 'sudo', 'ln', '-sf', website_home_folder + '/apache.conf', '/etc/apache2/sites-enabled/' + domain + '.conf' ]) subprocess.call(['sudo', 'service', 'apache2', 'reload']) # Send notification and Invoice to customer number = get_next_invoice_number() now = datetime.now() invoice_total = 0 for entry in invoice_entries: invoice_total += entry.item.amount * entry.quantity invoice = Invoice(subscription=service, amount=invoice_total, number=number, due_date=expiry, last_reminder=now, reminders_sent=1, is_one_off=True, entries=invoice_entries, months_count=billing_plan.setup_months_count) invoice.save(using=UMBRELLA) vendor = get_service_instance() add_event(vendor, NEW_INVOICE_EVENT, member=member, object_id=invoice.id) if partner_retailer: partner_profile = PartnerProfile.objects.using(UMBRELLA).get( service=partner_retailer) member.is_bao = False member.is_staff = False member.is_superuser = False member.save(using='default') service.save(using='default') config.save(using='default') sender = '%s <no-reply@%s>' % (partner_profile.company_name, partner_retailer.domain) else: sender = 'IKWEN <*****@*****.**>' invoice_url = 'http://www.ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id, )) subject = _("Your Web Billing Platform %s was created" % project_name) html_content = get_mail_content( subject, '', template_name='core/mails/service_deployed.html', extra_context={ 'service_activated': service, 'invoice': invoice, 'member': member, 'invoice_url': invoice_url }) msg = EmailMessage(subject, html_content, sender, [member.email]) msg.content_subtype = "html" Thread(target=lambda m: m.send(), args=(msg, )).start() return service