Example #1
0
 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)
Example #2
0
    def save_model(self, request, obj, form, change):
        # Send e-mail for manually generated Invoice upon creation
        super(SubscriptionAdmin, self).save_model(request, obj, form, change)
        if change:
            return

        # Send e-mail only if e-mail is a valid one. It will be agreed that if a client
        # does not have an e-mail. we create a fake e-mail that contains his phone number.
        # So e-mail containing phone number are invalid.
        member = obj.member
        service = get_service_instance()
        config = service.config
        subject, message, sms_text = get_subscription_registered_message(obj)

        # This helps differentiates from fake email accounts created as [email protected]
        if member.email.find(member.phone) < 0:
            add_event(service, SUBSCRIPTION_EVENT, member=member, object_id=obj.id, model=subscription_model_name)
            html_content = get_mail_content(subject, message, template_name='billing/mails/notice.html')
            # 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"
            Thread(target=lambda m: m.send(), args=(msg,)).start()
        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)
Example #3
0
def suspend_customers_services():
    """
    This cron task shuts down service and sends notice of Service suspension
    for Invoices which tolerance is exceeded.
    """
    vendor, config, invoicing_config, connection = _init_base_vars()
    now = timezone.now()
    count, total_amount = 0, 0
    deadline = now - timedelta(days=invoicing_config.tolerance)
    invoice_qs = Invoice.objects.filter(due_date__lte=deadline, status=Invoice.OVERDUE)
    print ("%d invoice(s) candidate for service suspension." % invoice_qs.count())
    for invoice in invoice_qs:
        subscription = invoice.subscription
        if not subscription:
            continue
        if getattr(settings, 'IS_IKWEN', False):
            if subscription.version == Service.FREE:
                continue
            _set_actual_vendor(subscription, vendor, config)
        invoice.status = Invoice.EXCEEDED
        invoice.save()
        action = getattr(settings, 'SERVICE_SUSPENSION_ACTION', None)
        if action:
            count += 1
            total_amount += invoice.amount
            action = import_by_path(action)
            try:
                action(subscription)
            except:
                logger.error("Error while processing subscription %s" % str(subscription), exc_info=True)
                continue
            member = subscription.member
            add_event(vendor, SERVICE_SUSPENDED_EVENT, member=member, object_id=invoice.id)
            subject, message, sms_text = get_service_suspension_message(invoice)
            if member.email:
                msg = _get_email_msg(member, subject, message, invoice, vendor, config)
                print ("Sending mail to %s" % member.email)
                try:
                    if msg.send():
                        print ("Mail sent to %s" % member.email)
                    else:
                        print ("Sending mail to %s failed" % member.email)
                        logger.error(u"Notice of suspension for Invoice #%s not sent to %s" % (invoice.number, member.email), exc_info=True)
                except:
                    print ("Sending mail to %s failed" % member.email)
                    logger.error(u"Connexion error on Invoice #%s to %s" % (invoice.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)
    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, SUSPENSION_NOTICES_SENT_EVENT, group_id=sudo_group.id, object_id=report.id)
Example #4
0
def send_invoice_overdue_notices():
    """
    This cron task sends notice of Invoice overdue
    """
    vendor, config, invoicing_config, connection = _init_base_vars()
    now = timezone.now()
    count, total_amount = 0, 0
    invoice_qs = Invoice.objects.filter(Q(status=Invoice.PENDING) | Q(status=Invoice.OVERDUE),
                                        due_date__lt=now, overdue_notices_sent__lt=3)
    print ("%d invoice(s) candidate for overdue notice." % invoice_qs.count())
    for invoice in invoice_qs:
        subscription = invoice.subscription
        if subscription and getattr(settings, 'IS_IKWEN', False):
            if subscription.version == Service.FREE:
                continue
            _set_actual_vendor(subscription, vendor, config)
        if invoice.last_overdue_notice:
            diff = now - invoice.last_overdue_notice
        else:
            invoice.status = Invoice.OVERDUE
            invoice.save()
        if not invoice.last_overdue_notice or diff.days == invoicing_config.overdue_delay:
            print ("Processing invoice for Service %s" % str(invoice.subscription))
            count += 1
            total_amount += invoice.amount
            member = invoice.subscription.member
            add_event(vendor, OVERDUE_NOTICE_EVENT, member=member, object_id=invoice.id)
            subject, message, sms_text = get_invoice_overdue_message(invoice)
            if member.email:
                msg = _get_email_msg(member, subject, message, invoice, vendor, config)
                invoice.last_overdue_notice = timezone.now()
                print ("Sending mail to %s" % member.email)
                try:
                    if msg.send():
                        print ("Mail sent to %s" % member.email)
                        invoice.overdue_notices_sent += 1
                    else:
                        print ("Sending mail to %s failed" % member.email)
                        logger.error(u"Overdue notice for Invoice #%s not sent to %s" % (invoice.number, member.email), exc_info=True)
                except:
                    print ("Sending mail to %s failed" % member.email)
                    logger.error(u"Connexion error on Invoice #%s to %s" % (invoice.number, member.email), exc_info=True)
                invoice.save()
            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)
    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, OVERDUE_NOTICES_SENT_EVENT, group_id=sudo_group.id, object_id=report.id)
