Example #1
0
def set_dara_stats(partner_original, service_partner, invoice, dara_earnings):
    set_counters(partner_original)
    increment_history_field(partner_original, 'turnover_history',
                            dara_earnings)
    increment_history_field(partner_original, 'earnings_history',
                            dara_earnings)
    increment_history_field(partner_original, 'transaction_count_history')

    set_counters(service_partner)
    increment_history_field(service_partner, 'earnings_history', dara_earnings)
    increment_history_field(service_partner, 'transaction_count_history')

    ikwen_service = get_service_instance()
    try:
        config = ikwen_service.config
        activate(partner_original.member.language)
        subject = _("New transaction on %s" % config.company_name)
        dashboard_url = 'https://daraja.ikwen.com' + reverse(
            'daraja:dashboard')
        html_content = get_mail_content(
            subject,
            template_name='daraja/mails/new_transaction.html',
            extra_context={
                'currency_symbol': config.currency_symbol,
                'amount': invoice.amount,
                'dara_earnings': dara_earnings,
                'tx_date': invoice.updated_on.strftime('%Y-%m-%d'),
                'tx_time': invoice.updated_on.strftime('%H:%M:%S'),
                'account_balance': partner_original.balance,
                'dashboard_url': dashboard_url
            })
        sender = 'ikwen Daraja <*****@*****.**>'
        msg = XEmailMessage(subject, html_content, sender,
                            [partner_original.member.email])
        msg.content_subtype = "html"
        if getattr(settings, 'UNIT_TESTING', False):
            msg.send()
        else:
            Thread(target=lambda m: m.send(), args=(msg, )).start()
    except:
        logger.error("Failed to notify %s Dara after follower purchase." %
                     partner_original,
                     exc_info=True)
Example #2
0
def rerun_complete_revivals(debug=False):
    """
    Re-run Revivals with status = COMPLETE to keep users engaged
    """
    t0 = datetime.now()
    total_revival, total_mail = 0, 0
    three_days_ago = timezone.now() - timedelta(days=3)
    for revival in Revival.objects.select_related('service').filter(
            status=COMPLETE, is_active=True):
        try:
            refreshed = Revival.objects.get(pk=revival.id)
            if refreshed.is_running:
                continue
            refreshed.is_running = True
            refreshed.save()
            total_revival += 1
        except Revival.DoesNotExist:
            continue

        try:
            mail_renderer = import_by_path(revival.mail_renderer)
            kwargs = {}
            if revival.get_kwargs:
                get_kwargs = import_by_path(revival.get_kwargs)
                kwargs = get_kwargs(revival)
        except:
            revival.is_running = False
            revival.save()
            logger.error("Error when starting revival %s for %s" %
                         (revival.mail_renderer, revival.service),
                         exc_info=True)
            continue

        service = revival.service
        db = revival.service.database
        add_database(db)
        balance = Balance.objects.using(WALLETS_DB_ALIAS).get(
            service_id=service.id)
        if balance.mail_count == 0:
            revival.is_running = False
            revival.save()
            try:
                notify_for_empty_messaging_credit(service, balance)
            except:
                logger.error(
                    "Failed to notify %s for empty messaging credit." %
                    service,
                    exc_info=True)
            continue
        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)
        tk = revival.model_name.split('.')
        model = get_model(tk[0], tk[1])
        try:
            obj = model._default_manager.using(db).get(pk=revival.object_id)
        except ObjectDoesNotExist:
            revival.is_running = False
            revival.save()
            continue
        try:
            profile_tag = ProfileTag.objects.using(db).get(
                pk=revival.profile_tag_id)
        except:
            revival.is_running = False
            revival.save()
            continue

        set_counters_many(profile_tag)
        revival_local = Revival.objects.using(db).get(pk=revival.id)

        target_queryset = revival_local.target_set.select_related(
            'member').filter(revived_on__lte=three_days_ago,
                             revival_count__lt=MAX_AUTO_REWARDS)
        if target_queryset.count() == 0:
            revival.is_running = False
            revival.save()
            continue
        revival.run_on = timezone.now()
        revival.status = STARTED
        revival.save()
        connection = mail.get_connection()
        try:
            connection.open()
        except:
            revival.is_running = False
            revival.save()
            logger.error(u"Connexion error", exc_info=True)
            continue

        logger.debug("Running rerun_complete_revivals() %s for %s" %
                     (revival.mail_renderer, revival.service))
        for target in target_queryset.order_by('updated_on')[:MAX_BATCH_SEND]:
            if not debug and balance.mail_count == 0:
                revival.is_running = False
                revival.save()
                try:
                    notify_for_empty_messaging_credit(service, balance)
                except:
                    logger.error(
                        "Failed to notify %s for empty messaging credit." %
                        service,
                        exc_info=True)
                break
            member = target.member
            if debug and not member.is_superuser:
                continue
            if member.language:
                activate(member.language)
            else:
                activate('en')

            if getattr(settings, 'UNIT_TESTING', False):
                sender, subject, html_content = mail_renderer(
                    target, obj, revival, **kwargs)
            else:
                try:
                    sender, subject, html_content = mail_renderer(
                        target, obj, revival, **kwargs)
                except:
                    logger.error(
                        "Could not render mail for member %s, Revival %s, Obj: %s"
                        % (member.email, revival.mail_renderer, str(obj)),
                        exc_info=True)
                    continue

            if not html_content:
                continue
            if debug:
                subject = 'Test remind - ' + subject
            msg = XEmailMessage(subject, html_content, sender, [member.email])
            msg.content_subtype = "html"
            msg.type = XEmailObject.REVIVAL
            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.revived_on = t0
                        target.save()
                        total_mail += 1
                        increment_history_field(profile_tag,
                                                'smart_revival_history')
                    else:
                        logger.error("Member %s not notified for Content %s" %
                                     (member.email, str(obj)),
                                     exc_info=True)
            except:
                logger.error("Member %s not notified for Content %s" %
                             (member.email, str(obj)),
                             exc_info=True)

        revival.is_running = False
        revival.progress += 1
        revival.save()

        try:
            connection.close()
        except:
            revival.is_running = False
            revival.save()

    diff = datetime.now() - t0
    logger.debug(
        "rerun_complete_revivals() run %d revivals. %d mails sent in %s" %
        (total_revival, total_mail, diff))
