Exemple #1
0
def product_set_checkout(request, *args, **kwargs):
    product_id = request.POST['product_id']
    product = Product.objects.get(pk=product_id)
    member = request.user
    now = datetime.now()
    expiry = now + timedelta(days=product.duration)
    subscription = Subscription.objects.create(member=member,
                                               product=product,
                                               since=now,
                                               expiry=expiry)
    number = get_next_invoice_number()
    item = InvoiceItem(label=product.name)
    entry = InvoiceEntry(item=item,
                         short_description=product.short_description,
                         total=product.cost)
    months_count = product.duration / 30
    invoice = Invoice.objects.create(subscription=subscription,
                                     amount=product.cost,
                                     number=number,
                                     due_date=now,
                                     last_reminder=now,
                                     is_one_off=product.is_one_off,
                                     entries=[entry],
                                     months_count=months_count)
    amount = invoice.amount
    notification_url = reverse('billing:product_do_checkout',
                               args=(invoice.id, ))
    cancel_url = request.META['HTTP_REFERER']
    return_url = reverse('billing:invoice_detail', args=(invoice.id, ))
    return invoice, amount, notification_url, return_url, cancel_url
Exemple #2
0
def product_set_checkout(request, *args, **kwargs):
    service = get_service_instance()
    product_id = request.POST['product_id']
    product = Product.objects.get(pk=product_id)
    member = request.user
    now = datetime.now()
    expiry = now + timedelta(days=product.duration)
    subscription = Subscription.objects.create(member=member, product=product, since=now, expiry=expiry)
    number = get_next_invoice_number()
    item = InvoiceItem(label=product.name)
    entry = InvoiceEntry(item=item, short_description=product.short_description, total=product.cost)
    months_count = product.duration / 30
    invoice = Invoice.objects.create(subscription=subscription, amount=product.cost, number=number, due_date=now,
                                     last_reminder=now, is_one_off=True, entries=[entry], months_count=months_count)

    request.session['amount'] = product.cost
    request.session['model_name'] = 'billing.Invoice'
    request.session['object_id'] = invoice.id

    mean = request.GET.get('mean', MTN_MOMO)
    request.session['mean'] = mean
    request.session['notif_url'] = service.url # Orange Money only
    request.session['cancel_url'] = service.url + reverse('billing:pricing') # Orange Money only
    request.session['return_url'] = reverse('billing:invoice_detail', args=(invoice.id, ))
Exemple #3
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()
Exemple #4
0
def set_my_kids_payment(request, *args, **kwargs):
    school_id = request.POST['school_id']
    student_id = request.POST['student_id']
    cycle = request.POST['my_kids_cycle']
    school = Service.objects.get(pk=school_id)
    student = Student.objects.get(pk=student_id)
    school_config = SchoolConfig.objects.get(service=school)
    Invoice.objects.filter(student=student,
                           is_my_kids=True,
                           status=Invoice.PENDING).delete()
    max_expiry = datetime(day=31, month=8, year=get_school_year() + 1)
    if cycle == Service.YEARLY:
        amount = school_config.my_kids_fees
    elif cycle == Service.QUARTERLY:
        amount = school_config.my_kids_fees_term
    else:
        amount = school_config.my_kids_fees_month
        cycle = Service.MONTHLY
    item = InvoiceItem(label=_("MyKids fees"), amount=amount)
    days = get_billing_cycle_days_count(cycle)
    now = datetime.now()
    expiry = now + timedelta(days=days)
    expiry = min(expiry, max_expiry)
    short_description = now.strftime("%Y/%m/%d") + ' - ' + expiry.strftime(
        "%Y/%m/%d")
    entry = InvoiceEntry(item=item,
                         short_description=short_description,
                         total=amount,
                         quantity_unit='')
    number = get_next_invoice_number()
    member = request.user
    invoice = Invoice.objects.create(number=number,
                                     member=member,
                                     student=student,
                                     school=school,
                                     is_one_off=True,
                                     amount=amount,
                                     my_kids_cycle=cycle,
                                     due_date=now,
                                     entries=[entry],
                                     is_my_kids=True)
    foulassi_weblet = get_service_instance()  # This is Foulassi service itself

    # Transaction is hidden from school if ikwen collects 100%.
    # This is achieved by changing the service_id of transaction
    tx_service_id = school.id if school_config.my_kids_share_rate < 100 else foulassi_weblet.id
    model_name = 'billing.Invoice'
    mean = request.GET.get('mean', MTN_MOMO)
    signature = ''.join([
        random.SystemRandom().choice(string.ascii_letters + string.digits)
        for i in range(16)
    ])
    MoMoTransaction.objects.using(WALLETS_DB_ALIAS).filter(
        object_id=invoice.id).delete()

    tx = MoMoTransaction.objects.using(WALLETS_DB_ALIAS)\
        .create(service_id=tx_service_id, type=MoMoTransaction.CASH_OUT, amount=amount, phone='N/A', model=model_name,
                object_id=invoice.id, task_id=signature, wallet=mean, username=request.user.username, is_running=True)
    notification_url = foulassi_weblet.url + reverse(
        'foulassi:confirm_my_kids_payment', args=(tx.id, signature))
    cancel_url = request.META['HTTP_REFERER']
    return_url = request.META['HTTP_REFERER']
    return invoice, amount, notification_url, return_url, cancel_url