Example #5
0
def send_invoice_reminders():
    """
    This cron task sends Invoice reminder notice to the client if unpaid
    """
    vendor, config, invoicing_config, connection = _init_base_vars()
    now = timezone.now()
    count, total_amount = 0, 0
    invoice_qs = Invoice.objects.filter(status=Invoice.PENDING, due_date__gte=now.date(), last_reminder__isnull=False)
    print ("%d invoice(s) candidate for reminder." % invoice_qs.count())
    for invoice in invoice_qs:
        subscription = invoice.subscription
        if getattr(settings, 'IS_IKWEN', False):
            if subscription.version == Service.FREE:
                continue
            _set_actual_vendor(subscription, vendor, config)
        diff = now - invoice.last_reminder
        if diff.days == invoicing_config.reminder_delay:
            print ("Processing invoice for Service %s" % str(invoice.subscription))
            count += 1
            total_amount += invoice.amount
            member = invoice.member
            add_event(vendor, INVOICE_REMINDER_EVENT, member=member, object_id=invoice.id)
            subject, message, sms_text = get_invoice_reminder_message(invoice)
            if member.email:
                msg = _get_email_msg(member, subject, message, invoice, vendor, config)
                invoice.last_reminder = timezone.now()
                print ("Sending mail to %s" % member.email)
                try:
                    if msg.send():
                        print ("Mail sent to %s" % member.email)
                        invoice.reminders_sent += 1
                    else:
                        print ("Sending mail to %s failed" % member.email)
                        logger.error(u"Reminder mail for Invoice #%s not sent to %s" % (invoice.number, member.email), exc_info=True)
                except:
                    print ("Sending mail to %s failed" % member.email)
                    logger.error(u"Connexion error on Invoice #%s to %s" % (invoice.number, member.email), exc_info=True)
                invoice.save()
            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)
    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, REMINDERS_SENT_EVENT, group_id=sudo_group.id, object_id=report.id)
Example #6
0
 def get(self, request, *args, **kwargs):
     action = request.GET.get('action')
     if action == 'invite_school':  # Send a request to see his children online to the school
         service = get_service_instance()
         member = request.user
         school_id = request.GET['school_id']
         kids_details = request.GET['kids_details']
         school = Service.objects.get(pk=school_id)
         db = school.database
         add_database(db)
         event_type, change = EventType.objects.using(db) \
             .get_or_create(codename=PARENT_REQUEST_KID, renderer='ikwen_foulassi.foulassi.events.render_parent_request_kid')
         kid_request = KidRequest.objects.using(db).create(parent=member, kids_details=kids_details)
         Event.objects.using(db).create(type=event_type, object_id_list=[kid_request.id])
         try:
             if member.gender == FEMALE:
                 parent_name = _("Mrs %s" % member.full_name)
             elif member.gender == MALE:
                 parent_name = _("Mr %s" % member.full_name)
             else:  # Unknown gender
                 parent_name = _("The parent %s" % member.full_name)
             subject = _("I would like to follow my kids in your school on Foulassi.")
             cta_url = school.url + reverse('foulassi:event_list')
             html_content = get_mail_content(subject, template_name='foulassi/mails/invite_school.html',
                                             extra_context={'parent': member, 'kids_details': kids_details,
                                                            'IKWEN_MEDIA_URL': MEDIA_URL, 'MEMBER_AVATAR': MEMBER_AVATAR,
                                                            'cta_url': cta_url})
             sender = '%s via ikwen Foulassi <no-reply@%s>' % (parent_name, service.domain)
             msg = EmailMessage(subject, html_content, sender, [school.config.contact_email.strip()])
             msg.content_subtype = "html"
             msg.cc = ["*****@*****.**"]
             if member.email:
                 msg.extra_headers = {'Reply-To': member.email}
             Thread(target=lambda m: m.send(), args=(msg,)).start()
             sms_text = _("%(parent_name)s would like to follow his kid(s) below on ikwen Foulassi:\n"
                          "%(kids_details)s" % {'parent_name': parent_name, 'kids_details': kids_details})
             if member.phone:
                 if len(member.phone) == 9:
                     member.phone = '237' + member.phone
                 send_sms(member.phone, sms_text)
         except:
             pass
         return HttpResponse(json.dumps({'success': True}, 'content-type: text/json'))
     return super(SearchSchool, self).get(request, *args, **kwargs)