Example #3
0
def notify_profiles(debug=False):
    """
    Cron job that revive users by mail. Must be configured
    to run with a settings file having 'umbrella' as default database.
    :return:
    """
    t0 = datetime.now()
    seven_hours_ago = t0 - timedelta(hours=7)
    total_revival, total_mail = 0, 0
    for revival in Revival.objects.select_related('service').exclude(
            status=COMPLETE, is_active=False):
        try:
            refreshed = Revival.objects.get(pk=revival.id)
            if refreshed.is_running:
                continue
            refreshed.is_running = True
            refreshed.save()
            total_revival += 1
        except Revival.DoesNotExist:
            continue

        try:
            mail_renderer = import_by_path(revival.mail_renderer)
            kwargs = {}
            if revival.get_kwargs:
                get_kwargs = import_by_path(revival.get_kwargs)
                kwargs = get_kwargs(revival)
        except:
            revival.is_running = False
            revival.save()
            logger.error("Error when starting revival %s for %s" %
                         (revival.mail_renderer, revival.service),
                         exc_info=True)
            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:
            revival.is_running = False
            revival.save()
            try:
                notify_for_empty_messaging_credit(service, balance)
            except:
                logger.error(
                    "Failed to notify %s for empty messaging credit." %
                    service,
                    exc_info=True)
            continue
        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)
        tk = revival.model_name.split('.')
        model = get_model(tk[0], tk[1])
        try:
            obj = model._default_manager.using(db).get(pk=revival.object_id)
        except ObjectDoesNotExist:
            revival.is_running = False
            revival.save()
            continue
        try:
            profile_tag = ProfileTag.objects.using(db).get(
                pk=revival.profile_tag_id)
        except:
            revival.is_running = False
            revival.save()
            continue

        if revival.status != PENDING:
            revival.is_running = False
            revival.save()
            continue

        set_counters(profile_tag)
        revival_local = Revival.objects.using(db).get(pk=revival.id)
        if debug:
            member_queryset = Member.objects.using(db).filter(
                is_superuser=True)
        else:
            member_queryset = Member.objects.using(db).filter(
                date_joined__lte=seven_hours_ago)
        total = member_queryset.count()
        chunks = total / 500 + 1
        target_count = 0
        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:
                    ref_tag = ProfileTag.objects.using(db).get(slug=REFERRAL)
                    profile = MemberProfile.objects.using(db).create(
                        member=member, tag_fk_list=[ref_tag.id])
                if revival.profile_tag_id in profile.tag_fk_list:
                    if debug:
                        tag = ProfileTag.objects.using(db).get(
                            pk=revival.profile_tag_id)
                        print "Profiles matching on %s for member %s" % (
                            tag, member)
                    if member.email:
                        Target.objects.using(db).get_or_create(
                            revival=revival_local, member=member)
                        target_count += 1

        if target_count == 0:
            revival.is_running = False
            revival.save()
            continue

        revival.run_on = datetime.now()
        revival.status = STARTED
        revival.total = revival_local.target_set.all().count()
        revival.save()

        connection = mail.get_connection()
        try:
            connection.open()
        except:
            revival.is_running = False
            revival.save()
            logger.error(u"Connexion error", exc_info=True)
            continue

        logger.debug("Running notify_profiles() %s for %s" %
                     (revival.mail_renderer, revival.service))
        for target in revival_local.target_set.select_related('member').filter(
                notified=False)[:MAX_BATCH_SEND]:
            if not debug and balance.mail_count == 0:
                revival.is_running = False
                revival.save()
                try:
                    notify_for_empty_messaging_credit(service, balance)
                except:
                    logger.error(
                        "Failed to notify %s for empty messaging credit." %
                        service,
                        exc_info=True)
                break
            member = target.member
            if member.language:
                activate(member.language)
            else:
                activate('en')

            if getattr(settings, 'UNIT_TESTING', False):
                sender, subject, html_content = mail_renderer(
                    target, obj, revival, **kwargs)
            else:
                try:
                    sender, subject, html_content = mail_renderer(
                        target, obj, revival, **kwargs)
                except:
                    logger.error(
                        "Could not render mail for member %s, Revival %s, Obj: %s"
                        % (member.email, revival.mail_renderer, str(obj)),
                        exc_info=True)
                    continue

            if not html_content:
                continue
            if debug:
                subject = 'Test - ' + subject
            msg = XEmailMessage(subject, html_content, sender, [member.email])
            msg.content_subtype = "html"
            msg.type = XEmailObject.REVIVAL
            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.notified = True
                        target.revived_on = t0
                        target.save()
                        total_mail += 1
                        increment_history_field(profile_tag,
                                                'smart_revival_history')
                    else:
                        logger.error("Member %s not notified for Content %s" %
                                     (member.email, str(obj)),
                                     exc_info=True)
            except:
                logger.error("Member %s not notified for Content %s" %
                             (member.email, str(obj)),
                             exc_info=True)
            revival.progress += 1
            revival.save()

        revival.is_running = False
        if revival.progress > 0 and revival.progress >= revival.total:
            revival.status = COMPLETE
        revival.save()

        try:
            connection.close()
        except:
            revival.is_running = False
            revival.save()

    diff = datetime.now() - t0
    logger.debug("notify_profiles() run %d revivals. %d mails sent in %s" %
                 (total_revival, total_mail, diff))