Exemple #5
0
def confirm_reservation_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
    amount = tx.amount
    now = datetime.now()

    # Update reservation status --- Confirm reservation
    reservation_id = tx.object_id
    reservation = Reservation.objects.get(pk=reservation_id)
    reservation.processor_tx_id = tx.processor_tx_id
    reservation.method = Reservation.MOBILE_MONEY
    reservation.status = CONFIRMED
    item = InvoiceItem(label=_("Reservation of %s " % reservation.post),
                       amount=reservation.amount)
    short_description = reservation.start_on.strftime(
        "%Y/%m/%d") + ' - ' + reservation.end_on.strftime("%Y/%m/%d")
    entry = InvoiceEntry(item=item,
                         short_description=short_description,
                         total=reservation.amount,
                         quantity_unit='')
    reservation.entries = [entry]
    reservation.save()

    # Share earnings
    weblet = get_service_instance(check_cache=False)
    config = weblet.config
    ikwen_charges = tx.amount * config.ikwen_share_rate / 100
    ikwen_share_rate = config.ikwen_share_fixed
    tx.fees = ikwen_charges
    tx.save()
    amount = (100 - ikwen_share_rate) * amount / 100
    weblet.raise_balance(amount, provider=tx.wallet)
    set_counters(weblet)
    increment_history_field(weblet, 'turnover_history', amount)
    increment_history_field(weblet, 'earnings_history', amount)
    increment_history_field(weblet, 'transaction_count_history')

    member = reservation.member

    # Notify customer and staff

    payer_email = member.email
    email = config.contact_email
    if not email:
        email = weblet.member.email
    if email or payer_email:
        subject = _("New reservation of %s done" % reservation.post)
        try:
            html_content = get_mail_content(
                subject,
                template_name='enfinchezmoi/mails/payment_notice.html',
                extra_context={
                    'currency_symbol': config.currency_symbol,
                    'post': reservation,
                    'payer': member,
                    'tx_date': tx.updated_on.strftime('%Y-%m-%d'),
                    'tx_time': tx.updated_on.strftime('%H:%M:%S')
                })
            sender = '%s <no-reply@%s>' % (weblet.project_name, weblet.domain)
            msg = EmailMessage(subject, html_content, sender, [payer_email])
            msg.bcc = [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("%s - Failed to send notice mail to %s." %
                         (weblet, email),
                         exc_info=True)

    # 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")
Exemple #6
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))
Exemple #7
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