Example #7
0
def send_billed_sms(weblet, parent_list, text):
    config = weblet.config
    balance = check_messaging_balance(weblet)
    for parent in parent_list:
        member = parent.member
        phone = member.phone
        activate(member.language)
        page_count = count_pages(text)
        if balance.sms_count < page_count:
            break
        phone = slugify(phone).replace('-', '')
        if len(phone) == 9:
            phone = '237' + phone
        try:
            with db_transaction.atomic(using='wallets'):
                balance.sms_count -= page_count
                balance.save()
                send_sms(phone, text, get_sms_label(config))
        except:
            pass
Example #8
0
    def send_code(self, request, new_code=False):
        service = get_service_instance()
        member = request.user
        code = ''.join(
            [random.SystemRandom().choice(string.digits) for _ in range(4)])
        do_send = False
        try:
            current = request.session[
                'code']  # Test whether there's a pending code in session
            if new_code:
                request.session['code'] = code
                do_send = True
        except KeyError:
            request.session['code'] = code
            do_send = True

        if do_send:
            phone = slugify(member.phone).replace('-', '')
            if len(phone) == 9:
                phone = '237' + phone  # This works only for Cameroon
            text = 'Your %s confirmation code is %s' % (service.project_name,
                                                        code)
            try:
                main_link = service.config.sms_api_script_url
                if not main_link:
                    main_link = getattr(settings, 'SMS_MAIN_LINK', None)
                send_sms(phone,
                         text,
                         script_url=main_link,
                         fail_silently=False)
            except:
                fallback_link = getattr(settings, 'SMS_FALLBACK_LINK', None)
                send_sms(phone,
                         text,
                         script_url=fallback_link,
                         fail_silently=False)
