Esempio n. 1
0
def webhook_invoice_chargeback(iID, amount, date, mollie_payment_id,
                               mollie_chargeback_id, note):
    """
    Actuall add chargeback invoice payment
    This function is separate for testability
    """
    from openstudio.os_invoice import Invoice
    invoice = Invoice(iID)

    print("note in wic")
    print(note)

    ipID = invoice.payment_add(
        amount,
        date,
        payment_methods_id=100,  # Static id for Mollie payments
        mollie_payment_id=mollie_payment_id,
        mollie_chargeback_id=mollie_chargeback_id,
        note=note)

    # Notify customer of chargeback
    cuID = invoice.get_linked_customer_id()
    os_mail = OsMail()
    msgID = os_mail.render_email_template('payment_recurring_failed')
    os_mail.send_and_archive(msgID, cuID)
Esempio n. 2
0
def upgrade_to_201906():
    """
        Upgrade operations to 2019.06
    """
    from openstudio.os_invoice import Invoice

    query = (db.customers_notes.Processed == None)
    db(query).update(Processed=True)

    # Insert missing links to customers for some subscription invoices
    left = [
        db.invoices_items.on(db.invoices_items_customers_subscriptions.
                             invoices_items_id == db.invoices_items.id),
        db.invoices.on(db.invoices_items.invoices_id == db.invoices.id),
        db.invoices_customers.on(
            db.invoices_customers.invoices_id == db.invoices.id),
        db.customers_subscriptions.on(
            db.invoices_items_customers_subscriptions.
            customers_subscriptions_id == db.customers_subscriptions.id),
        db.auth_user.on(
            db.customers_subscriptions.auth_customer_id == db.auth_user.id)
    ]

    query = (db.invoices_customers.id == None)

    rows = db(query).select(db.invoices.id,
                            db.auth_user.id,
                            db.invoices_customers.id,
                            left=left)

    for row in rows:
        invoice = Invoice(row.invoices.id)
        invoice.link_to_customer(row.auth_user.id)
    def set_status(self, status):
        """
        :param status: Set status of class booking
        :return:
        """
        from .os_cache_manager import OsCacheManager

        self.row.BookingStatus = status
        self.row.update_record()

        if status == 'cancelled':
            ##
            # Change invoice status to cancelled
            ##
            from openstudio.os_invoice import Invoice

            db = current.db
            query = (db.invoices_items_classes_attendance.classes_attendance_id
                     == self.id)
            rows = db(query).select(db.invoices_items_classes_attendance.ALL)
            for row in rows:
                item = db.invoices_items(row.invoices_items_id)

                invoice = Invoice(item.invoices_id)
                invoice.set_status('cancelled')

        # Clear api cache to refresh available spaces
        ocm = OsCacheManager()
        ocm.clear_classschedule_api()
Esempio n. 4
0
    def _list_invoices_get_balance(self, row):
        """
            Retuns the balance for an invoice
        """
        from os_invoice import Invoice

        iID = row.invoices.id
        invoice = Invoice(iID)

        return invoice.get_balance(formatted=True)
Esempio n. 5
0
    def add_get_form(self, cuID, csID=None, cmID=None, full_width=True):
        """
            Returns add form for an invoice
        """
        from os_customer import Customer
        from os_invoice import Invoice

        self._add_get_form_permissions_check()

        db = current.db
        T = current.T

        customer = Customer(cuID)
        self._add_get_form_set_default_values_customer(customer)
        self._add_get_form_enable_minimal_fields()
        if csID:
            self._add_get_form_enable_subscription_fields(csID)
        if cmID:
            self._add_get_form_enable_membership_fields(cmID)

        form = SQLFORM(db.invoices, formstyle='bootstrap3_stacked')

        elements = form.elements('input, select, textarea')
        for element in elements:
            element['_form'] = "invoice_add"

        form_element = form.element('form')
        form['_id'] = 'invoice_add'

        if form.process().accepted:
            iID = form.vars.id
            invoice = Invoice(
                iID
            )  # This sets due date and Invoice (calls invoice.on_create() #
            invoice.link_to_customer(cuID)
            self._add_reset_list_status_filter()

            if csID:
                invoice.link_to_customer_subscription(csID)
                invoice.item_add_subscription(form.vars.SubscriptionYear,
                                              form.vars.SubscriptionMonth)

            if cmID:
                invoice.item_add_membership(cmID,
                                            form.vars.MembershipPeriodStart,
                                            form.vars.MembershipPeriodEnd)

            redirect(URL('invoices', 'edit', vars={'iID': iID}))

        # So the grids display the fields normally
        for field in db.invoices:
            field.readable = True

        return form
