Make expire and paused date read-only, add fake months individually
This commit is contained in:
		| @@ -138,6 +138,7 @@ class MemberSerializer(serializers.ModelSerializer): | |||||||
|             'current_start_date', |             'current_start_date', | ||||||
|             'application_date', |             'application_date', | ||||||
|             'vetted_date', |             'vetted_date', | ||||||
|  |             'paused_date', | ||||||
|             'monthly_fees', |             'monthly_fees', | ||||||
|             'photo_large', |             'photo_large', | ||||||
|             'photo_medium', |             'photo_medium', | ||||||
| @@ -170,6 +171,8 @@ class AdminMemberSerializer(MemberSerializer): | |||||||
|         read_only_fields = [ |         read_only_fields = [ | ||||||
|             'id', |             'id', | ||||||
|             'status', |             'status', | ||||||
|  |             'expire_date', | ||||||
|  |             'paused_date', | ||||||
|             'photo_large', |             'photo_large', | ||||||
|             'photo_medium', |             'photo_medium', | ||||||
|             'photo_small', |             'photo_small', | ||||||
| @@ -214,7 +217,7 @@ class AdminSearchSerializer(serializers.Serializer): | |||||||
|             queryset = obj.user.transactions |             queryset = obj.user.transactions | ||||||
|         else: |         else: | ||||||
|             queryset = models.Transaction.objects.filter(member_id=obj.id) |             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 = TransactionSerializer(data=queryset, many=True) | ||||||
|         serializer.is_valid() |         serializer.is_valid() | ||||||
|         return serializer.data |         return serializer.data | ||||||
| @@ -318,13 +321,21 @@ class UserTrainingSerializer(serializers.ModelSerializer): | |||||||
| class UserSerializer(serializers.ModelSerializer): | class UserSerializer(serializers.ModelSerializer): | ||||||
|     training = UserTrainingSerializer(many=True) |     training = UserTrainingSerializer(many=True) | ||||||
|     member = MemberSerializer() |     member = MemberSerializer() | ||||||
|     transactions = TransactionSerializer(many=True) |     transactions = serializers.SerializerMethodField() | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = User |         model = User | ||||||
|         fields = ['id', 'username', 'member', 'transactions', 'cards', 'training', 'is_staff'] |         fields = ['id', 'username', 'member', 'transactions', 'cards', 'training', 'is_staff'] | ||||||
|         depth = 1 |         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): | def request_from_protospace(request): | ||||||
|     whitelist = ['24.66.110.96', '205.233.15.76', '205.233.15.69'] |     whitelist = ['24.66.110.96', '205.233.15.76', '205.233.15.69'] | ||||||
|   | |||||||
| @@ -48,8 +48,8 @@ def add_months(date, num_months): | |||||||
|  |  | ||||||
| def fake_missing_membership_months(member): | def fake_missing_membership_months(member): | ||||||
|     ''' |     ''' | ||||||
|     Return a transaction adding fake months on importing the member so the |     Add fake months on importing the member so the length of their membership | ||||||
|     length of their membership resolves to their imported expiry date |     resolves to their imported expiry date | ||||||
|     ''' |     ''' | ||||||
|     start_date = member.current_start_date |     start_date = member.current_start_date | ||||||
|     expire_date = member.expire_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) |     missing_months = num_months_spanned(expire_date, start_date) | ||||||
|  |  | ||||||
|     user = member.user if member.user else None |     user = member.user if member.user else None | ||||||
|     memo = '{} mth membership dues accounting old portal import, {} to {}'.format( |     tx = False | ||||||
|         str(missing_months), start_date, expire_date |     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( |         tx = models.Transaction.objects.create( | ||||||
|         amount=0, |             amount=0, | ||||||
|         user=user, |             user=user, | ||||||
|         memo=memo, |             memo=memo, | ||||||
|         member_id=member.id, |             member_id=member.id, | ||||||
|         reference_number='', |             reference_number='', | ||||||
|         info_source='System', |             info_source='System', | ||||||
|         payment_method='N/A', |             payment_method='N/A', | ||||||
|         category='Membership', |             category='Memberships:Fake Months', | ||||||
|         account_type='Clearing', |             account_type='Clearing', | ||||||
|         number_of_membership_months=missing_months, |             number_of_membership_months=1, | ||||||
|     ) |             date=add_months(start_date, i), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     return tx |     return tx | ||||||
|  |  | ||||||
| @@ -88,7 +91,7 @@ def tally_membership_months(member, fake_date=None): | |||||||
|  |  | ||||||
|     txs = models.Transaction.objects.filter(member_id=member.id) |     txs = models.Transaction.objects.filter(member_id=member.id) | ||||||
|     total_months_agg = txs.aggregate(Sum('number_of_membership_months')) |     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) |     expire_date = add_months(start_date, total_months) | ||||||
|     status, former = calc_member_status(expire_date, fake_date) |     status, former = calc_member_status(expire_date, fake_date) | ||||||
|   | |||||||
| @@ -144,13 +144,16 @@ for m in members: | |||||||
|     tx = utils.fake_missing_membership_months(m) |     tx = utils.fake_missing_membership_months(m) | ||||||
|     utils.tally_membership_months(m, import_date) |     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: |     if old_status != m.status or old_expire != m.expire_date: | ||||||
|         print('Expire / status mismatch member:', m.__dict__) |         print('Expire / status mismatch member:', m.__dict__) | ||||||
|         print('New status:', m.status) |         print('New status:', m.status) | ||||||
|         print('Old status:', old_status) |         print('Old status:', old_status) | ||||||
|  |         print('New expire:', m.expire_date) | ||||||
|         print('Old expire:', old_expire) |         print('Old expire:', old_expire) | ||||||
|  |         print('') | ||||||
|         bad_count += 1 |         bad_count += 1 | ||||||
|  |  | ||||||
| print('Import mismatch count:', bad_count) | print('Import mismatch count:', bad_count) | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import React, { useState, useEffect } from 'react'; | import React, { useState, useEffect } from 'react'; | ||||||
| import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; | import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; | ||||||
| import './light.css'; | 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 moment from 'moment'; | ||||||
| import { BasicTable, staticUrl, requester } from './utils.js'; | import { BasicTable, staticUrl, requester } from './utils.js'; | ||||||
| import { TransactionList, TransactionEditor } from './Transactions.js'; | import { TransactionList, TransactionEditor } from './Transactions.js'; | ||||||
| @@ -170,6 +170,7 @@ function AdminCardDetail(props) { | |||||||
| export function AdminMemberCards(props) { | export function AdminMemberCards(props) { | ||||||
| 	const { token, result, refreshResult } = props; | 	const { token, result, refreshResult } = props; | ||||||
| 	const cards = result.cards; | 	const cards = result.cards; | ||||||
|  | 	const [dimmed, setDimmed] = useState(result.member.paused_date && cards.length); | ||||||
| 	const [input, setInput] = useState({ active_status: 'card_active' }); | 	const [input, setInput] = useState({ active_status: 'card_active' }); | ||||||
| 	const [error, setError] = useState(false); | 	const [error, setError] = useState(false); | ||||||
| 	const [loading, setLoading] = useState(false); | 	const [loading, setLoading] = useState(false); | ||||||
| @@ -247,13 +248,25 @@ export function AdminMemberCards(props) { | |||||||
|  |  | ||||||
| 			<Header size='small'>Current Cards</Header> | 			<Header size='small'>Current Cards</Header> | ||||||
|  |  | ||||||
| 			{cards.length ? | 			<Dimmer.Dimmable dimmed={dimmed}> | ||||||
| 				cards.map(x => | 				{cards.length ? | ||||||
| 					<AdminCardDetail key={x.id} card={x} {...props} /> | 					cards.map(x => | ||||||
| 				) | 						<AdminCardDetail key={x.id} card={x} {...props} /> | ||||||
| 			: | 					) | ||||||
| 				<p>None</p> | 				: | ||||||
| 			} | 					<p>None</p> | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				<Dimmer active={dimmed}> | ||||||
|  | 					<p> | ||||||
|  | 						Member paused, {cards.length} card{cards.length === 1 ? '' : 's'} ignored anyway. | ||||||
|  | 					</p> | ||||||
|  | 					<p> | ||||||
|  | 						<Button size='tiny' onClick={() => setDimmed(false)}>Close</Button> | ||||||
|  | 					</p> | ||||||
|  | 				</Dimmer> | ||||||
|  | 			</Dimmer.Dimmable> | ||||||
|  |  | ||||||
| 		</div> | 		</div> | ||||||
| 	); | 	); | ||||||
| }; | }; | ||||||
| @@ -320,11 +333,6 @@ export function AdminMemberForm(props) { | |||||||
| 					{...makeProps('vetted_date')} | 					{...makeProps('vetted_date')} | ||||||
| 				/> | 				/> | ||||||
|  |  | ||||||
| 				<Form.Input |  | ||||||
| 					label='Expire Date' |  | ||||||
| 					{...makeProps('expire_date')} |  | ||||||
| 				/> |  | ||||||
|  |  | ||||||
| 				<Form.Input | 				<Form.Input | ||||||
| 					label='Membership Fee' | 					label='Membership Fee' | ||||||
| 					{...makeProps('monthly_fees')} | 					{...makeProps('monthly_fees')} | ||||||
| @@ -387,6 +395,15 @@ export function AdminMemberInfo(props) { | |||||||
| 						<Table.Cell>{member.status}</Table.Cell> | 						<Table.Cell>{member.status}</Table.Cell> | ||||||
| 					</Table.Row> | 					</Table.Row> | ||||||
|  |  | ||||||
|  | 					<Table.Row> | ||||||
|  | 						<Table.Cell>Expire Date:</Table.Cell> | ||||||
|  | 						<Table.Cell>{member.expire_date}</Table.Cell> | ||||||
|  | 					</Table.Row> | ||||||
|  | 					{member.paused_date && <Table.Row> | ||||||
|  | 						<Table.Cell>Paused Date:</Table.Cell> | ||||||
|  | 						<Table.Cell>{member.paused_date}</Table.Cell> | ||||||
|  | 					</Table.Row>} | ||||||
|  |  | ||||||
| 					<Table.Row> | 					<Table.Row> | ||||||
| 						<Table.Cell>Phone:</Table.Cell> | 						<Table.Cell>Phone:</Table.Cell> | ||||||
| 						<Table.Cell>{member.phone}</Table.Cell> | 						<Table.Cell>{member.phone}</Table.Cell> | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ function MemberInfo(props) { | |||||||
| 	const user = props.user; | 	const user = props.user; | ||||||
| 	const member = user.member; | 	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]; | 	const lastCard = user.cards && user.cards.sort((a, b) => a.last_seen_at < b.last_seen_at)[0]; | ||||||
|  |  | ||||||
| 	return ( | 	return ( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user