Example #9
0
def notify_profiles(debug=False):
    t0 = datetime.now()
    total_revival, total_mail, total_sms = 0, 0, 0
    logger.debug("Starting cyclic revival")
    today = t0.date()
    queryset = CyclicRevival.objects.select_related('service')\
        .filter(next_run_on=today, hour_of_sending=t0.hour, end_on__gt=today, is_active=True)
    for revival in queryset:
        try:
            refreshed = CyclicRevival.objects.get(pk=revival.id)
            if refreshed.is_running:
                continue
            refreshed.is_running = True
            refreshed.save()
            total_revival += 1
        except CyclicRevival.DoesNotExist:
            continue
        service = revival.service
        db = service.database
        add_database(db)
        balance = Balance.objects.using(WALLETS_DB_ALIAS).get(
            service_id=service.id)
        if balance.mail_count == 0 and balance.sms_count == 0:
            try:
                notify_for_empty_messaging_credit(service, balance)
            except:
                revival.is_running = False
                revival.save()
                logger.error(
                    "Failed to notify %s for empty messaging credit." %
                    service,
                    exc_info=True)
            continue
        if 0 < balance.mail_count < LOW_MAIL_LIMIT or 0 < balance.sms_count < LOW_SMS_LIMIT:
            try:
                notify_for_low_messaging_credit(service, balance)
            except:
                revival.is_running = False
                revival.save()
                logger.error("Failed to notify %s for low messaging credit." %
                             service,
                             exc_info=True)

        label = get_sms_label(service.config)
        notified_empty_mail_credit = False
        notified_empty_sms_credit = False
        if debug:
            member_queryset = Member.objects.using(db).filter(
                is_superuser=True)
        else:
            member_queryset = Member.objects.using(db).all()
        total = member_queryset.count()
        try:
            profile_tag = ProfileTag.objects.using(db).get(
                pk=revival.profile_tag_id)
        except ProfileTag.DoesNotExist:
            revival.delete()
            continue
        set_counters(profile_tag)

        revival_local = CyclicRevival.objects.using(db).get(pk=revival.id)
        chunks = total / 500 + 1
        for i in range(chunks):
            start = i * 500
            finish = (i + 1) * 500
            for member in member_queryset.order_by(
                    'date_joined')[start:finish]:
                try:
                    profile = MemberProfile.objects.using(db).get(
                        member=member)
                except MemberProfile.DoesNotExist:
                    continue
                match = set(profile.tag_fk_list) & {profile_tag.id}
                if len(match) > 0:
                    if member.email:
                        CyclicTarget.objects.using(db).get_or_create(
                            revival=revival_local, member=member)
        revival.set_next_run_date()

        connection = mail.get_connection()
        try:
            connection.open()
        except:
            logger.error(u"Connexion error", exc_info=True)
            break

        logger.debug("Running revival %s for %s" %
                     (revival.mail_subject, revival.service))
        for target in revival_local.cyclictarget_set.select_related('member'):
            member = target.member
            if member.language:
                activate(member.language)
            else:
                activate('en')
            subject = revival.mail_subject
            message = revival.mail_content.replace('$client',
                                                   member.first_name)
            sender = '%s <no-reply@%s>' % (service.project_name,
                                           service.domain)
            try:
                currency = Currency.objects.using(using=db).get(is_base=True)
            except Currency.DoesNotExist:
                currency = None
            product_list = []
            if service.app.slug == 'kakocase':
                product_list = Product.objects.using(db).filter(
                    pk__in=revival.items_fk_list)
            extra_context = {
                'revival':
                revival,
                'currency':
                currency,
                'media_url':
                getattr(settings, 'CLUSTER_MEDIA_URL') +
                service.project_name_slug + '/',
                'product_list':
                product_list
            }
            try:
                html_content = get_mail_content(
                    subject,
                    message,
                    template_name='revival/mails/default.html',
                    service=service,
                    extra_context=extra_context)
            except:
                logger.error(
                    "Could not render mail for member %s, Cyclic revival on %s"
                    % (member.username, profile_tag),
                    exc_info=True)
                break
            msg = XEmailMessage(subject, html_content, sender, [member.email])
            msg.content_subtype = "html"
            msg.type = XEmailObject.REVIVAL

            if balance.mail_count == 0 and not notified_empty_mail_credit:
                notify_for_empty_messaging_credit(service, balance)
                notified_empty_mail_credit = True
            else:
                try:
                    with transaction.atomic(using=WALLETS_DB_ALIAS):
                        if not debug:
                            balance.mail_count -= 1
                            balance.save()
                        if msg.send():
                            target.revival_count += 1
                            target.save()
                            total_mail += 1
                            increment_history_field(
                                profile_tag, 'cyclic_revival_mail_history')
                        else:
                            logger.error(
                                "Cyclic revival with subject %s not sent for member %s"
                                % (subject, member.email),
                                exc_info=True)
                except:
                    logger.error(
                        "Critical error in revival %s when processing Mail sending for member %s"
                        % (revival.id, member.email),
                        exc_info=True)
            if revival.sms_text:
                if balance.sms_count == 0 and not notified_empty_sms_credit:
                    notify_for_empty_messaging_credit(service, balance)
                    notified_empty_sms_credit = True
                else:
                    sms_text = revival.sms_text.replace(
                        '$client', member.first_name)
                    page_count = count_pages(sms_text)
                    try:
                        with transaction.atomic(using=WALLETS_DB_ALIAS):
                            balance.sms_count -= page_count
                            balance.save()
                            send_sms(recipient=member.phone,
                                     text=sms_text,
                                     fail_silently=False)
                            total_sms += 1
                            increment_history_field(
                                profile_tag, 'cyclic_revival_sms_history')
                            SMSObject.objects.create(recipient=member.phone,
                                                     text=sms_text,
                                                     label=label)
                    except:
                        logger.error(
                            "Critical error in revival %s when processing SMS sending for member %s"
                            % (revival.id, member.email),
                            exc_info=True)
            if balance.mail_count == 0 and balance.sms_count == 0:
                break
        revival.is_running = False
        revival.save()
        try:
            connection.close()
        except:
            pass
        diff = datetime.now() - t0
        logger.debug("%d revivals run. %d mails and %d SMS sent in %s" %
                     (total_revival, total_mail, total_sms, diff))
