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) {
Current Cards
- {cards.length ? - cards.map(x => - - ) - : -

None

- } + + {cards.length ? + cards.map(x => + + ) + : +

None

+ } + + +

+ Member paused, {cards.length} card{cards.length === 1 ? '' : 's'} ignored anyway. +

+

+ +

+
+
+ ); }; @@ -320,11 +333,6 @@ export function AdminMemberForm(props) { {...makeProps('vetted_date')} /> - - {member.status} + + Expire Date: + {member.expire_date} + + {member.paused_date && + Paused Date: + {member.paused_date} + } + Phone: {member.phone} diff --git a/webclient/src/Home.js b/webclient/src/Home.js index c223ac4..693baa0 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -10,7 +10,7 @@ function MemberInfo(props) { const user = props.user; const member = user.member; - const lastTrans = user.transactions && user.transactions.slice(-3).slice().reverse(); + const lastTrans = user.transactions && user.transactions.slice(0,3); const lastCard = user.cards && user.cards.sort((a, b) => a.last_seen_at < b.last_seen_at)[0]; return (