Esempio n. 6
0
def webhook_invoice_paid(iID, payment_amount, payment_date, mollie_payment_id):
    """
        :param iID: db.invoices.id
        :return: None
    """
    invoice = Invoice(iID)

    ipID = invoice.payment_add(
        payment_amount,
        payment_date,
        payment_methods_id=100,  # Static id for Mollie payments
        mollie_payment_id=mollie_payment_id)
Esempio n. 7
0
def webhook_invoice_paid(iID, payment_amount, payment_date, mollie_payment_id):
    """
        :param iID: db.invoices.id
        :return: None
    """
    query = (db.invoices_payments.mollie_payment_id == mollie_payment_id)
    if not db(query).count():
        # Don't process a payment twice
        invoice = Invoice(iID)

        ipID = invoice.payment_add(
            payment_amount,
            payment_date,
            payment_methods_id=100,  # Static id for Mollie payments
            mollie_payment_id=mollie_payment_id)
Esempio n. 8
0
def webhook_invoice_refund(iID, amount, date, mollie_payment_id,
                           mollie_refund_id, note):
    """
    Actually add refund invoice payment
    This function is separate for testability
    """
    from openstudio.os_invoice import Invoice
    invoice = Invoice(iID)

    ipID = invoice.payment_add(
        amount,
        date,
        payment_methods_id=100,  # Static id for Mollie payments
        mollie_payment_id=mollie_payment_id,
        mollie_refund_id=mollie_refund_id,
        note=note)
Esempio n. 9
0
def upgrade_to_20183():
    """
        Upgrade operations to 2018.3
    """
    ##
    # Set invoice customer data
    ##
    query = (db.invoices_customers.id > 0)

    rows = db(query).select(db.invoices_customers.ALL)
    for row in rows:
        invoice = Invoice(row.invoices_id)
        invoice.set_customer_info(row.auth_customer_id)

    ##
    # clear cache
    ##
    cache.ram.clear(regex='.*')
Esempio n. 10
0
    def sell_to_customer_create_invoice(self, cmID):
        """
        Add an invoice after adding a membership
        :param cmID: db.customers_memberships.id
        :return: db.invoices.id
        """
        from openstudio.os_customer_membership import CustomerMembership
        from openstudio.os_invoice import Invoice
        
        db = current.db
        T = current.T

        cm = CustomerMembership(cmID)

        # Check if price exists and > 0:
        if self.row.Price:
            igpt = db.invoices_groups_product_types(ProductType='membership')

            iID = db.invoices.insert(
                invoices_groups_id=igpt.invoices_groups_id,
                Description=cm.get_name(),
                Status='sent',
                payment_methods_id=cm.row.payment_methods_id
            )

            invoice = Invoice(iID)
            invoice.link_to_customer(cm.row.auth_customer_id)
            invoice.item_add_membership(
                cmID,
            )

            return iID
        else:
            return None