Example #10
0
def send_order_confirmation_sms(buyer_name, buyer_phone, order):
    service = get_service_instance()
    config = service.config
    script_url = getattr(settings, 'SMS_API_SCRIPT_URL', config.sms_api_script_url)
    if not script_url:
        return
    details_max_length = 90
    details = order.get_products_as_string()
    if len(details) > details_max_length:
        tokens = details.split(',')
        while len(details) > details_max_length:
            tokens = tokens[:-1]
            details = ','.join(tokens)
        details += ' ...'
    client_text = _("Order successful:\n"
                    "%(details)s\n"
                    "Your RCC is %(rcc)s\n"
                    "Thank you." % {'details': details, 'rcc': order.rcc.upper()})
    iao_text = "Order from %(buyer_name)s:\n" \
               "%(details)s\n" \
               "RCC: %(rcc)s" % {'buyer_name': buyer_name[:20], 'details': details, 'rcc': order.rcc.upper()}

    iao_phones = [phone.strip() for phone in config.notification_phone.split(',') if phone.strip()]
    client_page_count = count_pages(client_text)
    iao_page_count = count_pages(iao_text)
    needed_credit = client_page_count + iao_page_count * len(iao_phones)
    balance, update = Balance.objects.using(WALLETS_DB_ALIAS).get_or_create(service_id=service.id)
    if needed_credit < balance.mail_count < LOW_SMS_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.sms_count < needed_credit:
        try:
            notify_for_empty_messaging_credit(service, balance)
        except:
            logger.error("Failed to notify %s for empty messaging credit." % service, exc_info=True)
        return
    buyer_phone = buyer_phone.strip()
    buyer_phone = slugify(buyer_phone).replace('-', '')
    if buyer_phone and len(buyer_phone) == 9:
        buyer_phone = '237' + buyer_phone  # This works only for Cameroon
    try:
        with transaction.atomic(using=WALLETS_DB_ALIAS):
            balance.sms_count -= client_page_count
            balance.save()
            send_sms(buyer_phone, client_text, script_url=script_url, fail_silently=False)
    except:
        pass

    for phone in iao_phones:
        phone = slugify(phone).replace('-', '')
        if len(phone) == 9:
            phone = '237' + phone
        try:
            with transaction.atomic(using=WALLETS_DB_ALIAS):
                balance.sms_count -= iao_page_count
                balance.save()
                send_sms(phone, iao_text, script_url=script_url, fail_silently=False)
        except:
            pass
Example #11
0
    def run_test(self, request):
        from echo.utils import count_pages
        from echo.models import Balance, SMSObject
        revival_id = request.GET['revival_id']
        test_email_list = request.GET['test_email_list'].split(',')
        test_phone_list = request.GET['test_phone_list'].split(',')
        service = get_service_instance()
        revival = CyclicRevival.objects.using(UMBRELLA).get(pk=revival_id)
        balance = Balance.objects.using(WALLETS_DB_ALIAS).get(
            service_id=service.id)
        if balance.mail_count == 0 and balance.sms_count == 0:
            response = {'error': 'Insufficient Email and SMS credit'}
            return HttpResponse(json.dumps(response))

        connection = mail.get_connection()
        try:
            connection.open()
        except:
            response = {
                'error':
                'Failed to connect to mail server. Please check your internet'
            }
            return HttpResponse(json.dumps(response))
        config = service.config

        warning = []
        for email in test_email_list:
            if balance.mail_count == 0:
                warning.append('Insufficient email Credit')
                break
            email = email.strip()
            subject = revival.mail_subject
            try:
                member = Member.objects.filter(email=email)[0]
                message = revival.mail_content.replace('$client',
                                                       member.first_name)
            except:
                message = revival.mail_content.replace('$client',
                                                       _("<Unknown>"))
            sender = '%s <no-reply@%s>' % (config.company_name, service.domain)
            media_url = ikwen_settings.CLUSTER_MEDIA_URL + service.project_name_slug + '/'
            product_list = []
            if service.app.slug == 'kakocase':
                product_list = Product.objects.filter(
                    pk__in=revival.items_fk_list)
            try:
                currency = Currency.objects.get(is_base=True)
            except Currency.DoesNotExist:
                currency = None
            html_content = get_mail_content(
                subject,
                message,
                template_name='revival/mails/default.html',
                extra_context={
                    'media_url': media_url,
                    'product_list': product_list,
                    'revival': revival,
                    'currency': currency
                })
            msg = EmailMessage(subject, html_content, sender, [email])
            msg.content_subtype = "html"
            with transaction.atomic(using='wallets'):
                try:
                    balance.mail_count -= 1
                    balance.save()
                    if not msg.send():
                        transaction.rollback(using='wallets')
                        warning.append('Mail not sent to %s' % email)
                except:
                    transaction.rollback(using='wallets')
        try:
            connection.close()
        except:
            pass

        if revival.sms_text:
            label = get_sms_label(config)
            for phone in test_phone_list:
                if balance.sms_count == 0:
                    warning.append('Insufficient SMS Credit')
                    break
                try:
                    member = Member.objects.filter(phone=phone)[0]
                    sms_text = revival.sms_text.replace(
                        '$client', member.first_name)
                except:
                    sms_text = revival.mail_content.replace('$client', "")
                page_count = count_pages(sms_text)
                with transaction.atomic():
                    try:
                        balance.sms_count -= page_count
                        balance.save()
                        phone = phone.strip()
                        if len(phone) == 9:
                            phone = '237' + phone
                        send_sms(recipient=phone,
                                 text=revival.sms_text,
                                 fail_silently=False)
                        SMSObject.objects.create(recipient=phone,
                                                 text=revival.sms_text,
                                                 label=label)
                    except:
                        transaction.rollback()
                        warning.append('SMS not sent to %s' % phone)
        response = {'success': True, 'warning': warning}
        return HttpResponse(json.dumps(response))