Example #4
0
def confirm_service_invoice_payment(request, *args, **kwargs):
    """
    This view is run after successful user cashout "MOMO_AFTER_CHECKOUT"
    """
    status = request.GET['status']
    message = request.GET['message']
    operator_tx_id = request.GET['operator_tx_id']
    phone = request.GET['phone']
    tx_id = kwargs['tx_id']
    extra_months = int(kwargs['extra_months'])
    try:
        tx = MoMoTransaction.objects.using(WALLETS_DB_ALIAS).get(pk=tx_id, is_running=True)
        if not getattr(settings, 'DEBUG', False):
            tx_timeout = getattr(settings, 'IKWEN_PAYMENT_GATEWAY_TIMEOUT', 15) * 60
            expiry = tx.created_on + timedelta(seconds=tx_timeout)
            if datetime.now() > expiry:
                return HttpResponse("Transaction %s timed out." % tx_id)

        tx.status = status
        tx.message = 'OK' if status == MoMoTransaction.SUCCESS else message
        tx.processor_tx_id = operator_tx_id
        tx.phone = phone
        tx.is_running = False
        tx.save()
    except:
        raise Http404("Transaction %s not found" % tx_id)
    if status != MoMoTransaction.SUCCESS:
        return HttpResponse("Notification for transaction %s received with status %s" % (tx_id, status))
    invoice_id = tx.object_id
    amount = tx.amount
    signature = tx.task_id

    callback_signature = kwargs.get('signature')
    no_check_signature = request.GET.get('ncs')
    if getattr(settings, 'DEBUG', False):
        if not no_check_signature:
            if callback_signature != signature:
                return HttpResponse('Invalid transaction signature')
    else:
        if callback_signature != signature:
            return HttpResponse('Invalid transaction signature')
    now = datetime.now()
    ikwen_service = get_service_instance()
    invoice = Invoice.objects.get(pk=invoice_id)
    invoice.paid += amount
    invoice.status = Invoice.PAID
    invoice.save()
    payment = Payment.objects.create(invoice=invoice, method=Payment.MOBILE_MONEY,
                                     amount=amount, processor_tx_id=tx.processor_tx_id)
    service = invoice.service
    total_months = invoice.months_count + extra_months
    days = get_days_count(total_months)
    invoicing_config = get_invoicing_config_instance()
    if service.status == Service.SUSPENDED:
        days -= invoicing_config.tolerance  # Catch-up days that were offered before service suspension
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    elif service.expiry:
        expiry = service.expiry + timedelta(days=days)
    else:
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    service.expiry = expiry
    service.status = Service.ACTIVE
    if invoice.is_one_off:
        service.version = Service.FULL
        try:
            support_bundle = SupportBundle.objects.get(type=SupportBundle.TECHNICAL, channel=SupportBundle.PHONE, cost=0)
            token = ''.join([random.SystemRandom().choice(string.digits) for i in range(6)])
            support_expiry = now + timedelta(days=support_bundle.duration)
            SupportCode.objects.create(service=service, token=token, bundle=support_bundle,
                                       balance=support_bundle.quantity, expiry=support_expiry)
            logger.debug("Free Support Code created for %s" % service)
        except SupportBundle.DoesNotExist:
            logger.error("Free Support Code not created for %s" % service, exc_info=True)
    service.save()
    mean = tx.wallet
    is_early_payment = False
    if service.app.slug == 'kakocase' or service.app.slug == 'webnode':
        if invoice.due_date <= now.date():
            is_early_payment = True
        refill_tsunami_messaging_bundle(service, is_early_payment)
    share_payment_and_set_stats(invoice, total_months, mean)
    member = service.member
    vendor = service.retailer
    vendor_is_dara = vendor and vendor.app.slug == DARAJA
    if vendor and not vendor_is_dara:
        add_database_to_settings(vendor.database)
        sudo_group = Group.objects.using(vendor.database).get(name=SUDO)
    else:
        vendor = ikwen_service
        sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO)
    add_event(vendor, PAYMENT_CONFIRMATION, member=member, object_id=invoice.id)
    add_event(vendor, PAYMENT_CONFIRMATION, group_id=sudo_group.id, object_id=invoice.id)

    try:
        invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
    except:
        invoice_pdf_file = None

    if member.email:
        activate(member.language)
        invoice_url = service.url + reverse('billing:invoice_detail', args=(invoice.id,))
        subject, message, sms_text = get_payment_confirmation_message(payment, member)
        html_content = get_mail_content(subject, message, service=vendor, template_name='billing/mails/notice.html',
                                        extra_context={'member_name': member.first_name, 'invoice': invoice,
                                                       'cta': _("View invoice"), 'invoice_url': invoice_url,
                                                       'early_payment': is_early_payment})
        sender = '%s <no-reply@%s>' % (vendor.config.company_name, ikwen_service.domain)
        msg = XEmailMessage(subject, html_content, sender, [member.email])
        if vendor != ikwen_service and not vendor_is_dara:
            msg.service = vendor
        if invoice_pdf_file:
            msg.attach_file(invoice_pdf_file)
        msg.content_subtype = "html"
        if getattr(settings, 'UNIT_TESTING', False):
            msg.send()
        else:
            Thread(target=lambda m: m.send(), args=(msg,)).start()
    return HttpResponse("Notification received")