Esempio n. 11
0
    def sell_to_customer_create_invoice(self, cmID):
        """
        Add an invoice after adding a membership
        :param cmID: db.customers_memberships.id
        :return: db.invoices.id
        """
        from openstudio.os_customer_membership import CustomerMembership
        from openstudio.os_invoice import Invoice

        db = current.db
        T = current.T

        cm = CustomerMembership(cmID)

        # Check if price exists and > 0:
        if self.get_price_on_date(cm.row.Startdate):
            period_start = cm.row.Startdate
            period_end = cm.get_period_enddate(cm.row.Startdate)

            igpt = db.invoices_groups_product_types(ProductType='membership')

            iID = db.invoices.insert(
                invoices_groups_id=igpt.invoices_groups_id,
                Description=cm.get_name(),
                MembershipPeriodStart=period_start,
                MembershipPeriodEnd=period_end,
                Status='sent')

            invoice = Invoice(iID)
            invoice.link_to_customer(cm.row.auth_customer_id)
            invoice.item_add_membership(cmID, period_start, period_end)

            return iID
        else:
            return None
Esempio n. 12
0
def webhook_invoice_chargeback(iID, chargeback_amount, chargeback_date,
                               mollie_payment_id, chargeback_id,
                               chargeback_details):
    """
    Chargebacks happen when a direct debit payment fails due to insufficient funds in the customers' bank account
    :return:
    """
    invoice = Invoice(iID)

    ipID = invoice.payment_add(
        chargeback_amount,
        chargeback_date,
        payment_methods_id=100,  # Static id for Mollie payments
        mollie_payment_id=mollie_payment_id,
        note="Mollie Chargeback (%s) - %s" %
        (chargeback_id, chargeback_details))

    # Notify customer of chargeback
    cuID = invoice.get_linked_customer_id()
    os_mail = OsMail()
    msgID = os_mail.render_email_template('payment_recurring_failed')
    os_mail.send(msgID, cuID)
Esempio n. 13
0
def invoice_pay():
    """
        Link to mollie payment page from invoice payment
    """
    from openstudio.os_customer import Customer

    #response.title = T("Pay invoice")
    iID = request.vars['iID']

    invoice = Invoice(iID)
    invoice_amounts = invoice.get_amounts()

    if not invoice.get_linked_customer_id() == auth.user.id:
        return 'Not authorized'

    mollie = Mollie.API.Client()
    mollie_api_key = get_sys_property('mollie_website_profile')
    mollie.setApiKey(mollie_api_key)

    description = invoice.invoice.Description + ' - ' + invoice.invoice.InvoiceID
    recurring_type = None
    mollie_customer_id = None

    # Subscription invoice?
    if invoice.get_linked_customer_subscription_id():
        # subscription invoice
        # customer = Customer(auth.user.id)
        # mollie_customer_id = customer.row.mollie_customer_id
        # check if we have a mollie customer id
        os_customer = Customer(auth.user.id)
        if os_customer.row.mollie_customer_id:
            # yep
            mollie_customer_id = os_customer.row.mollie_customer_id
            try:
                mollie_customer = mollie.customers.get(mollie_customer_id)
                # print "we've got one!"
                # print mollie_customer
                # print mollie.customers.all()
            except Exception as e:
                # print e.__class__.__name__
                # print str(e)
                # print 'customer id invalid, create new customer'
                if 'The customer id is invalid' in str(e):
                    create_mollie_customer(auth.user.id, mollie)
                    os_customer = Customer(auth.user.id) # refresh
        else:
            create_mollie_customer(auth.user.id, mollie)
            os_customer = Customer(auth.user.id) # refresh

        mandates = os_customer.get_mollie_mandates()
        # set default recurring type, change to recurring if a valid mandate is found.
        recurring_type = 'first'
        if mandates['count'] > 0:
            # background payment
            valid_mandate = False
            for mandate in mandates:
                if mandate['status'] == 'valid':
                    valid_mandate = True
                    break

            if valid_mandate:
                # Do a normal payment, probably an automatic payment failed somewhere in the process
                # and customer should pay manually now
                recurring_type = None

    # Do a regular payment or first recurring payment
    try:
        webhook_url = 'https://' + request.env.http_host + '/mollie/webhook'
        payment = mollie.payments.create({
            'amount':      invoice_amounts.TotalPriceVAT,
            'description': description,
            'recurringType': recurring_type,
            'customerId': mollie_customer_id,
            'redirectUrl': 'https://' + request.env.http_host + '/shop/complete?iID=' + unicode(iID),
            'webhookUrl': webhook_url,
            'metadata': {
                'invoice_id': invoice.invoice.id,
                'customers_orders_id': 'invoice' # This lets the webhook function know it's dealing with an invoice
            }
        })

        db.invoices_mollie_payment_ids.insert(
            invoices_id=iID,
            mollie_payment_id=payment['id'],
            RecurringType=recurring_type,
            WebhookURL=webhook_url
        )

        # Send the customer off to complete the payment.
        redirect(payment.getPaymentUrl())

    except Mollie.API.Error as e:
        return 'API call failed: ' + e.message