Example #12
0
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)
Example #13
0
def suspend_customers_services():
    """
    This cron task shuts down service and sends notice of Service suspension
    for Invoices which tolerance is exceeded.
    """
    vendor = get_service_instance()
    config = vendor.config
    now = timezone.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
    deadline = now - timedelta(days=invoicing_config.tolerance)
    invoice_qs = Invoice.objects.filter(due_date__lte=deadline,
                                        status=Invoice.OVERDUE)
    print("%d invoice(s) candidate for service suspension." %
          invoice_qs.count())
    for invoice in invoice_qs:
        subscription = invoice.subscription
        if not subscription:
            continue
        if getattr(settings, 'IS_IKWEN', False):
            if subscription.version == Service.FREE:
                continue
            if subscription.retailer:
                vendor = subscription.retailer
                config = vendor.config
        invoice.status = Invoice.EXCEEDED
        invoice.save()
        action = getattr(settings, 'SERVICE_SUSPENSION_ACTION', None)
        if action:
            count += 1
            total_amount += invoice.amount
            action = import_by_path(action)
            try:
                action(subscription)
            except:
                logger.error("Error while processing subscription %s" %
                             str(subscription),
                             exc_info=True)
                continue
            member = subscription.member
            add_event(vendor,
                      SERVICE_SUSPENDED_EVENT,
                      member=member,
                      object_id=invoice.id)
            subject, message, sms_text = get_service_suspension_message(
                invoice)
            if member.email:
                activate(member.language)
                invoice_url = 'http://ikwen.com' + reverse(
                    'billing:invoice_detail', args=(invoice.id, ))
                html_content = get_mail_content(
                    subject,
                    message,
                    template_name='billing/mails/notice.html',
                    extra_context={
                        'member_name': member.first_name,
                        'invoice': invoice,
                        '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
                    })
                # 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])
                msg.content_subtype = "html"
                print("Sending mail to %s" % member.email)
                try:
                    if msg.send():
                        print("Mail sent to %s" % member.email)
                    else:
                        print("Sending mail to %s failed" % member.email)
                        logger.error(
                            u"Notice of suspension for Invoice #%s not sent to %s"
                            % (invoice.number, member.email),
                            exc_info=True)
                except:
                    print("Sending mail to %s failed" % member.email)
                    logger.error(u"Connexion error on Invoice #%s to %s" %
                                 (invoice.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)
    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,
                      SUSPENSION_NOTICES_SENT_EVENT,
                      group_id=sudo_group.id,
                      object_id=report.id)
Example #14
0
def send_invoice_overdue_notices():
    """
    This cron task sends notice of Invoice overdue
    """
    vendor = get_service_instance()
    config = vendor.config
    now = timezone.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
    invoice_qs = Invoice.objects.filter(Q(status=Invoice.PENDING)
                                        | Q(status=Invoice.OVERDUE),
                                        due_date__lt=now,
                                        overdue_notices_sent__lt=3)
    print("%d invoice(s) candidate for overdue notice." % invoice_qs.count())
    for invoice in invoice_qs:
        subscription = invoice.subscription
        if subscription and getattr(settings, 'IS_IKWEN', False):
            if subscription.version == Service.FREE:
                continue
            if subscription.retailer:
                vendor = subscription.retailer
                config = vendor.config
        if invoice.last_overdue_notice:
            diff = now - invoice.last_overdue_notice
        else:
            invoice.status = Invoice.OVERDUE
            invoice.save()
        if not invoice.last_overdue_notice or diff.days == invoicing_config.overdue_delay:
            print("Processing invoice for Service %s" %
                  str(invoice.subscription))
            count += 1
            total_amount += invoice.amount
            member = invoice.subscription.member
            add_event(vendor,
                      OVERDUE_NOTICE_EVENT,
                      member=member,
                      object_id=invoice.id)
            subject, message, sms_text = get_invoice_overdue_message(invoice)
            if member.email:
                activate(member.language)
                invoice_url = 'http://ikwen.com' + reverse(
                    'billing:invoice_detail', args=(invoice.id, ))
                html_content = get_mail_content(
                    subject,
                    message,
                    template_name='billing/mails/notice.html',
                    extra_context={
                        'member_name': member.first_name,
                        'invoice': invoice,
                        '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
                    })
                # 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])
                msg.content_subtype = "html"
                invoice.last_overdue_notice = timezone.now()
                print("Sending mail to %s" % member.email)
                try:
                    if msg.send():
                        print("Mail sent to %s" % member.email)
                        invoice.overdue_notices_sent += 1
                    else:
                        print("Sending mail to %s failed" % member.email)
                        logger.error(
                            u"Overdue notice for Invoice #%s not sent to %s" %
                            (invoice.number, member.email),
                            exc_info=True)
                except:
                    print("Sending mail to %s failed" % member.email)
                    logger.error(u"Connexion error on Invoice #%s to %s" %
                                 (invoice.number, member.email),
                                 exc_info=True)
                invoice.save()
            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)
    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,
                      OVERDUE_NOTICES_SENT_EVENT,
                      group_id=sudo_group.id,
                      object_id=report.id)
