From 1c225da510e97a3420f974f5b7a4379c507e4478 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 22 Aug 2022 21:33:38 +0000 Subject: [PATCH] Display protocoin amounts, improve transaction validation --- apiserver/apiserver/api/serializers.py | 46 ++++++++++++++++++++------ webclient/src/AdminMembers.js | 5 +++ webclient/src/Home.js | 6 +++- webclient/src/Transactions.js | 9 +++-- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 89eb61f..9f83091 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -3,6 +3,7 @@ logger = logging.getLogger(__name__) from django.contrib.auth.models import User, Group from django.shortcuts import get_object_or_404 +from django.db.models import Max, F, Count, Q, Sum from django.utils.timezone import now from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -77,13 +78,21 @@ class TransactionSerializer(serializers.ModelSerializer): 'paypal_payer_id', ] - def create(self, validated_data): + def validate_transaction(self, validated_data): if not self.initial_data.get('member_id', None): raise ValidationError(dict(member_id='This field is required.')) member = get_object_or_404(models.Member, id=self.initial_data['member_id']) validated_data['user'] = member.user + if validated_data['account_type'] == 'Protocoin': + validated_data['amount'] = 0 + else: + validated_data['protocoin'] = 0 + + if validated_data['category'] != 'Membership': + validated_data['number_of_membership_months'] = 0 + if validated_data['account_type'] == 'Protocoin' and validated_data['category'] == 'Exchange': raise ValidationError(dict(category='Can\'t purchase Protocoin with Protocoin.')) @@ -92,10 +101,29 @@ class TransactionSerializer(serializers.ModelSerializer): raise ValidationError(dict(category='Can\'t purchase 0 Protocoin.')) validated_data['protocoin'] = validated_data['amount'] + if validated_data['account_type'] == 'Protocoin': + if validated_data['protocoin'] == 0: + raise ValidationError(dict(account_type='Can\'t have a 0.00 protocoin transaction.')) + if validated_data['account_type'] not in ['Clearing', 'Protocoin']: if validated_data['amount'] == 0: raise ValidationError(dict(account_type='Can\'t have a $0.00 {} transaction. Do you want "Membership Adjustment"?'.format(validated_data['account_type']))) + if validated_data['protocoin'] < 0: + current_protocoin = member.user.transactions.aggregate(Sum('protocoin'))['protocoin__sum'] + new_protocoin = current_protocoin + validated_data['protocoin'] + if new_protocoin < 0: + raise ValidationError(dict(protocoin='Insufficient funds. Member only has {} protocoin.'.format(current_protocoin))) + + if validated_data['account_type'] in ['Interac', 'Dream Pmt', 'Square Pmt', 'PayPal']: + if not validated_data.get('reference_number', None): + raise ValidationError(dict(reference_number='This field is required.')) + + return validated_data + + def create(self, validated_data): + validated_data = self.validate_transaction(validated_data) + if validated_data['account_type'] == 'PayPal': msg = 'Manual PayPal transaction added:\n' + str(validated_data) utils.alert_tanner(msg) @@ -104,18 +132,10 @@ class TransactionSerializer(serializers.ModelSerializer): msg = 'Manual Protocoin transaction added:\n' + str(validated_data) utils.alert_tanner(msg) - if validated_data['account_type'] in ['Interac', 'Dream Pmt', 'Square Pmt', 'PayPal']: - if not validated_data.get('reference_number', None): - raise ValidationError(dict(reference_number='This field is required.')) - return super().create(validated_data) def update(self, instance, validated_data): - if not self.initial_data.get('member_id', None): - raise ValidationError(dict(member_id='This field is required.')) - - member = get_object_or_404(models.Member, id=self.initial_data['member_id']) - validated_data['user'] = member.user + validated_data = self.validate_transaction(validated_data) return super().update(instance, validated_data) def get_member_id(self, obj): @@ -183,6 +203,7 @@ class MemberSerializer(serializers.ModelSerializer): crop = serializers.CharField(write_only=True, required=False) email = fields.UserEmailField(serializers.EmailField) phone = serializers.CharField() + protocoin = serializers.SerializerMethodField() class Meta: model = models.Member @@ -221,6 +242,11 @@ class MemberSerializer(serializers.ModelSerializer): 'mediawiki_username', ] + def get_protocoin(self, obj): + transactions = obj.user.transactions + total = transactions.aggregate(Sum('protocoin'))['protocoin__sum'] + return total + def update(self, instance, validated_data): instance.user.email = validated_data.get('email', instance.user.email) instance.user.save() diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js index 3e4a9dd..4c4c4cb 100644 --- a/webclient/src/AdminMembers.js +++ b/webclient/src/AdminMembers.js @@ -539,6 +539,11 @@ export function AdminMemberInfo(props) { + + Protocoin: + ₱ {member.protocoin.toFixed(2)} + + Application Date: {member.application_date} diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 9c5b751..e09af24 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -48,6 +48,10 @@ function MemberInfo(props) { {member.status || 'Unknown'} + + Protocoin: + ₱ {member.protocoin.toFixed(2)} + Expiry: {member.expire_date ? moment(member.expire_date).format('ll') : 'Unknown'} @@ -126,7 +130,7 @@ function MemberInfo(props) { {moment(x.date).format('ll')} {x.account_type} - ${x.amount} + {x.protocoin !== '0.00' ? '₱ ' + x.protocoin : '$ ' + x.amount} ) : diff --git a/webclient/src/Transactions.js b/webclient/src/Transactions.js index 49d0198..7894edf 100644 --- a/webclient/src/Transactions.js +++ b/webclient/src/Transactions.js @@ -159,7 +159,6 @@ export function TransactionEditor(props) { {...makeProps('memo')} /> - ); }; @@ -304,7 +303,7 @@ export function TransactionList(props) { x.member_name } } - ${x.amount} + {x.protocoin !== '0.00' ? '₱ ' + x.protocoin : '$ ' + x.amount} {x.account_type} {!noCategory && {x.category}} {x.memo || x.report_memo} @@ -361,8 +360,12 @@ class TransactionTable extends React.Component { Amount: - ${transaction.amount} + $ {transaction.amount} + {transaction.protocoin !== '0.00' && + Protocoin: + ₱ {transaction.protocoin} + } Category: {transaction.category}