def task_mollie_subscription_invoices_and_payments():
    """
        Create subscription invoices for subscriptions with payment method 100
        Collect payment for these invoices
    """
    def send_mail_failed(cuID):
        """
            When a recurring payment fails, mail customer with request to pay manually
        """
        os_mail = OsMail()
        msgID = os_mail.render_email_template('payment_recurring_failed')
        os_mail.send_and_archive(msgID, cuID)

    from openstudio.os_customer import Customer

    # hostname
    sys_hostname = get_sys_property('sys_hostname')
    # set up Mollie
    mollie = Client()
    mollie_api_key = get_sys_property('mollie_website_profile')
    mollie.set_api_key(mollie_api_key)
    # set dates
    today = datetime.date.today()
    firstdaythismonth = datetime.date(today.year, today.month, 1)
    lastdaythismonth = get_last_day_month(firstdaythismonth)

    # call some function to do stuff

    # find all active subscriptions with payment method 100 (Mollie)
    query = (db.customers_subscriptions.payment_methods_id == 100) & \
            (db.customers_subscriptions.Startdate <= lastdaythismonth) & \
            ((db.customers_subscriptions.Enddate >= firstdaythismonth) |
             (db.customers_subscriptions.Enddate == None))
    rows = db(query).select(db.customers_subscriptions.ALL)

    success = 0
    failed = 0

    # create invoices
    for i, row in enumerate(rows):
        cs = CustomerSubscription(row.id)
        # This function returns the invoice id if it already exists
        iID = cs.create_invoice_for_month(TODAY_LOCAL.year, TODAY_LOCAL.month)

        #print 'invoice created'
        #print iID

        # Do we have an invoice?
        if not iID:
            continue

        invoice = Invoice(iID)
        # Only do something if the invoice status is sent
        if not invoice.invoice.Status == 'sent':
            continue

        # We're good, continue processing
        invoice_amounts = invoice.get_amounts()
        #print invoice.invoice.InvoiceID
        description = invoice.invoice.Description + ' - ' + invoice.invoice.InvoiceID
        db.commit()

        #create recurring payments using mandates
        #subscription invoice
        customer = Customer(row.auth_customer_id)
        mollie_customer_id = customer.row.mollie_customer_id
        mandates = customer.get_mollie_mandates()
        valid_mandate = False
        # set default recurring type, change to recurring if a valid mandate is found.
        if mandates['count'] > 0:
            # background payment
            for mandate in mandates['_embedded']['mandates']:
                if mandate['status'] == 'valid':
                    valid_mandate = True
                    break

            if valid_mandate:
                # Create recurring payment
                try:
                    webhook_url = URL('mollie',
                                      'webhook',
                                      scheme='https',
                                      host=sys_hostname)
                    payment = mollie.payments.create({
                        'amount': {
                            'currency': CURRENCY,
                            'value': str(invoice_amounts.TotalPriceVAT)
                        },
                        'customerId': mollie_customer_id,
                        'sequenceType': 'recurring',  # important
                        'description': description,
                        'webhookUrl': webhook_url,
                        'metadata': {
                            'invoice_id': invoice.invoice.id,
                            'customers_orders_id':
                            'invoice'  # This lets the webhook function know it's dealing with an invoice
                        }
                    })

                    # link invoice to mollie_payment_id
                    db.invoices_mollie_payment_ids.insert(
                        invoices_id=invoice.invoice.id,
                        mollie_payment_id=payment['id'],
                        RecurringType='recurring',
                        WebhookURL=webhook_url)

                    success += 1

                except MollieError as e:
                    print(e)
                    # send mail to ask customer to pay manually
                    send_mail_failed(cs.auth_customer_id)

                    failed += 1
                    # return error
                    # return 'API call failed: ' + e.message
        else:
            # send mail to ask customer to pay manually
            send_mail_failed(cs.auth_customer_id)

            failed += 1

    # For scheduled tasks, db has to be committed manually
    db.commit()

    return T("Payments collected") + ': ' + str(success) + '<br>' + \
        T("Payments failed to collect") + ': ' + str(failed)