Example #15
0
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
Example #16
0
def suspend_subscriptions():
    """
    This cron task shuts down service and sends notice of Service suspension
    for Invoices which tolerance is exceeded.
    """
    ikwen_service = get_service_instance()
    now = datetime.now()
    connection = mail.get_connection()
    try:
        connection.open()
    except:
        logger.error(u"Connexion error", exc_info=True)

    for invoicing_config in InvoicingConfig.objects.all():
        service = invoicing_config.service
        if service.status != Service.ACTIVE:
            continue
        config = service.basic_config
        db = service.database
        add_database(db)
        deadline = now - timedelta(days=invoicing_config.tolerance)
        invoice_qs = Invoice.objects.using(db).select_related('subscription')\
            .filter(due_date__lte=deadline, status=Invoice.OVERDUE)
        count, total_amount = 0, 0
        for invoice in invoice_qs:
            due_date = invoice.due_date
            due_datetime = datetime(due_date.year, due_date.month,
                                    due_date.day)
            diff = now - due_datetime
            subscription = invoice.subscription
            tolerance = subscription.tolerance
            if diff.days < tolerance:
                continue
            invoice.status = Invoice.EXCEEDED
            invoice.save()
            count += 1
            total_amount += invoice.amount
            subscription.status = Subscription.SUSPENDED
            subscription.save()
            member = subscription.member
            add_event(service,
                      SERVICE_SUSPENDED_EVENT,
                      member=member,
                      object_id=invoice.id)
            subject, message, sms_text = get_service_suspension_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={
                            'member_name': member.first_name,
                            'invoice': invoice,
                            '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.service = service
                    msg.content_subtype = "html"
                    try:
                        with transaction.atomic(using=WALLETS_DB_ALIAS):
                            if msg.send():
                                balance.mail_count -= 1
                                balance.save()
                            else:
                                logger.error(
                                    u"Notice of suspension for Invoice #%s not sent to %s"
                                    % (invoice.number, member.email),
                                    exc_info=True)
                    except:
                        print "Sending mail to %s failed" % member.email
                        logger.error(u"Connexion error on Invoice #%s to %s" %
                                     (invoice.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 overdue notice for invoice #%s not sent to %s" %
                        (invoice.number, member.phone),
                        exc_info=True)

        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,
                      SUSPENSION_NOTICES_SENT_EVENT,
                      group_id=sudo_group.id,
                      object_id=report.id)

    try:
        connection.close()
    except:
        pass
Example #17
0
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        total_amount = 0
        instance = None
        for instance in instances:
            if isinstance(instance, Payment):
                total_amount += instance.amount
                if instance.cashier:
                    # Instances with non null cashier are those that previously existed.
                    # setting them to None allows to ignore them at the end of the loop
                    # since we want to undertake action only for newly added Payment
                    instance = None
                    continue
                instance.cashier = request.user
                instance.save()
        if instance:
            # TODO: Check if the last payment is newly added
            # Notice email is sent only for the last saved Payment,
            # this is why this code is not run within the "for" loop above
            member = instance.invoice.subscription.member
            service = get_service_instance()
            config = service.config
            invoice = instance.invoice
            s = invoice.service
            days = get_days_count(invoice.months_count)
            if s.status == Service.SUSPENDED:
                invoicing_config = get_invoicing_config_instance()
                days -= invoicing_config.tolerance  # Catch-up days that were offered before service suspension
                expiry = datetime.now() + timedelta(days=days)
                expiry = expiry.date()
            elif s.expiry:
                expiry = s.expiry + timedelta(days=days)
            else:
                expiry = datetime.now() + timedelta(days=days)
                expiry = expiry.date()
            s.expiry = expiry
            s.status = Service.ACTIVE
            if invoice.is_one_off:
                s.version = Service.FULL
            s.save()
            share_payment_and_set_stats(invoice, invoice.months_count)

            if s.retailer:
                db = s.retailer.database
                Service.objects.using(db).filter(pk=s.id).update(
                    expiry=s.expiry, status=s.status, version=s.version)

            sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO)
            add_event(service,
                      PAYMENT_CONFIRMATION,
                      group_id=sudo_group.id,
                      object_id=invoice.id)
            add_event(service,
                      PAYMENT_CONFIRMATION,
                      member=member,
                      object_id=invoice.id)
            subject, message, sms_text = get_payment_confirmation_message(
                instance, member)
            if member.email:
                try:
                    currency = Currency.active.default().symbol
                except:
                    currency = config.currency_code
                invoice_url = service.url + reverse('billing:invoice_detail',
                                                    args=(invoice.id, ))
                subject, message, sms_text = get_payment_confirmation_message(
                    instance, member)
                html_content = get_mail_content(
                    subject,
                    message,
                    template_name='billing/mails/notice.html',
                    extra_context={
                        'member_name': member.first_name,
                        'invoice': invoice,
                        'cta': _("View invoice"),
                        'invoice_url': invoice_url,
                        'currency': currency
                    })
                sender = '%s <no-reply@%s>' % (config.company_name,
                                               service.domain)
                msg = EmailMessage(subject, html_content, sender,
                                   [member.email])
                msg.content_subtype = "html"
                Thread(target=lambda m: m.send(), args=(msg, )).start()
            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)
            if total_amount >= invoice.amount:
                invoice.status = AbstractInvoice.PAID
                invoice.save()
