diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py
index 102d5d6..351f6f9 100644
--- a/apiserver/apiserver/api/serializers.py
+++ b/apiserver/apiserver/api/serializers.py
@@ -138,6 +138,7 @@ class MemberSerializer(serializers.ModelSerializer):
'current_start_date',
'application_date',
'vetted_date',
+ 'paused_date',
'monthly_fees',
'photo_large',
'photo_medium',
@@ -170,6 +171,8 @@ class AdminMemberSerializer(MemberSerializer):
read_only_fields = [
'id',
'status',
+ 'expire_date',
+ 'paused_date',
'photo_large',
'photo_medium',
'photo_small',
@@ -214,7 +217,7 @@ class AdminSearchSerializer(serializers.Serializer):
queryset = obj.user.transactions
else:
queryset = models.Transaction.objects.filter(member_id=obj.id)
- queryset = queryset.order_by('-date')
+ queryset = queryset.order_by('-id', '-date')
serializer = TransactionSerializer(data=queryset, many=True)
serializer.is_valid()
return serializer.data
@@ -318,13 +321,21 @@ class UserTrainingSerializer(serializers.ModelSerializer):
class UserSerializer(serializers.ModelSerializer):
training = UserTrainingSerializer(many=True)
member = MemberSerializer()
- transactions = TransactionSerializer(many=True)
+ transactions = serializers.SerializerMethodField()
class Meta:
model = User
fields = ['id', 'username', 'member', 'transactions', 'cards', 'training', 'is_staff']
depth = 1
+ def get_transactions(self, obj):
+ queryset = models.Transaction.objects.filter(user=obj)
+ queryset = queryset.exclude(category='Memberships:Fake Months')
+ queryset = queryset.order_by('-id', '-date')
+ serializer = TransactionSerializer(data=queryset, many=True)
+ serializer.is_valid()
+ return serializer.data
+
def request_from_protospace(request):
whitelist = ['24.66.110.96', '205.233.15.76', '205.233.15.69']
diff --git a/apiserver/apiserver/api/utils.py b/apiserver/apiserver/api/utils.py
index 3d25a9a..58447bb 100644
--- a/apiserver/apiserver/api/utils.py
+++ b/apiserver/apiserver/api/utils.py
@@ -48,8 +48,8 @@ def add_months(date, num_months):
def fake_missing_membership_months(member):
'''
- Return a transaction adding fake months on importing the member so the
- length of their membership resolves to their imported expiry date
+ Add fake months on importing the member so the length of their membership
+ resolves to their imported expiry date
'''
start_date = member.current_start_date
expire_date = member.expire_date
@@ -57,22 +57,25 @@ def fake_missing_membership_months(member):
missing_months = num_months_spanned(expire_date, start_date)
user = member.user if member.user else None
- memo = '{} mth membership dues accounting old portal import, {} to {}'.format(
- str(missing_months), start_date, expire_date
- )
+ tx = False
+ for i in range(missing_months):
+ memo = '{} / {} month membership dues accounting old portal import, {} to {} - hidden'.format(
+ str(i+1), str(missing_months), start_date, expire_date
+ )
- tx = models.Transaction.objects.create(
- amount=0,
- user=user,
- memo=memo,
- member_id=member.id,
- reference_number='',
- info_source='System',
- payment_method='N/A',
- category='Membership',
- account_type='Clearing',
- number_of_membership_months=missing_months,
- )
+ tx = models.Transaction.objects.create(
+ amount=0,
+ user=user,
+ memo=memo,
+ member_id=member.id,
+ reference_number='',
+ info_source='System',
+ payment_method='N/A',
+ category='Memberships:Fake Months',
+ account_type='Clearing',
+ number_of_membership_months=1,
+ date=add_months(start_date, i),
+ )
return tx
@@ -88,7 +91,7 @@ def tally_membership_months(member, fake_date=None):
txs = models.Transaction.objects.filter(member_id=member.id)
total_months_agg = txs.aggregate(Sum('number_of_membership_months'))
- total_months = total_months_agg['number_of_membership_months__sum']
+ total_months = total_months_agg['number_of_membership_months__sum'] or 0
expire_date = add_months(start_date, total_months)
status, former = calc_member_status(expire_date, fake_date)
diff --git a/apiserver/import_old_portal.py b/apiserver/import_old_portal.py
index be4d3d7..450553e 100755
--- a/apiserver/import_old_portal.py
+++ b/apiserver/import_old_portal.py
@@ -144,13 +144,16 @@ for m in members:
tx = utils.fake_missing_membership_months(m)
utils.tally_membership_months(m, import_date)
- print(m.first_name, m.last_name, tx.memo)
+ if tx:
+ print(m.first_name, m.last_name, tx.memo)
if old_status != m.status or old_expire != m.expire_date:
print('Expire / status mismatch member:', m.__dict__)
print('New status:', m.status)
print('Old status:', old_status)
+ print('New expire:', m.expire_date)
print('Old expire:', old_expire)
+ print('')
bad_count += 1
print('Import mismatch count:', bad_count)
diff --git a/webclient/src/Admin.js b/webclient/src/Admin.js
index b7af65e..bcbc20d 100644
--- a/webclient/src/Admin.js
+++ b/webclient/src/Admin.js
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom';
import './light.css';
-import { Button, Container, Checkbox, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
+import { Button, Container, Checkbox, Dimmer, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
import moment from 'moment';
import { BasicTable, staticUrl, requester } from './utils.js';
import { TransactionList, TransactionEditor } from './Transactions.js';
@@ -170,6 +170,7 @@ function AdminCardDetail(props) {
export function AdminMemberCards(props) {
const { token, result, refreshResult } = props;
const cards = result.cards;
+ const [dimmed, setDimmed] = useState(result.member.paused_date && cards.length);
const [input, setInput] = useState({ active_status: 'card_active' });
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
@@ -247,13 +248,25 @@ export function AdminMemberCards(props) {
None
- } +None
+ } + ++ Member paused, {cards.length} card{cards.length === 1 ? '' : 's'} ignored anyway. +
++ +
+