def payment_travel(): """ Configure travel allowance for teachers """ from openstudio.os_customer import Customer from openstudio.os_teacher import Teacher teID = request.vars['teID'] response.view = 'general/only_content.html' customer = Customer(teID) response.title = customer.get_name() response.subtitle = T("Travel allowance") teacher = Teacher(teID) content = teacher.get_payment_travel_display() add = os_gui.get_button('add', URL('teachers', 'payment_travel_add', vars={'teID': teID}), _class='pull-right') back = back_index() return dict(content=content, header_tools=add, back=back)
def privacy_get_documents(var=None): """ returns list of documents for customer """ from openstudio.os_customer import Customer customer = Customer(auth.user.id) rows = customer.get_documents_rows() if not len(rows): return '' header = THEAD( TR( TH(T("Description")), TH(T("Download")), ) ) table = TABLE(header, _class='table table-striped table-hover') for row in rows: tr = TR( TD(row.Description), TD(A(T("Download"), _href=URL('default', 'download', args=row.DocumentFile))) ) table.append(tr) documents = DIV( H4(T('Files')), table ) return documents
def payment_fixed_rate_class_add(): """ Add customers to attendance for a class """ from openstudio.os_customer import Customer from openstudio.os_customers import Customers from general_helpers import datestr_to_python response.view = 'general/only_content.html' teID = request.vars['teID'] customer = Customer(teID) response.title = customer.get_name() response.subtitle = T("Add class payment rate") if 'date' in request.vars: date = datestr_to_python(DATE_FORMAT, request.vars['date']) else: date = TODAY_LOCAL customers = Customers() result = customers.classes_add_get_form_date(teID, date) form = result['form'] form_date = result['form_styled'] db.classes.id.readable = False # list of classes grid = customers.classes_add_get_list(date, 'tp_fixed_rate', teID=teID) back = os_gui.get_button('back', URL('payment_fixed_rate', vars={'teID': teID}), _class='left') return dict(content=DIV(form_date, grid), back=back)
def payment_fixed_rate(): """ Configure fixed rate payments for this teacher """ from openstudio.os_customer import Customer from openstudio.os_teacher import Teacher teID = request.vars['teID'] response.view = 'general/only_content.html' customer = Customer(teID) response.title = customer.get_name() response.subtitle = T("Payments") teacher = Teacher(teID) content = DIV( teacher.get_payment_fixed_rate_default_display(), teacher.get_payment_fixed_rate_classes_display() ) back = back_index() return dict(content=content, #menu=menu, back=back)
def get_classcards(self, auth_user_id=None, public_only=True): """ :param public_only: Defines whether or not to show only public classcards, True by default False means all cards are returned Returns classcards for school """ from .tools import OsTools db = current.db os_tools = OsTools() allow_trial_for_existing_customers = os_tools.get_sys_property( 'shop_allow_trial_cards_for_existing_customers') query = (db.school_classcards.Archived == False) if public_only: query &= (db.school_classcards.PublicCard == True) if auth_user_id and allow_trial_for_existing_customers != 'on': from .os_customer import Customer customer = Customer(auth_user_id) existing_customer = customer.get_has_or_had_subscription_or_classcard( ) if existing_customer: query &= (db.school_classcards.Trialcard == False) return db(query).select(db.school_classcards.ALL, orderby=db.school_classcards.Trialcard | db.school_classcards.Name)
def payment_travel_edit(): """ Add travel allowance for a teacher """ from openstudio.os_customer import Customer from openstudio.os_forms import OsForms teID = request.vars['teID'] tpfrtID = request.vars['tpfrtID'] customer = Customer(teID) response.title = customer.get_name() response.subtitle = T("Edit travel allowance") response.view = 'general/only_content.html' return_url = payment_travel_return_url(teID) os_forms = OsForms() result = os_forms.get_crud_form_update( db.teachers_payment_travel, return_url, tpfrtID ) form = result['form'] back = os_gui.get_button('back', return_url) return dict(content=form, save=result['submit'], back=back)
def payment_fixed_rate_class(): """ Add default fixed rate for this teacher """ from openstudio.os_customer import Customer from openstudio.os_teacher import Teacher from openstudio.os_forms import OsForms teID = request.vars['teID'] clsID = request.vars['clsID'] response.view = 'general/only_content.html' customer = Customer(teID) response.title = customer.get_name() response.subtitle = T("Set class rate") record = db.classes(clsID) location = db.school_locations[record.school_locations_id].Name classtype = db.school_classtypes[record.school_classtypes_id].Name class_name = NRtoDay(record.Week_day) + ' ' + \ record.Starttime.strftime(TIME_FORMAT) + ' - ' + \ classtype + ', ' + location os_forms = OsForms() return_url = payment_fixed_rate_default_return_url(teID) db.teachers_payment_fixed_rate_class.auth_teacher_id.default = teID db.teachers_payment_fixed_rate_class.classes_id.default = clsID teacher = Teacher(teID) payment = db.teachers_payment_fixed_rate_class( classes_id = clsID, auth_teacher_id = teID ) if payment: title = H4(T('Edit class rate for'), ' ', class_name) result = os_forms.get_crud_form_update( db.teachers_payment_fixed_rate_class, return_url, payment.id ) else: title = H4(T('Add class rate for'), ' ', class_name) result = os_forms.get_crud_form_create( db.teachers_payment_fixed_rate_class, return_url, ) form = result['form'] content = DIV( title, form ) back = os_gui.get_button('back', return_url) return dict(content=content, back=back, save=result['submit'])
def events(): """ Page to show list of workshops with invoices for a customer """ from openstudio.os_customer import Customer response.title = T('Profile') response.subtitle = T('Events') # response.view = 'shop/index.html' features = db.customers_profile_features(1) if not features.Workshops: redirect(URL('profile', 'index')) customer = Customer(auth.user.id) rows = customer.get_workshops_rows() #link_shop = A(T('Book workshops'), _href=URL('shop', 'workshops')) link_shop = '' features = db.customers_shop_features(1) if features.Workshops: link_shop = A(SPAN(os_gui.get_fa_icon('fa-shopping-cart'), ' ', T('Book workshop'), BR(), BR()), _href=URL('shop', 'workshops')) back = os_gui.get_button('back', URL('profile', 'index')) return dict(rows=rows, link_shop=link_shop, back=back)
def upgrade_to_201881(): """ Upgrade operations to 2018.81 """ from openstudio.os_customer import Customer ## # Link enrollments to subscriptions, where possible. ## # List all active enrollments query = ((db.classes_reservation.Enddate >= TODAY_LOCAL) | (db.classes_reservation.Enddate == None)) & \ (db.classes_reservation.ResType == 'recurring') rows = db(query).select(db.classes_reservation.ALL) for row in rows: customer = Customer(row.auth_customer_id) # Check if customer has subscriptions today subscriptions = customer.get_subscriptions_on_date(TODAY_LOCAL) # If 1: link # Else pass; 0 - nothing to link & 2 - no way to be certain which one if subscriptions and len(subscriptions) == 1: row.customers_subscriptions_id = subscriptions.first( ).customers_subscriptions.id row.update_record() # set CustomerMembership field for db.classes_attendance query = (db.classes_attendance.CustomerMembership == None) db(query).update(CustomerMembership=False) ## # Migrate teachers_payment_fixed_rate_travel to # teachers_payment_travel ## define_teachers_payment_fixed_rate_travel() rows = db(db.teachers_payment_fixed_rate_travel).select( db.teachers_payment_fixed_rate_travel.ALL) for row in rows: db.teachers_payment_travel.insert( auth_teacher_id=row.auth_teacher_id, school_locations_id=row.school_locations_id, TravelAllowance=row.TravelAllowance, tax_rates_id=row.tax_rates_id) ## # Set fixed rate as default payment system ## from openstudio.os_setup import OsSetup setup = OsSetup() setup._setup_teachers_payment_rate_type()
def payment_fixed_rate_default(): """ Add default fixed rate for this teacher """ from openstudio.os_customer import Customer from openstudio.os_teacher import Teacher from openstudio.os_forms import OsForms teID = request.vars['teID'] response.view = 'general/only_content.html' customer = Customer(teID) response.title = customer.get_name() response.subtitle = T("Teacher profile") os_forms = OsForms() return_url = payment_fixed_rate_default_return_url(teID) db.teachers_payment_fixed_rate_default.auth_teacher_id.default = teID teacher = Teacher(teID) default_payments = teacher.get_payment_fixed_rate_default() if default_payments: title = H4(T('Edit default rate')) result = os_forms.get_crud_form_update( db.teachers_payment_fixed_rate_default, return_url, default_payments.first().id ) else: title = H4(T('Add default rate')) result = os_forms.get_crud_form_create( db.teachers_payment_fixed_rate_default, return_url, ) form = result['form'] content = DIV( title, form ) back = os_gui.get_button('back', return_url) return dict(content=content, #menu=menu, back=back, save=result['submit'])
def enrollments(): """ List recurring class reservations for customers """ from openstudio.os_customer import Customer response.title = T('Profile') response.subtitle = T('Enrollments') response.view = 'shop/index.html' customer = Customer(auth.user.id) header = THEAD(TR( TH(T('Weekday')), TH(T('Time')), TH(T('Location')), TH(T('Class')), TH(T('Start')), TH(T('End')), )) table = TABLE(header, _class='table table-hover table-striped') rows = customer.get_reservations_rows() for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] enddate = repr_row.classes_reservation.Enddate if not row.classes_reservation.Enddate: enddate = A(T("End enrollment"), _href=URL('enrollment_end', args=row.classes_reservation.id)) table.append(TR( TD(repr_row.classes.Week_day), TD(repr_row.classes.Starttime, ' - ', repr_row.classes.Endtime), TD(repr_row.classes.school_locations_id), TD(repr_row.classes.school_classtypes_id), TD(repr_row.classes_reservation.Startdate), TD(enddate), )) content = table back = os_gui.get_button('back', enrollments_get_back()) return dict(content=content, back=back)
def get_class_booking_options(): """ List booking options for a class for a given customer :return: """ from openstudio.os_attendance_helper import AttendanceHelper from openstudio.os_customer import Customer clsID = request.vars['clsID'] cuID = request.vars['cuID'] set_headers() customer = Customer(cuID) complementary_permission = (auth.has_membership(group_id='Admins') or auth.has_permission('complementary', 'classes_attendance')) ah = AttendanceHelper() options = ah.get_customer_class_booking_options( clsID, TODAY_LOCAL, customer, trial=True, complementary=complementary_permission, list_type='attendance') return dict(options=options)
def staff_payments_get_content(var=None): """ :param var: :return: """ from openstudio.os_customer import Customer customer = Customer(auth.user.id) rows = customer.get_invoices_rows( public_group=False, payments_only=True ) header = THEAD(TR( TH(T('Invoice #')), TH(T('Date')), TH(T('Due')), TH(T('Amount')), TH(T('Status')), TH(), )) table = TABLE(header, _class='table table-striped table-hover') for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] pdf = os_gui.get_button( 'print', URL('invoices', 'pdf', vars={'iID':row.invoices.id}), btn_size='', _class='pull-right' ) table.append(TR( TD(row.invoices.InvoiceID), TD(repr_row.invoices.DateCreated), TD(repr_row.invoices.DateDue), TD(repr_row.invoices_amounts.TotalPriceVAT), TD(repr_row.invoices.Status), TD(pdf) )) return table
def invoices(): """ Shows all invoices for a customer """ from openstudio.os_customer import Customer response.title = T('Invoices') response.subtitle = '' #response.view = 'shop/index.html' features = db.customers_profile_features(1) if not features.Invoices: redirect(URL('profile', 'index')) customer = Customer(auth.user.id) rows = customer.get_invoices_rows() back = os_gui.get_button('back', URL('profile', 'orders'), _class='btn-link') return dict(rows = rows, back=back)
def checkin_booking_options(): """ List booking options for a customer """ cuID = request.vars['cuID'] clsID = request.vars['clsID'] date_formatted = request.vars['date'] date = datestr_to_python(DATE_FORMAT, date_formatted) customer = Customer(cuID) cls = Class(clsID, date) starttime = cls.cls.Starttime.strftime(TIME_FORMAT) pretty_date = date.strftime('%B %d, %Y') classtype = db.school_classtypes(cls.cls.school_classtypes_id) location = db.school_locations(cls.cls.school_locations_id) response.title = T(classtype.Name) response.subtitle = SPAN(starttime, ' ', pretty_date) response.view = 'selfcheckin/checkin.html' return_url = URL('selfcheckin', 'checkin', vars={ 'clsID': clsID, 'date': date_formatted }) cls = Class(clsID, date) ah = AttendanceHelper() options = ah.get_customer_class_booking_options_formatted( clsID, date, customer, trial=True, list_type='selfcheckin', controller='classes') cancel = os_gui.get_button('noicon', return_url, title=T('Cancel'), btn_size='') content = DIV(H3(T('Booking options for'), ' ', customer.row.display_name, _class='center'), BR(), options, DIV(BR(), cancel, _class="col-md-12 center"), _class="row") back = os_gui.get_button('back', return_url) return dict(content=content, back=back)
def index(): """ Main page for customers portal """ from openstudio.os_customer import Customer response.title = T('Welcome') response.subtitle = auth.user.display_name upcoming_workshops = '' classcards = '' session.profile_class_cancel_confirm_back = 'profile_index' session.profile_subscription_credits_back = 'profile_index' # announcements announcements = index_get_announcements() # cutomer content customer_content = DIV(_class='row') cc_left = DIV(_class='col-md-6') cc_right = DIV(_class='col-md-6') features = db.customers_profile_features(1) customer = Customer(auth.user.id) if features.Classes: upcoming_classes = index_get_upcoming_classes(customer) cc_left.append(DIV(upcoming_classes, _class='no-padding-left no-padding-right col-md-12')) if features.Workshops: upcoming_events = index_get_upcoming_events(customer) cc_right.append(DIV(upcoming_events, _class='no-padding-left no-padding-right col-md-12')) if features.Classcards: classcards = index_get_classcards(customer) cc_left.append(DIV(classcards, _class='no-padding-left no-padding-right col-md-12')) if features.Subscriptions: subscriptions = index_get_subscriptions(customer) cc_right.append(DIV(subscriptions, _class='no-padding-left no-padding-right col-md-12')) if features.Memberships: memberships = index_get_memberships(customer) cc_left.append(DIV(memberships, _class='no-padding-left no-padding-right col-md-12')) customer_content.append(cc_left) customer_content.append(cc_right) content = DIV(announcements, customer_content) return dict(content=content)
def create_mollie_customer(auth_user_id, mollie): """ :param auth_user_id: db.auth_user.id :param mollie: mollie api client object :return: """ from openstudio.os_customer import Customer os_customer = Customer(auth_user_id) if not mollie_customer_check_valid(os_customer): mollie_customer = mollie.customers.create({ 'name': os_customer.row.display_name, 'email': os_customer.row.email }) os_customer.row.mollie_customer_id = mollie_customer['id'] os_customer.row.update_record()
def user_register_log_acceptance(form): """ Log acceptance of general terms, privacy policy and true data """ from openstudio.os_customer import Customer cuID = form.vars.id customer = Customer(cuID) reg_url = URL('default', 'user', args='register', scheme=True, host=True) organization = ORGANIZATIONS[ORGANIZATIONS['default']] if organization: user_register_log_acceptance_terms_and_conditions( customer, organization, reg_url) user_register_log_acceptance_privacy_notice(customer, organization, reg_url) user_register_log_acceptance_true_data(customer, reg_url)
def get_prices_customer(self, cuID, force_membership_price=False): """ Returns the price for a class :param cuID: db.auth_user.id :return: dict of class prices """ from openstudio.os_customer import Customer db = current.db customer = Customer(cuID) prices = self.get_prices() has_membership = False if prices['school_memberships_id']: has_membership = customer.has_given_membership_on_date( prices['school_memberships_id'], self.date ) trial = prices['trial'] trial_tax = db.tax_rates(prices['trial_tax_rates_id']) dropin = prices['dropin'] dropin_tax = db.tax_rates(prices['dropin_tax_rates_id']) dropin_tax_rates_id = prices['dropin_tax_rates_id'] dropin_tax_percentage = prices['dropin_tax_percentage'] trial_tax_rates_id = prices['trial_tax_rates_id'] trial_tax_percentage = prices['trial_tax_percentage'] dropin_glaccount = prices['dropin_glaccount'] trial_glaccount = prices['trial_glaccount'] dropin_costcenter = prices['dropin_costcenter'] trial_costcenter = prices['trial_costcenter'] if prices['dropin_membership'] and (has_membership or force_membership_price): dropin = prices['dropin_membership'] dropin_tax = db.tax_rates(prices['dropin_tax_rates_id_membership']) if prices['trial_membership'] and (has_membership or force_membership_price): trial = prices['trial_membership'] trial_tax = db.tax_rates(prices['trial_tax_rates_id_membership']) try: dropin_tax_rates_id = dropin_tax.id dropin_tax_percentage = dropin_tax.Percentage except AttributeError: pass try: trial_tax_rates_id = trial_tax.id trial_tax_percentage = trial_tax.Percentage except AttributeError: pass return dict( trial = trial, dropin = dropin, trial_tax_rates_id = trial_tax_rates_id, dropin_tax_rates_id = dropin_tax_rates_id, trial_tax_percentage = trial_tax_percentage, dropin_tax_percentage = dropin_tax_percentage, dropin_glaccount = dropin_glaccount, dropin_costcenter = dropin_costcenter, trial_glaccount = trial_glaccount, trial_costcenter = trial_costcenter )
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 edit(): """ :return: shows order """ response.title = T('Order #') + request.vars['coID'] response.subtitle = T('Edit') response.view = 'general/only_content.html' coID = request.vars['coID'] order = Order(coID) cuID = order.order.auth_customer_id customer = Customer(cuID) # Info table info = TABLE(THEAD( TR( TH(T('Customer')), TH(T('Ordered on')), TH(T('Status')), )), _class='table') # Display status for field in db.customers_orders: field.readable = False field.writable = False db.customers_orders.Status.readable = True db.customers_orders.Status.writable = True crud.messages.record_updated = T('Saved') form = crud.update(db.customers_orders, coID) result = set_form_id_and_get_submit_button(form, 'MainForm') form = result['form'] submit = result['submit'] form = DIV( XML('<form id="MainForm" action="#" enctype="multipart/form-data" method="post">' ), form.custom.widget.Status, form.custom.end) #status = form # status = represent_customers_orders_status(order.order.Status, order.order) # Display ordered on ordered_on = represent_datetime(order.order.DateCreated, order.order) customer_link = A(customer.get_name(), _href=URL('customers', 'edit', args=customer.row.id)) info.append(TR(TD(customer_link), TD(ordered_on), TD(form))) # Info content content = DIV(DIV(info, _class='col-md-8 no-padding-left')) # Display items rows = order.get_order_items_rows() header = THEAD( TR( TH(T('Product')), TH(T('Description')), TH(SPAN(T('Amount incl. VAT'), _class='right')), TH(T("G/L Account")), TH(T("Cost center")), TH(), )) table = TABLE(header, _class='table table-striped table-hover order-items') for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] table.append( TR( TD(repr_row.customers_orders_items.ProductName), TD(repr_row.customers_orders_items.Description), TD( SPAN(repr_row.customers_orders_items.TotalPriceVAT, _class='right')), TD(repr_row.customers_orders_items.accounting_glaccounts_id), TD(repr_row.customers_orders_items.accounting_costcenters_id), TD(), )) # Display totals amounts = order.get_amounts() footer = TFOOT( TR( TD(), TD(B(T('Subtotal'))), TD( SPAN(CURRSYM, ' ', format(amounts.TotalPrice, '.2f'), _class='bold right')), TD()), TR( TD(), TD(B(T('VAT'))), TD( SPAN(CURRSYM, ' ', format(amounts.VAT, '.2f'), _class='bold right')), TD()), TR( TD(), TD(B(T('Total'))), TD( SPAN(CURRSYM, ' ', format(amounts.TotalPriceVAT, '.2f'), _class='bold right')), TD())) table.append(footer) content.append(table) # Customer message customer_message = '' if order.order.CustomerNote: customer_message = DIV( B(T('Customer message')), BR(), BR(), XML(order.order.CustomerNote.replace("\n", "<br>")), ) content.append(customer_message) back = os_gui.get_button('back', edit_get_return_url(cuID)) return dict(content=content, back=back, save=submit)
def orders_display(var=None): """ Returns orders display """ from openstudio.os_customer import Customer customer = Customer(auth.user.id) orders = customer.get_orders_with_items_and_amounts() content = DIV() items_header = THEAD(TR(TH(T('Product')), TH(T('Description')), TH(T('Quantity')), TH(T('Price')), TH(T('Total')))) for order in orders: cancel = '' if order['row'].customers_orders.Status == 'awaiting_payment' or \ order['row'].customers_orders.Status == 'received': onclick = "return confirm('" + \ T('Do you really want to cancel this order?') + "');" cancel = A(T('Cancel'), _href=URL('order_cancel', vars={'coID':order['row'].customers_orders.id}), _title=T('Cancel this order'), _onclick=onclick, _class='btn btn-link btn-xs') online_payment_provider = get_sys_property('online_payment_provider') pay_now = '' if order['row'].customers_orders.Status == 'awaiting_payment' and not online_payment_provider == 'disabled': pay_now = A(T('Pay now'), _href=URL('mollie', 'order_pay', vars={'coID': order['row'].customers_orders.id}), _class='btn btn-success btn-xs pull-right') invoice = '' if order['row'].invoices.id: invoice = A(I(_class="fa fa-file-pdf-o"), ' ', order['row'].invoices.InvoiceID, _href=URL('invoices', 'pdf', vars={'iID':order['row'].invoices.id}), _title=T('Save invoice as PDF')) items = TABLE(items_header, _class='table table-condensed') for i, item in enumerate(order['items']): repr_item = list(order['items'][i:i + 1].render())[0] items.append(TR(TD(item.ProductName), TD(item.Description), TD(item.Quantity), TD(repr_item.Price), TD(repr_item.TotalPriceVAT))) items.append(TR(TD(), TD(), TD(), TD(), TD(B(order['repr_row'].customers_orders_amounts.TotalPriceVAT)))) status = order['row'].customers_orders.Status repr_status = order['repr_row'].customers_orders.Status display_status = DIV(repr_status, _class='pull-right') box_class = '' if status == 'delivered': box_class = 'box-success' elif status == 'cancelled': box_class = 'box-warning' elif status == 'awaiting_payment' or status == 'received': box_class = 'box-primary' customer_message = '' if order['row'].customers_orders.CustomerNote: customer_message = DIV( T("We received the following message with your order:"), BR(), order['row'].customers_orders.CustomerNote, _class='grey' ) display_order = DIV( display_status, H5(order['repr_row'].customers_orders.DateCreated), DIV(DIV(DIV(cancel, ' ', pay_now, ' ', invoice, _class='pull-right'), H3(T('Order'), ' #', order['row'].customers_orders.id, _class='box-title'), _class='box-header with-border'), DIV(DIV(items, _class=''), customer_message, _class='box-body'), _class='box ' + box_class) ) content.append(display_order) return content
def classes(): """ Page to list classes for a customer """ from openstudio.os_customer import Customer from openstudio.os_class_attendance import ClassAttendance response.title = T('Profile') response.subtitle = T('Classes') features = db.customers_profile_features(1) if not features.Classes: redirect(URL('profile', 'index')) customer = Customer(auth.user.id) if 'all' in request.vars: limit = False link_all = '' else: limit_nr = 25 limit = limit_nr + 1 link_all = A(T('Show all'), _href=URL(vars={'all': True})) session.profile_class_cancel_confirm_back = 'profile_classes' # select 1 row extra, if we get one extra row, we need the show all link rows = customer.get_classes_attendance_rows(limit) header = THEAD(TR(TH(T('Date')), TH(T('Time')), TH(T('Class')), TH(T('Location')), TH(T('Used')), TH(), TH())) table = TABLE(header, _class='table table-striped table-hover') for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] att_type = '' if row.classes_attendance.customers_subscriptions_id: att_type = repr_row.classes_attendance.customers_subscriptions_id elif row.classes_attendance.customers_classcards_id: att_type = SPAN(row.school_classcards.Name, _title=T('Class card') + ' ' + unicode(row.classes_attendance.customers_classcards_id)) cancel = '' clatt = ClassAttendance(row.classes_attendance.id) if clatt.get_cancellation_possible() and not row.classes_attendance.BookingStatus == 'cancelled': cancel = A(T('Cancel'), _href=URL('class_cancel_confirm', vars={'clattID':row.classes_attendance.id}), _class='pull-right') status = SPAN(repr_row.classes_attendance.BookingStatus, _class='pull-right') table.append(TR(TD(repr_row.classes_attendance.ClassDate), TD(SPAN(repr_row.classes.Starttime, ' - ', repr_row.classes.Endtime)), TD(repr_row.classes.school_classtypes_id), TD(repr_row.classes.school_locations_id), TD(att_type), TD(cancel), TD(status))) # determine whether to show show all link if limit: if len(rows) <= limit_nr: link_all = '' link_shop = '' features = db.customers_shop_features(1) if features.Classes: link_shop = A(SPAN(os_gui.get_fa_icon('fa-shopping-cart'), ' ', T('Book classes')), _href=URL('shop', 'classes')) back = os_gui.get_button('back', URL('profile', 'index')) return dict(content=table, link_all=link_all, link_shop=link_shop, back=back)
def validate_cart(): """ Process shopping cart :return: """ # print request.env set_headers() error = False message = '' receipt_link = None receipt_items = None receipt_amounts = None receipt_pmID = None #If no customerID; just make receipt and update stock #if customerID; Make order, deliver order, add payment to invoice created by deliver order items = request.vars['items'] pmID = request.vars['payment_methodID'] cuID = request.vars['customerID'] print 'customerID' print type(cuID) print cuID print 'validate_cart_items:' print items # Verify items if not items: error = True message = T("No items were submitted for processing") if not error and not pmID: error = True message = T("No payment method was selected") # Verify customer doesn't already have subscription or membership if cuID and not error: from openstudio.os_customer import Customer customer = Customer(cuID) for item in items: if item['item_type'] == 'subscription': subscription_ids = customer.get_school_subscriptions_ids_on_date( TODAY_LOCAL) print 'validating subscriptions' print subscription_ids if item['data']['id'] in subscription_ids: error = True message = T("This customer already has this subscription") if item['item_type'] == 'membership': membership_ids = customer.get_school_memberships_ids_on_date( TODAY_LOCAL) print 'validating memberhsips' print membership_ids if item['data']['id'] in membership_ids: error = True message = T("This customer already has this membership") ## IMPORTANT: Get Item price & VAT info from server DB, not from Stuff submitted by Javascript. # JS can be manipulated. if not error: # IF customerID; add order; deliver invoice = None invoices_payment_id = None invoice_created = False if cuID: print 'create order' invoice = validate_cart_create_order(cuID, pmID, items) invoice_created = True # Always create payment receipt print 'create receipt' receipt = validate_cart_create_receipt( invoice_created, invoice, pmID, items, ) receipt_link = URL('finance', 'receipt', vars={'rID': receipt.receipts_id}, extension='', scheme=True, host=True) receipt_items = receipt.get_receipt_items_rows() print receipt_items receipt_amounts = receipt.get_amounts() receipt_pmID = receipt.row.payment_methods_id return dict(error=error, message=message, receipt_link=receipt_link, receipt_items=receipt_items, receipt_amounts=receipt_amounts, receipt_payment_methods_id=receipt_pmID)
def get_prices_customer(self, cuID): """ Returns the price for a class :param cuID: db.auth_user.id :return: dict of class prices """ from openstudio.os_customer import Customer db = current.db customer = Customer(cuID) has_membership = customer.has_membership_on_date(self.date) dropin = 0 trial = 0 trial_tax_rates_id = None dropin_tax_rates_id = None trial_tax_percentage = None dropin_tax_percentage = None dropin_glaccount = None trial_glaccount = None dropin_costcenter = None trial_costcenter = None query = (db.classes_price.classes_id == self.clsID) & \ (db.classes_price.Startdate <= self.date) & \ ((db.classes_price.Enddate >= self.date) | (db.classes_price.Enddate == None)) prices = db(query).select(db.classes_price.ALL, orderby=db.classes_price.Startdate) if prices: prices = prices.first() dropin_glaccount = prices.accounting_glaccounts_id_dropin trial_glaccount = prices.accounting_glaccounts_id_trial dropin_costcenter = prices.accounting_costcenters_id_dropin trial_costcenter = prices.accounting_costcenters_id_trial dropin = prices.Dropin or 0 trial = prices.Trial or 0 dropin_tax = db.tax_rates(prices.tax_rates_id_dropin) trial_tax = db.tax_rates(prices.tax_rates_id_trial) if has_membership and prices.DropinMembership: dropin = prices.DropinMembership dropin_tax = db.tax_rates( prices.tax_rates_id_dropin_membership) if has_membership and prices.TrialMembership: trial = prices.TrialMembership trial_tax = db.tax_rates(prices.tax_rates_id_trial_membership) try: dropin_tax_rates_id = dropin_tax.id dropin_tax_percentage = dropin_tax.Percentage except AttributeError: pass try: trial_tax_rates_id = trial_tax.id trial_tax_percentage = trial_tax.Percentage except AttributeError: pass return dict(trial=trial, dropin=dropin, trial_tax_rates_id=trial_tax_rates_id, dropin_tax_rates_id=dropin_tax_rates_id, trial_tax_percentage=trial_tax_percentage, dropin_tax_percentage=dropin_tax_percentage, dropin_glaccount=dropin_glaccount, dropin_costcenter=dropin_costcenter, trial_glaccount=trial_glaccount, trial_costcenter=trial_costcenter)
def get_subscriptions_formatted(self, auth_customer_id=None, per_row=3, public_only=True, link_type='shop'): """ :param public: boolean, defines whether to show only public or all subscriptions :return: list of school_subscriptions formatted for shop """ from general_helpers import get_last_day_month from openstudio.os_school_subscription import SchoolSubscription from openstudio.os_customer import Customer T = current.T TODAY_LOCAL = current.TODAY_LOCAL os_gui = current.globalenv['os_gui'] get_sys_property = current.globalenv['get_sys_property'] customer_has_membership = False customer_subscriptions_ids = [] if auth_customer_id: startdate = TODAY_LOCAL shop_subscriptions_start = get_sys_property( 'shop_subscriptions_start') if not shop_subscriptions_start == None: if shop_subscriptions_start == 'next_month': startdate = get_last_day_month( TODAY_LOCAL) + datetime.timedelta(days=1) customer = Customer(auth_customer_id) customer_has_membership = customer.has_membership_on_date( startdate) customer_subscriptions_ids = customer.get_school_subscriptions_ids_on_date( startdate) if per_row == 3: card_class = 'col-md-4' elif per_row == 4: card_class = 'col-md-3' else: raise ValueError('Incompatible value: per_row has to be 3 or 4') rows = self.get_subscriptions(public_only=public_only) subscriptions = DIV() display_row = DIV(_class='row') row_item = 0 for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] ssu = SchoolSubscription(row.id) name = max_string_length(row.Name, 33) classes = '' classes_unit = '' classes_text = T("Classes") if row.Unlimited: classes = T('Unlimited') classes_unit = T("Classes") elif row.SubscriptionUnit == 'week': if row.Classes == 1: classes_text = T("Class") classes = SPAN(str(row.Classes) + ' ' + classes_text) classes_unit = T("Per week") elif row.SubscriptionUnit == 'month': if row.Classes == 1: classes_text = T("Class") classes = SPAN(str(row.Classes) + ' ' + classes_text) classes_unit = T("Per month") subscription = DIV(DIV( self._get_formatted_display_widget_header( name, ssu.get_price_on_date(TODAY_LOCAL), ), DIV(DIV(T("Minimum duration"), ': ', repr_row.MinDuration, _class='col-md-12 bold'), DIV(repr_row.Description, _class='col-md-12'), _class='box-body'), DIV( DIV(DIV(DIV(H5('Payment', _class="description-header"), SPAN(T("Monthly"), _class="description-text"), _class="description-block"), _class="col-sm-4 border-right"), DIV(DIV(H5(classes, _class="description-header"), SPAN(classes_unit, _class="description-text"), _class="description-block"), _class="col-sm-4 border-right"), DIV(DIV(H5( self._get_subscriptions_formatted_button_to_cart( row.id, row.school_memberships_id, customer_subscriptions_ids), _class="description-header"), SPAN(T(""), _class="description-text"), _class="description-block"), _class="col-sm-4"), _class="row"), _class="box-footer", ), _class="box box-widget widget-user"), _class=card_class) # subscription_content = TABLE(TR(TD(T('Classes')), # TD(classes)), # TR(TD(T('Monthly')), # TD(ssu.get_price_on_date(datetime.date.today()))), # TR(TD(T('Description')), # TD(row.Description or '')), # _class='table') # # panel_class = 'box-primary' # # footer_content = '' # if link_type == 'shop': # footer_content = self._get_subscriptions_formatted_button_to_cart( # row.id, # row.MembershipRequired, # customer_has_membership, # customer_subscriptions_ids # ) # # subscription = DIV(os_gui.get_box_table(name, # subscription_content, # panel_class, # show_footer=True, # footer_content=footer_content), # _class=card_class) display_row.append(subscription) row_item += 1 if row_item == per_row or i == (len(rows) - 1): subscriptions.append(display_row) display_row = DIV(_class='row') row_item = 0 return subscriptions
def get_classcards_formatted(self, auth_user_id=None, public_only=True, per_row=3, link_type=None): """ :param public_only: show only public cards - Default: True :param per_row: Number of cards in each row - Default 4. Allowed values: [3, 4] :param link_type: Specified what kind of link will be shown in the footer of each classcard. Allowed values: ['backend', 'shop'] - backend adds a modal to choose date - shop adds a button to add the card to the shopping cart Returns classcards formatted in BS3 style """ def get_validity(row): """ takes a db.school_classcards() row as argument """ validity = SPAN(str(row.Validity), ' ') validity_in = represent_validity_units(row.ValidityUnit, row) if row.Validity == 1: # Cut the last 's" validity_in = validity_in[:-1] validity.append(validity_in) return validity from .os_customer import Customer TODAY_LOCAL = current.TODAY_LOCAL os_gui = current.globalenv['os_gui'] T = current.T customer_has_membership = False if auth_user_id: customer = Customer(auth_user_id) customer_has_membership = customer.has_membership_on_date( TODAY_LOCAL) if per_row == 3: card_class = 'col-md-4' elif per_row == 4: card_class = 'col-md-3' else: raise ValueError('Incompatible value: per_row has to be 3 or 4') rows = self.get_classcards(auth_user_id=auth_user_id, public_only=public_only) cards = DIV() display_row = DIV(_class='row') row_item = 0 for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] card_name = max_string_length(row.Name, 37) validity = get_validity(row) over_trial_times = self._get_classcards_formatted_trialcard_over_times_available( row) description = repr_row.Description btn_cart = self._get_classcards_formatted_button_to_cart( row.id, row.school_memberships_id, customer_has_membership) if over_trial_times: description = T( "You've reached the maximum number of times you can purchase this card." ) btn_cart = SPAN(os_gui.get_fa_icon('fa-ban fa-2x'), _class='grey') card = DIV(DIV(DIV( self._get_formatted_display_widget_header( card_name, repr_row.Price)), DIV(DIV(description, _class='col-md-12'), _class='box-body'), DIV( DIV(DIV(DIV(H5(validity, _class="description-header"), SPAN(T("Validity"), _class="description-text"), _class="description-block"), _class="col-sm-4 border-right"), DIV(DIV(H5(repr_row.Classes, _class="description-header"), SPAN(T("Classes"), _class="description-text"), _class="description-block"), _class="col-sm-4 border-right"), DIV(DIV(H5(btn_cart, _class="description-header"), SPAN(T(""), _class="description-text"), _class="description-block"), _class="col-sm-4"), _class="row"), _class="box-footer", ), _class="box box-widget widget-user"), _class=card_class) display_row.append(card) row_item += 1 if row_item == per_row or i == (len(rows) - 1): cards.append(display_row) display_row = DIV(_class='row') row_item = 0 return cards
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)
def order_pay(): """ Page to pay an order """ from openstudio.os_customer import Customer coID = request.vars['coID'] order = Order(coID) os_customer = Customer(auth.user.id) mollie_customer_id = None invoice_id = '' mollie_payment_id = '' # check if the order belongs to the currently logged in customer if not order.order.auth_customer_id == auth.user.id: session.flash = T("Unable to show order") redirect(URL('profile', 'index')) # Check if the order contains a class that's been fully booked in the main time if order.contains_class(): cls = order.get_class_object_order_item() if cls.get_full() or cls.get_full_bookings_shop(): redirect( URL("shop", "class_full", vars={ "clsID": cls.clsID, "date": cls.date.strftime(DATE_FORMAT) })) mollie = Client() mollie_api_key = get_sys_property('mollie_website_profile') mollie.set_api_key(mollie_api_key) amounts = order.get_amounts() # Go to Mollie for payment amount = format(amounts.TotalPriceVAT, '.2f') description = T('Order') + ' #' + str(coID) 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 mollie_customer_id = os_customer.row.mollie_customer_id else: create_mollie_customer(auth.user.id, mollie) os_customer = Customer(auth.user.id) # refresh mollie_customer_id = os_customer.row.mollie_customer_id contains_subscription = order.contains_subscription() recurring_type = None if contains_subscription: 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['_embedded']['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 try: redirect_url = 'https://' + request.env.http_host + '/shop/complete?coID=' + str( coID) payment = mollie.payments.create({ 'amount': { 'currency': CURRENCY, 'value': amount }, 'description': description, 'sequenceType': recurring_type, 'customerId': mollie_customer_id, 'redirectUrl': redirect_url, 'webhookUrl': 'https://' + request.env.http_host + '/mollie/webhook', 'metadata': { 'customers_orders_id': coID } }) db.customers_orders_mollie_payment_ids.insert( customers_orders_id=coID, mollie_payment_id=payment['id'], RecurringType=recurring_type) # Send the customer off to complete the payment. redirect(payment.checkout_url) except MollieError as e: return 'API call failed: ' + e.message