Example #18
0
def send_invoice_overdue_notices():
    """
    This cron task sends notice of Invoice overdue
    """
    ikwen_service = get_service_instance()
    now = datetime.now()
    connection = mail.get_connection()
    try:
        connection.open()
    except:
        logger.error(u"Connexion error", exc_info=True)

    for invoicing_config in InvoicingConfig.objects.exclude(
            service=ikwen_service):
        service = invoicing_config.service
        if service.status != Service.ACTIVE:
            continue
        config = service.basic_config
        db = service.database
        add_database(db)
        invoice_qs = Invoice.objects.using(db).select_related('subscription')\
            .filter(Q(status=Invoice.PENDING) | Q(status=Invoice.OVERDUE),
                    due_date__lt=now, overdue_notices_sent__lt=3)
        count, total_amount = 0, 0
        for invoice in invoice_qs:
            if invoice.last_overdue_notice:
                diff = now - invoice.last_overdue_notice
            else:
                invoice.status = Invoice.OVERDUE
                invoice.save()
            if invoice.last_overdue_notice and diff.days != invoicing_config.overdue_delay:
                continue
            count += 1
            total_amount += invoice.amount
            member = invoice.subscription.member
            add_event(service,
                      OVERDUE_NOTICE_EVENT,
                      member=member,
                      object_id=invoice.id)
            subject, message, sms_text = get_invoice_overdue_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 = 'http://ikwen.com' + reverse(
                        'billing:invoice_detail', args=(invoice.id, ))
                    html_content = get_mail_content(
                        subject,
                        message,
                        service=service,
                        template_name='billing/mails/notice.html',
                        extra_context={
                            'member_name': member.first_name,
                            'invoice': invoice,
                            '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.service = service
                    msg.content_subtype = "html"
                    invoice.last_overdue_notice = timezone.now()
                    try:
                        with transaction.atomic(using=WALLETS_DB_ALIAS):
                            if msg.send():
                                invoice.overdue_notices_sent += 1
                                balance.mail_count -= 1
                                balance.save()
                            else:
                                logger.error(
                                    u"Overdue notice for Invoice #%s not sent to %s"
                                    % (invoice.number, member.email),
                                    exc_info=True)
                    except:
                        logger.error(u"Connexion error on Invoice #%s to %s" %
                                     (invoice.number, member.email),
                                     exc_info=True)
                    invoice.save()

            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 overdue notice for invoice #%s not sent to %s" %
                        (invoice.number, member.phone),
                        exc_info=True)
        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,
                      OVERDUE_NOTICES_SENT_EVENT,
                      group_id=sudo_group.id,
                      object_id=report.id)

    try:
        connection.close()
    except:
        pass