Example #5
0
 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()
Example #6
0
def confirm_correction_payment(request, *args, **kwargs):
    status = request.GET['status']
    message = request.GET['message']
    operator_tx_id = request.GET['operator_tx_id']
    phone = request.GET['phone']
    tx_id = kwargs['tx_id']
    try:
        tx = MoMoTransaction.objects.using(WALLETS_DB_ALIAS).get(pk=tx_id)
        if not getattr(settings, 'DEBUG', False):
            tx_timeout = getattr(settings, 'IKWEN_PAYMENT_GATEWAY_TIMEOUT',
                                 15) * 60
            expiry = tx.created_on + timedelta(seconds=tx_timeout)
            if datetime.now() > expiry:
                return HttpResponse("Transaction %s timed out." % tx_id)
    except:
        raise Http404("Transaction %s not found" % tx_id)

    callback_signature = kwargs.get('signature')
    no_check_signature = request.GET.get('ncs')
    if getattr(settings, 'DEBUG', False):
        if not no_check_signature:
            if callback_signature != tx.task_id:
                return HttpResponse('Invalid transaction signature')
    else:
        if callback_signature != tx.task_id:
            return HttpResponse('Invalid transaction signature')

    if status != MoMoTransaction.SUCCESS:
        return HttpResponse(
            "Notification for transaction %s received with status %s" %
            (tx_id, status))

    school = get_service_instance()
    school_config = SchoolConfig.objects.get(service=school)
    ikwen_charges = tx.amount * school_config.my_kids_share_rate / 100
    teacher_earnings = tx.amount * (100 -
                                    school_config.my_kids_share_rate) / 100

    tx.status = status
    tx.message = message
    tx.processor_tx_id = operator_tx_id
    tx.phone = phone
    tx.is_running = False
    tx.fees = ikwen_charges
    tx.save()
    # mean = tx.wallet
    #
    # amount = tx.amount - ikwen_charges
    # payment = Payment.objects.get(object_id=tx.object_id)

    assignment = Assignment.objects.get(title=tx.message.rstrip(' correction'))
    correction = assignment.assignmentcorrection
    parent = request.user
    subject = assignment.subject
    classroom = assignment.classroom
    teacher = subject.get_teacher(classroom=classroom)
    daraja = Application.objects.get(slug=DARAJA)
    try:
        dara_weblet = Service.objects.using(UMBRELLA).get(
            app=daraja, member=teacher.member)
        dara_db = dara_weblet.database
        dara_weblet_self = Service.objects.using(dara_db).get(
            pk=dara_weblet.id)
        set_counters(dara_weblet_self)
        increment_history_field(dara_weblet_self, 'turnover_history',
                                teacher_earnings)
        increment_history_field(dara_weblet_self, 'earnings_history',
                                teacher_earnings)
        increment_history_field(dara_weblet_self, 'transaction_count_history')
        # share_payment_and_set_stats(invoice, mean)
    except:
        logger.error("The teacher %s doesn't yet have a Dara account" %
                     teacher)
    foulassi_weblet = get_service_instance()
    try:
        currency = Currency.active.default().symbol
    except:
        currency = school_config.currency_code
    body = _(
        "A student just purchase the correction of %(assignment_title)s in %(subject_name)s"
        % {
            'assignment_title': assignment.title,
            'subject_name': subject.name
        })
    member_teacher = teacher.member
    subject = _("New correction of %s paid" % correction)
    cta_url = 'https://daraja.ikwen.com' + reverse('daraja:dashboard')
    html_content = get_mail_content(
        subject,
        template_name='foulassi/mails/correction_paid.html',
        extra_context={
            'teacher': member_teacher.first_name,
            'classroom': classroom,
            'cta_url': cta_url,
            'subject': assignment.subject.name,
            'assignment': assignment,
            'currency': currency
        })
    sender = '%s <no-reply@%s>' % (school_config.company_name, school.domain)
    msg = XEmailMessage(subject, html_content, sender, [member_teacher.email])
    msg.bcc = ['*****@*****.**', '*****@*****.**']
    msg.content_subtype = "html"
    try:
        msg.send()
    except Exception as e:
        logger.debug(e.message)
    send_push(foulassi_weblet, member_teacher, subject, body, cta_url)

    body = _(
        "You just pay the correction of %(assignment_title)s from %(subject_name)s "
        % {
            'assignment_title': assignment.title,
            'subject_name': subject.name
        })
    subject = _("New correction paid")
    if parent.email:
        html_content = get_mail_content(
            subject,
            template_name='foulassi/mails/correction_paid_parent_notif.html',
            extra_context={
                'classroom': classroom,
                'parent_name': parent.first_name,
                'subject': assignment.subject.name,
                'assignment': assignment,
                'currency': currency
            })
        sender = '%s <no-reply@%s>' % (school_config.company_name,
                                       school.domain)
        msg = XEmailMessage(subject, html_content, sender, [parent.email])
        msg.bcc = ['*****@*****.**', '*****@*****.**']
        msg.content_subtype = "html"
        try:
            msg.send()
        except Exception as e:
            logger.debug(e.message)
    send_push(foulassi_weblet, parent, subject, body, cta_url)
    return HttpResponse(
        "Notification for transaction %s received with status %s" %
        (tx_id, status))