Esempio n. 15
0
    def batch_generate_teachers_invoices(self, year, month):
        """
        :return: Int - number of generated invoices
        """
        from general_helpers import NRtoMonth
        from os_teacher import Teacher
        from os_teachers import Teachers
        from openstudio.os_invoice import Invoice
        import datetime

        db = current.db
        T = current.T

        # Get list of teachers
        teachers = Teachers()
        result = teachers.get_teachers_list_classes_in_month(year, month)

        #print teacher_classes
        invoices_created = 0

        for teID in result:
            teacher_classes = result[teID]['classes']
            number_of_classes = result[teID]['classes_count']
            if not number_of_classes:
                continue

            teacher = Teacher(teID)
            default_rate = teacher.get_payment_fixed_rate_default()

            if not default_rate:
                continue  # No default rate, not enough data to create invoice

            # Check if we have an invoice already, if so, skip
            query = (db.invoices_customers.auth_customer_id == teID) & \
                    (db.invoices.TeacherPayment == True) & \
                    (db.invoices.TeacherPaymentMonth == month) & \
                    (db.invoices.TeacherPaymentYear == year) & \
                    (db.invoices_customers.invoices_id == db.invoices.id)

            if db(query).count():
                continue

            # Create invoice
            invoices_created += 1
            igpt = db.invoices_groups_product_types(
                ProductType='teacher_payments')
            iID = db.invoices.insert(
                invoices_groups_id=igpt.invoices_groups_id,
                TeacherPayment=True,
                TeacherPaymentMonth=month,
                TeacherPaymentYear=year,
                Description=T('Classes') + ' ' + NRtoMonth(month) + ' ' +
                unicode(year),
                Status='sent')

            invoice = Invoice(iID)
            invoice.link_to_customer(teID)

            for date, rows in sorted(teacher_classes.iteritems()):
                prev_class_end = datetime.datetime(date.year, date.month,
                                                   date.day, 0, 0)
                for row in rows:
                    ## Check not adding travel allowance for consecutive classes
                    class_start = datetime.datetime(
                        date.year, date.month, date.day,
                        row.classes.Starttime.hour,
                        row.classes.Starttime.minute)

                    consecutive = class_start <= (
                        prev_class_end + datetime.timedelta(minutes=30))

                    if not consecutive:
                        invoice.item_add_teacher_class_credit_travel_allowance(
                            row.classes.id, date)

                    prev_class_end = datetime.datetime(
                        date.year, date.month, date.day,
                        row.classes.Endtime.hour, row.classes.Endtime.minute)

                    # Add class
                    invoice.item_add_teacher_class_credit_payment(
                        row.classes.id, date)

            invoice.set_amounts()

        return invoices_created