diff --git a/README.md b/README.md index 4f1ec47..9d23e4e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,22 @@ Kassa helps you register purchased membership cards and memberships. It's a tool pip install -r requirements.txt python manage.py migrate -### LDAP +## Configuration +Create a user in Dusken with the following permissions: +* dusken | User | Can view User +* dusken | Member card | Can change Member card +* dusken | Member card | Can view Member card +* dusken | Membership | Can add Membership +* dusken | Membership | Can view Membership +* dusken | Order | Can view Order + +Fetch DUSKEN_API_KEY: + curl -H "Content-Type: application/json" -X POST -d '{"username":"apiuser","password":"test"}' DUSKEN_API_URL/auth/obtain-token/ + +If developing you can disable calls to Tekstmelding in local_settings.py: + TEKSTMELDING_ENABLED = False + +## LDAP sudo docker run -e LDAP_DOMAIN=neuf.no -e LDAP_ORGANISATION="Neuf" -e LDAP_ADMIN_PASSWORD="toor" -p 389:389 -d nikolaik/openldap ldapadd -D "cn=admin,dc=neuf,dc=no" -w "toor" -f test/testdata.ldif # Testdata # Verify import diff --git a/apps/kassa/admin.py b/apps/kassa/admin.py index d9ae24b..1ad497c 100644 --- a/apps/kassa/admin.py +++ b/apps/kassa/admin.py @@ -3,8 +3,10 @@ from django.contrib import admin class KassaEventAdmin(admin.ModelAdmin): - list_display = ['id', 'event', 'card_number', 'user_phone_number', 'user_inside_id', 'created'] + list_display = ['id', 'event', 'card_number', 'user_phone_number', 'user_dusken_id', + 'user_inside_id', 'created', 'transaction_id'] list_filter = ['event'] - search_fields = ['card_number', 'user_phone_number', 'user_inside_id'] + search_fields = ['card_number', 'user_phone_number', 'user_dusken_id', + 'user_inside_id', 'transaction_id'] admin.site.register(KassaEvent, KassaEventAdmin) diff --git a/apps/kassa/forms.py b/apps/kassa/forms.py index 2df895a..4c2e005 100644 --- a/apps/kassa/forms.py +++ b/apps/kassa/forms.py @@ -14,3 +14,4 @@ class AddCardForm(forms.Form): card_number = forms.IntegerField(widget=forms.NumberInput(attrs={'autofocus': True})) phone_number = PhoneNumberField(widget=forms.TextInput(attrs={'type': 'tel'})) user_id = forms.IntegerField(widget=forms.HiddenInput(), required=False) + order_uuid = forms.IntegerField(widget=forms.HiddenInput(), required=False) diff --git a/apps/kassa/migrations/0007_kassaevent_transaction_id.py b/apps/kassa/migrations/0007_kassaevent_transaction_id.py new file mode 100644 index 0000000..a13ca64 --- /dev/null +++ b/apps/kassa/migrations/0007_kassaevent_transaction_id.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2017-07-22 12:40 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('kassa', '0006_auto_20150829_1811'), + ] + + operations = [ + migrations.AddField( + model_name='kassaevent', + name='transaction_id', + field=models.UUIDField(blank=True, null=True), + ), + ] diff --git a/apps/kassa/migrations/0008_kassaevent_user_dusken_id.py b/apps/kassa/migrations/0008_kassaevent_user_dusken_id.py new file mode 100644 index 0000000..87801c8 --- /dev/null +++ b/apps/kassa/migrations/0008_kassaevent_user_dusken_id.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2017-07-22 12:43 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('kassa', '0007_kassaevent_transaction_id'), + ] + + operations = [ + migrations.AddField( + model_name='kassaevent', + name='user_dusken_id', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/apps/kassa/models.py b/apps/kassa/models.py index 21ba3ed..571a0e2 100644 --- a/apps/kassa/models.py +++ b/apps/kassa/models.py @@ -21,7 +21,9 @@ class KassaEvent(models.Model): ) event = models.CharField(max_length=255, choices=EVENT_CHOICES) card_number = models.IntegerField(null=True, blank=True) + user_dusken_id = models.IntegerField(null=True, blank=True) user_inside_id = models.IntegerField(null=True, blank=True) user_phone_number = models.CharField(max_length=100, null=True, blank=True) + transaction_id = models.UUIDField(null=True, blank=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) diff --git a/apps/kassa/templates/kassa/register.html b/apps/kassa/templates/kassa/register.html index b3496eb..847dff4 100644 --- a/apps/kassa/templates/kassa/register.html +++ b/apps/kassa/templates/kassa/register.html @@ -18,7 +18,7 @@ {{ add_card_form|bootstrap }}
-
+
diff --git a/apps/kassa/urls.py b/apps/kassa/urls.py index dfc8466..b49b9dc 100644 --- a/apps/kassa/urls.py +++ b/apps/kassa/urls.py @@ -1,12 +1,13 @@ from django.conf.urls import url -from apps.kassa.views import register, inside_user_api, inside_card_api, check_phone_number, \ - register_card_and_membership, stats_card_sales, renew_membership +from apps.kassa.views import (register, user_search, check_card, + check_phone_number, register_card_and_membership, + stats_card_sales, renew_membership) urlpatterns = [ url(r'^$', register, name='register'), - url(r'^inside/user/$', inside_user_api, name='inside-user-api'), - url(r'^check-phonenumber/$', check_phone_number, name='check-phone-number'), - url(r'^inside/card/$', inside_card_api, name='inside-card-number-api'), + url(r'^user-search/$', user_search, name='user-search'), + url(r'^check-phone-number/$', check_phone_number, name='check-phone-number'), + url(r'^check-card/$', check_card, name='check-card'), url(r'^register-card-membership/$', register_card_and_membership, name='register-card-and-membership'), url(r'^renew-membership/$', renew_membership, name='renew-membership'), url(r'^stats/card-sales/$', stats_card_sales, name='stats-card-sales') diff --git a/apps/kassa/utils.py b/apps/kassa/utils.py index 7cb2609..689076b 100644 --- a/apps/kassa/utils.py +++ b/apps/kassa/utils.py @@ -3,6 +3,13 @@ from django.conf import settings import datetime import phonenumbers import requests +import logging + +logger = logging.getLogger(__name__) + +dusken_auth = { + 'Authorization': 'Token {}'.format(settings.DUSKEN_API_KEY) +} def tekstmelding_new_membership_card(card_number, phone_number): @@ -12,6 +19,10 @@ def tekstmelding_new_membership_card(card_number, phone_number): 'action': 'new_card_no_user', } + if not settings.TEKSTMELDING_ENABLED: + logger.info('Tekstmelding disabled. Payload would be: ', payload) + return + url = '{}kassa/new-membership-card'.format(settings.TEKSTMELDING_API_URL) response = requests.post( url, @@ -23,48 +34,69 @@ def tekstmelding_new_membership_card(card_number, phone_number): return response.json() -def inside_get_card(card_number): - url = '{}card.php'.format(settings.INSIDE_API_URL) - params = { - 'apikey': settings.INSIDE_API_KEY, - 'card_number': card_number - } - - return requests.get(url, params=params) +def get_card(card_number): + url = '{}cards/{}/'.format(settings.DUSKEN_API_URL, card_number) + return requests.get(url, headers=dusken_auth) -def inside_update_card(card_number, user_id, phone_number, action, membership_trial=None): - url = '{}card.php'.format(settings.INSIDE_API_URL) +def get_order(order_uuid): + url = '{}orders/{}/'.format(settings.DUSKEN_API_URL, order_uuid) + return requests.get(url, headers=dusken_auth) + +def get_latest_order_by_card(card_number): + url = '{}orders/'.format(settings.DUSKEN_API_URL) + payload = {'card_number': card_number} + orders = requests.get(url, params=payload, headers=dusken_auth).json() + return orders.get('results')[0] + + +def get_latest_order_by_phone(phone_number): + url = '{}orders/'.format(settings.DUSKEN_API_URL) + payload = {'phone_number': phone_number} + orders = requests.get(url, params=payload, headers=dusken_auth).json() + if orders.get('count'): + return orders.get('results')[0] + return None + + +def get_user(user_id): + url = '{}users/{}/'.format(settings.DUSKEN_API_URL, user_id) + return requests.get(url, headers=dusken_auth) + + +def get_user_by_phone(phone_number): + url = '{}users/'.format(settings.DUSKEN_API_URL) + payload = {'phone_number': phone_number} + users = requests.get(url, params=payload, headers=dusken_auth).json() + if users.get('count') == 1: + return users.get('results')[0] + return None + + +def update_card(card_number, user_id=None, order_uuid=None): + assert not (user_id and order_uuid) + url = '{}kassa/card/'.format(settings.DUSKEN_API_URL) payload = { - 'card_number': card_number, - 'user_id': user_id, + 'member_card': card_number, + 'user': user_id, + 'order': order_uuid, + } + return requests.patch(url, json=payload, headers=dusken_auth) + + +def update_membership(user=None, phone_number=None, card_number=None, membership_type=None, + transaction_id=None): + assert membership_type in ('standard', 'trial') + url = '{}kassa/membership/'.format(settings.DUSKEN_API_URL) + payload = { + 'user': user, 'phone_number': phone_number, - 'action': action # new_card_membership, update_card, add_or_renew, sms_card_notify + 'member_card': card_number, + 'membership_type': membership_type, + 'transaction_id': transaction_id, } - if membership_trial is not None: - payload.update({'membership_trial': membership_trial}) - - return requests.post( - url, - json=payload, - params={'apikey': settings.INSIDE_API_KEY}, - headers=dict(content_type='application/json') - ) - - -def inside_update_membership(user_id, purchased=None, membership_trial=None): - url = '{}membership.php'.format(settings.INSIDE_API_URL) - payload = { - 'apikey': settings.INSIDE_API_KEY, - 'user_id': user_id, - 'purchased': purchased, - 'source': 'kassa' - } - if membership_trial is not None: - payload.update({'membership_trial': membership_trial}) - - return requests.post(url, json=payload, headers=dict(content_type='application/json')) + return requests.post(url, json=payload, headers=dusken_auth) def format_phone_number(number): diff --git a/apps/kassa/views.py b/apps/kassa/views.py index e200af6..3729440 100644 --- a/apps/kassa/views.py +++ b/apps/kassa/views.py @@ -6,14 +6,16 @@ from django.shortcuts import render from django.utils import timezone from itertools import groupby import json +import uuid import logging import phonenumbers import requests from apps.kassa.models import KassaEvent from apps.kassa.forms import AddCardForm, SearchUserForm -from apps.kassa.utils import tekstmelding_new_membership_card, inside_update_card, inside_get_card, \ - inside_update_membership, format_phone_number, is_autumn +from apps.kassa.utils import tekstmelding_new_membership_card, update_card, get_card, \ + get_order, get_latest_order_by_card, get_latest_order_by_phone, get_user, get_user_by_phone, \ + dusken_auth, update_membership, format_phone_number, is_autumn logger = logging.getLogger(__name__) @@ -29,18 +31,17 @@ def register(request): @login_required -def inside_user_api(request): +def user_search(request): payload = { - 'apikey': settings.INSIDE_API_KEY, - 'q': request.GET.get('q', '') + 'search': request.GET.get('search', '') } - url = '{}user.php'.format(settings.INSIDE_API_URL) - data = requests.get(url, params=payload).json() + url = '{}users/'.format(settings.DUSKEN_API_URL) + data = requests.get(url, params=payload, headers=dusken_auth).json() return JsonResponse(data) -@login_required() +@login_required def check_phone_number(request): number = request.GET.get('phone_number', '').strip() @@ -56,115 +57,128 @@ def check_phone_number(request): return JsonResponse({'error': str(e).replace('(1) ', '')}) number = phonenumbers.format_number(p, phonenumbers.PhoneNumberFormat.E164) - - # Inside lookup existing user - payload = { - 'apikey': settings.INSIDE_API_KEY, - 'q': number - } - url = '{}phonenumber.php'.format(settings.INSIDE_API_URL) - inside_data = requests.get(url, params=payload).json() - - # Tekstmelding lookup pending memberships - payload = { - 'api_key': settings.TEKSTMELDING_API_KEY, - 'number': number - } - url = '{}kassa/pending-membership'.format(settings.TEKSTMELDING_API_URL) - tekstmelding_data = requests.get(url, params=payload).json() + user = get_user_by_phone(number) + order = get_latest_order_by_phone(number) return JsonResponse({ - 'tekstmelding': tekstmelding_data, - 'inside': inside_data + 'user': user, + 'order': order }) @login_required -def inside_card_api(request): +def check_card(request): if request.method != 'GET': return JsonResponse({'error': 'Only method GET supported'}) - response = inside_get_card(request.GET.get('card_number')) - return JsonResponse(response.json(), status=response.status_code) + card_number = request.GET.get('card_number') + response = get_card(card_number) + if response.status_code != 200: + return JsonResponse(response.json(), status=response.status_code) + card = response.json() + if card.get('user'): + card['user'] = get_user(card['user']).json() + card['order'] = None + if card.get('orders'): + card['order'] = get_latest_order_by_card(card_number) + card.pop('orders') + return JsonResponse(card, status=response.status_code) -@login_required() +@login_required def register_card_and_membership(request): if request.method != 'POST': return JsonResponse({'error': 'Only method POST supported'}) post_data = json.loads(request.body.decode('utf-8')) - action = post_data.get('action') # new_card_membership, update_card, add_or_renew, sms_card_notify + # new_card_membership, update_card, add_or_renew, sms_card_notify # TODO: multiple actions (to allow renewal only) + action = post_data.get('action') user_id = post_data.get('user_id') + order_uuid = post_data.get('order_uuid') card_number = post_data.get('card_number') + phone_number = post_data.get('phone_number') membership_trial = post_data.get('membership_trial') + membership_type = 'trial' if membership_trial else 'standard' - response = inside_update_card( - card_number, - user_id, - post_data.get('phone_number'), - action, - membership_trial - ) - if response.status_code != 200: - return JsonResponse(response.json(), status=response.status_code) + # Update card number on user or order + if action in ('update_card', 'sms_card_notify'): + if action == 'update_card' and user_id: + response = update_card(card_number, user_id=user_id) + elif action == 'sms_card_notify' and order_uuid: + response = update_card(card_number, order_uuid=order_uuid) + else: + assert user_id or order_uuid + if response.status_code != 200: + return JsonResponse(response.json(), status=response.status_code) - event_data = { - 'event': KassaEvent.UPDATE_CARD, - 'user_phone_number': format_phone_number(post_data.get('phone_number')), - 'card_number': card_number - } - if user_id: - event_data.update({'user_inside_id': user_id}) + event_data = { + 'event': KassaEvent.UPDATE_CARD, + 'user_phone_number': phone_number, + 'card_number': card_number, + 'user_dusken_id': user_id, + } + logger.debug(event_data) + KassaEvent.objects.create(**event_data) - logger.debug(event_data) - - KassaEvent.objects.create(**event_data) - - card_update_response = response.json() + # Add initial or renew membership for existing user or order + if action in ('add_or_renew', 'new_card_membership'): + transaction_id = uuid.uuid4() + response = update_membership( + user=user_id, + phone_number=phone_number, + card_number=card_number, + membership_type=membership_type, + transaction_id=str(transaction_id)) + KassaEvent.objects.create( + event=KassaEvent.ADD_OR_RENEW if membership_type != 'trial' else KassaEvent.MEMBERSHIP_TRIAL, + user_dusken_id=user_id, + card_number=card_number, + user_phone_number=phone_number, + transaction_id=transaction_id + ) # Send activation notification (link) to user by SMS - if action == 'new_card_membership' or action == 'sms_card_notify': - card = card_update_response['card'] + if action in ('new_card_membership', 'sms_card_notify'): event = KassaEvent.NEW_CARD_MEMBERSHIP if action == 'sms_card_notify': event = KassaEvent.SMS_CARD_NOTIFY - elif event == KassaEvent.NEW_CARD_MEMBERSHIP and membership_trial is not None: - event = KassaEvent.MEMBERSHIP_TRIAL # FIXME: could be async - tekstmelding_new_membership_card(card_number=card['card_number'], phone_number=card['owner_phone_number']) + tekstmelding_new_membership_card(card_number=card_number, + phone_number=phone_number) KassaEvent.objects.create( event=event, - card_number=card['card_number'], - user_phone_number=card['owner_phone_number'] - ) - - # Add initial or renew membership for existing user - elif action == 'add_or_renew': - response = inside_update_membership(user_id, post_data.get('purchased'), membership_trial=membership_trial) - KassaEvent.objects.create( - event=KassaEvent.ADD_OR_RENEW if membership_trial is None else KassaEvent.MEMBERSHIP_TRIAL, - user_inside_id=user_id + card_number=card_number, + user_phone_number=phone_number ) return JsonResponse(response.json(), status=response.status_code) -@login_required() +@login_required def renew_membership(request): if request.method != 'POST': return JsonResponse({'error': 'Only method POST supported'}) post_data = json.loads(request.body.decode('utf-8')) user_id = post_data.get('user_id') + phone_number = post_data.get('phone_number') + card_number = post_data.get('card_number') membership_trial = post_data.get('membership_trial') + membership_type = 'trial' if membership_trial else 'standard' + transaction_id = str(uuid.uuid4()) - response = inside_update_membership(user_id, purchased=post_data.get('purchased'), membership_trial=membership_trial) + response = update_membership( + user=user_id, + phone_number=phone_number, + card_number=card_number, + membership_type=membership_type, + transaction_id=transaction_id) KassaEvent.objects.create( event=KassaEvent.RENEW_ONLY if membership_trial is None else KassaEvent.MEMBERSHIP_TRIAL, - user_inside_id=user_id + user_dusken_id=user_id, + transaction_id=transaction_id ) return JsonResponse(response.json(), status=response.status_code) diff --git a/kassasite/settings.py b/kassasite/settings.py index 55dbb12..6a21e5c 100644 --- a/kassasite/settings.py +++ b/kassasite/settings.py @@ -113,11 +113,12 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') LOGIN_REDIRECT_URL = '/' -INSIDE_API_KEY = '' -INSIDE_API_URL = 'https://inside.studentersamfundet.no/api/' +DUSKEN_API_KEY = '' +DUSKEN_API_URL = 'https://dusken.neuf.no/api/' TEKSTMELDING_API_KEY = '' TEKSTMELDING_API_URL = 'https://tekstmelding.neuf.no/' +TEKSTMELDING_ENABLED = True CORS_ORIGIN_ALLOW_ALL = True diff --git a/static/app/scripts/app.js b/static/app/scripts/app.js index 785c282..741ee37 100644 --- a/static/app/scripts/app.js +++ b/static/app/scripts/app.js @@ -20,20 +20,7 @@ function getParameterByName(name) { results = regex.exec(location.search); return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } -function update_user_with_card_details(user) { - /* Cards with a number lower than this is legacy as of 2015-08 */ - var legacy_card_limit = 100000000; - for(var j=0; j 0) { - _dom.results.html(format_results(data.results)); + if (searching) { + searching.abort(); + } + searching = $.getJSON(urls.userSearch, {search: val}, function(data) { + if (data.results && data.results.length > 0) { + sorted_results = sort_search_results(data.results); + _dom.results.html(format_results(sorted_results)); users = data.results; } - else if(data.error) { + else if (data.error) { _dom.results.html(data.error); } else { if(val !== "") { - _dom.results.html("Found no existing user with search param: '"+ val +"'"); + _dom.results.html("Found no user matching: '"+ val +"'"); } else { _dom.results.html(''); } @@ -420,7 +467,7 @@ $(document).ready(function(){ set_selected_user(_.findWhere(users, {id: user_id})); /* Update phone number field */ - if(number === '-') { + if (number === '-') { number = ''; } _dom.phoneNumberField.val(number).trigger('input'); @@ -438,13 +485,13 @@ $(document).ready(function(){ /* Register type */ // FIXME: get from form // action: 'new_card_membership', 'update_card', 'add_or_renew', 'sms_card_notify' - if(selectedUser === null && pendingSMSMembership === null) { + if (selectedUser === null && membershipOrder === null) { payload.action = 'new_card_membership'; - } else if(selectedUser === null && pendingSMSMembership !== null) { + } else if (selectedUser === null && membershipOrder !== null) { payload.action = 'sms_card_notify'; - payload.purchased = pendingSMSMembership.purchase_date; + payload.order_uuid = membershipOrder.uuid; } else { - if(selectedUser.is_member === '1') { + if (selectedUser.is_member) { payload.action = 'update_card'; } else { payload.action = 'add_or_renew'; // Note: could also update card number @@ -456,20 +503,17 @@ $(document).ready(function(){ dataType: 'json', type: 'post', headers: {'X-CSRFToken': getCookie('csrftoken')} - }).success(function(data){ - var phone_number; + }).done(function(data) { var success_message = 'New card registered ' + payload.card_number; - if(payload.action === 'add_or_renew') { - var full_name = data.user[0].firstname + ' ' + data.user[0].lastname; + if (payload.action === 'add_or_renew') { + var full_name = selectedUser.first_name + ' ' + selectedUser.last_name; success_message = 'New membership and card number '+ payload.card_number + ' registered to ' + full_name + ' '; } - else if(payload.action ==='new_card_membership') { - phone_number = data.card.owner_phone_number; - success_message = 'New membership and card registered to ' + format_phone_number(phone_number) + '. Activation SMS sent '; + else if (payload.action ==='new_card_membership') { + success_message = 'New membership and card registered to ' + format_phone_number(payload.phone_number) + '. Activation SMS sent '; } - else if(payload.action === 'sms_card_notify') { - phone_number = data.card.owner_phone_number; - success_message = 'New card registered to ' + format_phone_number(phone_number) + '. Membership already paid by SMS ('+ pendingSMSMembership.purchase_date +'). Activation SMS sent '; + else if (payload.action === 'sms_card_notify') { + success_message = 'New card registered to ' + format_phone_number(payload.phone_number) + '. Membership already paid '+ membershipOrder.product.start_date +'. Activation SMS sent '; } set_toast(success_message + ' :-)', 'success'); resetCardForm(true); @@ -477,8 +521,12 @@ $(document).ready(function(){ }).fail(function(data) { console.log("failed", data); var error_text = data.responseText; - if(data.responseJSON) { - error_text = data.responseJSON.error; + if (data.responseJSON) { + if (data.responseJSON.non_field_errors) { + error_text = data.responseJSON.non_field_errors.join('
'); + } else { + error_text = 'Not sure why. :-('; + } } set_toast('Failed! '+ error_text, 'error'); }); @@ -487,35 +535,44 @@ $(document).ready(function(){ /* Membership renewal button */ _dom.membershipSubmitButton.on('click', function(e) { e.preventDefault(); - if(!cardForm.fields.phoneNumber || selectedUser.card_number_active === '') { - set_toast('Either users phone number is not valid or user does not have a card registered.', 'error'); + if (!selectedUser && !membershipOrder) { + set_toast('Computer says no.', 'error'); return; } var payload = getFormData(_dom.registerCardForm); - if( pendingSMSMembership !== null) { - payload.purchased = pendingSMSMembership.purchase_date; - } - else if( selectedUser.expires && moment(selectedUser.expires) > moment() ) { - payload.purchased = selectedUser.expires; // future purchase date + + /* If order, make sure to bring along both card number and phone number */ + if (membershipOrder) { + payload.card_number = membershipOrder.member_card; + payload.phone_number = membershipOrder.phone_number; } + $.ajax(urls.renewMembership, { data: JSON.stringify(payload), contentType: 'application/json', dataType: 'json', type: 'post', headers: {'X-CSRFToken': getCookie('csrftoken')} - }).success(function(data) { - var full_name = data.user[0].firstname + ' ' + data.user[0].lastname; - var success_message = 'Membership renewed for ' + full_name + ' '; - set_toast(success_message + ' :-)', 'success'); + }).done(function(data) { + var name = 'unknown'; + if (data.user) { + name = selectedUser.first_name + ' ' + selectedUser.last_name; + } else { + name = 'phone number ' + data.phone_number; + } + var success_message = 'Membership renewed for ' + name + ' :-)'; + set_toast(success_message, 'success'); resetCardForm(true); - }).fail(function(data) { console.log("failed", data); var error_text = data.responseText; - if(data.responseJSON) { - error_text = data.responseJSON.error; + if (data.responseJSON) { + if (data.responseJSON.non_field_errors) { + error_text = data.responseJSON.non_field_errors.join('
'); + } else { + error_text = 'Not sure why. :-('; + } } set_toast('Failed! '+ error_text, 'error'); }); @@ -526,6 +583,11 @@ $(document).ready(function(){ /* Render selected user in search form */ render_selected_user(selectedUser); }); + /* On order uuid change */ + _dom.orderUuidField.on('change', function() { + /* Render selected order */ + render_selected_order(membershipOrder); + }); /* Reset buttons */ _dom.registerResetButton.on('click', function() { resetCardForm(); @@ -541,7 +603,7 @@ $(document).ready(function(){ /* Render initial placeholder user */ var selected_user_html = nunjucksEnv.render('search_result.html', {placeholder: true}); - _dom.selectedUserWrap.html(selected_user_html); + _dom.selectedEntityWrap.html(selected_user_html); /* Initial focus */ if(_dom.usernameField) { diff --git a/static/app/styles/app.scss b/static/app/styles/app.scss index 6acb720..cb9aa0a 100644 --- a/static/app/styles/app.scss +++ b/static/app/styles/app.scss @@ -70,7 +70,7 @@ input[type=number] { .glyphicon-phone-large { font-size: 26px; } -.register-card-form--selected-user-wrap { +.register-card-form--selected-entity-wrap { min-height: 78px; margin-bottom: 10px; } @@ -86,6 +86,11 @@ input[type=number] { } +#membership-submit-btn:not(:disabled) { + background-color: #dff0d8; + border-color: #3c763d; +} + .query-form { margin-bottom: 20px; } @@ -111,7 +116,7 @@ input[type=number] { .label-card-yes { color: $green-color; } - .label-is-active { + .label-is-volunteer { background-color: $active-color; } [type=radio] { diff --git a/static/app/templates/search_result.html b/static/app/templates/search_result.html index 159f645..6226003 100644 --- a/static/app/templates/search_result.html +++ b/static/app/templates/search_result.html @@ -1,29 +1,29 @@ {% if not placeholder %} -