Example #7
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 #8
0
def pull_invoice(request, *args, **kwargs):
    api_signature = request.POST.get('api_signature')
    try:
        service = Service.objects.get(api_signature=api_signature)
    except:
        notice = "Invalid API Signature."
        response = {'error': notice}
        return HttpResponse(json.dumps(response))

    db = service.database
    add_database(db)
    invoicing_config, update = InvoicingConfig.objects.using(db).get_or_create(
        service=service)
    if not invoicing_config.pull_invoice:
        notice = "Cannot import when not explicitly configured to do so. You must activate " \
                 "'pull_invoice' in your platform configuration for import to work."
        response = {'error': notice}
        return HttpResponse(json.dumps(response))

    lang = request.POST.get('lang', "en")
    activate(lang)
    missing = []
    errors = []
    do_pull = True
    try:
        number = request.POST['invoice_number'].strip()
        try:
            Invoice.objects.using(db).get(number=number)
            errors.append(
                "Invoice with number '%s' already exists. Invoice numbers must be unique."
                % number)
            do_pull = False
        except Invoice.DoesNotExist:
            pass
    except KeyError:
        missing.append('invoice_number')
        do_pull = False
    try:
        reference_id = request.POST['reference_id']
    except KeyError:
        reference_id = None
        missing.append('reference_id')
        do_pull = False
    try:
        amount = request.POST['amount']
        amount = float(amount)
    except KeyError:
        missing.append('amount')
        do_pull = False
    except ValueError:
        errors.append("Invalid amount '%s'. Expected valid float or int.")
        do_pull = False
    try:
        due_date = request.POST['due_date']
        time.strptime(due_date, '%Y-%m-%d')
    except KeyError:
        missing.append('due_date')
        do_pull = False
    except ValueError:
        errors.append(
            "Invalid due_date '%s'. Expected valid date in the format 'YYYY-mm-dd'."
        )
        do_pull = False
    try:
        quantity = request.POST['quantity']
    except KeyError:
        missing.append('quantity')
        do_pull = False
    except ValueError:
        errors.append("Invalid quantity '%s'. Expected valid int.")
        do_pull = False
    quantity_unit = request.POST.get('quantity_unit', _("Month(s)"))
    currency_code = request.POST.get('currency_code', 'XAF')
    if reference_id:
        try:
            subscription = Subscription.objects.using(db).select_related(
                'member', 'product').get(reference_id=reference_id)
        except Subscription.DoesNotExist:
            do_pull = False
            notice = "reference_id '%s' not found." % reference_id
            errors.append(notice)
    if not do_pull:
        response = {'error': '\n'.join(errors)}
        if missing:
            response[
                'missing'] = 'Following parameters are missing: ' + ', '.join(
                    missing)
        return HttpResponse(json.dumps(response))

    product = subscription.product
    if product:
        short_description = product.name
    else:
        short_description = request.POST.get('short_description', '---')
    invoice_entries = []
    item = InvoiceItem(label=_('Subscription'), amount=amount)
    entry = InvoiceEntry(item=item,
                         short_description=short_description,
                         quantity=quantity,
                         quantity_unit=quantity_unit,
                         total=amount)
    invoice_entries.append(entry)
    invoice = Invoice.objects.using(db).create(number=number,
                                               member=subscription.member,
                                               subscription=subscription,
                                               amount=amount,
                                               months_count=quantity,
                                               due_date=due_date,
                                               entries=invoice_entries)
    config = service.config
    member = subscription.member
    if member.email:
        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:
                notify_for_low_messaging_credit(service, balance)
            if balance.mail_count <= 0:
                notify_for_empty_messaging_credit(service, balance)
                response = {
                    'success': True,
                    'warning': "Email not set due to empty mail credit"
                }
                return HttpResponse(json.dumps(response))
            subject, message, sms_text = get_invoice_generated_message(invoice)
            try:
                currency = Currency.objects.using(db).get(
                    code=currency_code).symbol
            except:
                try:
                    currency = Currency.active.default().symbol
                except:
                    currency = currency_code

            invoice_url = service.url + reverse('billing:invoice_detail',
                                                args=(invoice.id, ))
            html_content = get_mail_content(
                subject,
                template_name='billing/mails/notice.html',
                service=service,
                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, [member.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()
    response = {'success': True, 'invoice_id': invoice.id}
    return HttpResponse(json.dumps(response))
Example #9
0
def confirm_service_invoice_payment(request, *args, **kwargs):
    """
    This view is run after successful user cashout "MOMO_AFTER_CHECKOUT"
    """
    tx = kwargs[
        'tx']  # Decoration with @momo_gateway_callback makes 'tx' available in kwargs
    extra_months = int(kwargs['extra_months'])
    payment = Payment.objects.select_related().get(pk=tx.object_id)
    payment.processor_tx_id = tx.processor_tx_id
    payment.save()
    invoice = payment.invoice
    now = datetime.now()
    ikwen_service = get_service_instance()
    invoice.paid += tx.amount
    if invoice.paid >= invoice.amount:
        invoice.status = Invoice.PAID
    invoice.save()
    service = invoice.service
    total_months = invoice.months_count + extra_months
    days = get_days_count(total_months)
    invoicing_config = get_invoicing_config_instance()
    if service.status == Service.SUSPENDED:
        days -= invoicing_config.tolerance  # Catch-up days that were offered before service suspension
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    elif service.expiry:
        expiry = service.expiry + timedelta(days=days)
    else:
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    service.expiry = expiry
    service.status = Service.ACTIVE
    if invoice.is_one_off:
        service.version = Service.FULL
        try:
            support_bundle = SupportBundle.objects.get(
                type=SupportBundle.TECHNICAL,
                channel=SupportBundle.PHONE,
                cost=0)
            token = ''.join([
                random.SystemRandom().choice(string.digits) for i in range(6)
            ])
            support_expiry = now + timedelta(days=support_bundle.duration)
            SupportCode.objects.create(service=service,
                                       token=token,
                                       bundle=support_bundle,
                                       balance=support_bundle.quantity,
                                       expiry=support_expiry)
            logger.debug("Free Support Code created for %s" % service)
        except SupportBundle.DoesNotExist:
            logger.error("Free Support Code not created for %s" % service,
                         exc_info=True)
    service.save()
    mean = tx.wallet
    is_early_payment = False
    if service.app.slug == 'kakocase' or service.app.slug == 'webnode':
        if invoice.due_date <= now.date():
            is_early_payment = True
        refill_tsunami_messaging_bundle(service, is_early_payment)
    share_payment_and_set_stats(invoice, total_months, mean, tx)
    member = service.member
    vendor = service.retailer
    vendor_is_dara = vendor and vendor.app.slug == DARAJA
    if vendor and not vendor_is_dara:
        add_database_to_settings(vendor.database)
        sudo_group = Group.objects.using(vendor.database).get(name=SUDO)
    else:
        vendor = ikwen_service
        sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO)
    add_event(vendor,
              PAYMENT_CONFIRMATION,
              member=member,
              object_id=invoice.id)
    add_event(vendor,
              PAYMENT_CONFIRMATION,
              group_id=sudo_group.id,
              object_id=invoice.id)

    try:
        invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
    except:
        invoice_pdf_file = None

    if member.email:
        activate(member.language)
        invoice_url = ikwen_service.url + reverse('billing:invoice_detail',
                                                  args=(invoice.id, ))
        subject, message, sms_text = get_payment_confirmation_message(
            payment, member)
        html_content = get_mail_content(
            subject,
            message,
            service=vendor,
            template_name='billing/mails/notice.html',
            extra_context={
                'member_name': member.first_name,
                'invoice': invoice,
                'cta': _("View invoice"),
                'invoice_url': invoice_url,
                'early_payment': is_early_payment
            })
        sender = '%s <no-reply@%s>' % (vendor.config.company_name,
                                       ikwen_service.domain)
        msg = XEmailMessage(subject, html_content, sender, [member.email])
        if vendor != ikwen_service and not vendor_is_dara:
            msg.service = vendor
        if invoice_pdf_file:
            msg.attach_file(invoice_pdf_file)
        msg.content_subtype = "html"
        if getattr(settings, 'UNIT_TESTING', False):
            msg.send()
        else:
            Thread(target=lambda m: m.send(), args=(msg, )).start()
    return HttpResponse("Notification received")
Example #10
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 #11
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 #12
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