Display protocoin amounts, improve transaction validation

This commit is contained in:
Tanner Collin 2022-08-22 21:33:38 +00:00
parent 81c9bd9c9b
commit 1c225da510
4 changed files with 52 additions and 14 deletions

View File

@ -3,6 +3,7 @@ logger = logging.getLogger(__name__)
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.shortcuts import get_object_or_404 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 django.utils.timezone import now
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
@ -77,13 +78,21 @@ class TransactionSerializer(serializers.ModelSerializer):
'paypal_payer_id', 'paypal_payer_id',
] ]
def create(self, validated_data): def validate_transaction(self, validated_data):
if not self.initial_data.get('member_id', None): if not self.initial_data.get('member_id', None):
raise ValidationError(dict(member_id='This field is required.')) raise ValidationError(dict(member_id='This field is required.'))
member = get_object_or_404(models.Member, id=self.initial_data['member_id']) member = get_object_or_404(models.Member, id=self.initial_data['member_id'])
validated_data['user'] = member.user 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': if validated_data['account_type'] == 'Protocoin' and validated_data['category'] == 'Exchange':
raise ValidationError(dict(category='Can\'t purchase Protocoin with Protocoin.')) 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.')) raise ValidationError(dict(category='Can\'t purchase 0 Protocoin.'))
validated_data['protocoin'] = validated_data['amount'] 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['account_type'] not in ['Clearing', 'Protocoin']:
if validated_data['amount'] == 0: 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']))) 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': if validated_data['account_type'] == 'PayPal':
msg = 'Manual PayPal transaction added:\n' + str(validated_data) msg = 'Manual PayPal transaction added:\n' + str(validated_data)
utils.alert_tanner(msg) utils.alert_tanner(msg)
@ -104,18 +132,10 @@ class TransactionSerializer(serializers.ModelSerializer):
msg = 'Manual Protocoin transaction added:\n' + str(validated_data) msg = 'Manual Protocoin transaction added:\n' + str(validated_data)
utils.alert_tanner(msg) 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) return super().create(validated_data)
def update(self, instance, validated_data): def update(self, instance, validated_data):
if not self.initial_data.get('member_id', None): validated_data = self.validate_transaction(validated_data)
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
return super().update(instance, validated_data) return super().update(instance, validated_data)
def get_member_id(self, obj): def get_member_id(self, obj):
@ -183,6 +203,7 @@ class MemberSerializer(serializers.ModelSerializer):
crop = serializers.CharField(write_only=True, required=False) crop = serializers.CharField(write_only=True, required=False)
email = fields.UserEmailField(serializers.EmailField) email = fields.UserEmailField(serializers.EmailField)
phone = serializers.CharField() phone = serializers.CharField()
protocoin = serializers.SerializerMethodField()
class Meta: class Meta:
model = models.Member model = models.Member
@ -221,6 +242,11 @@ class MemberSerializer(serializers.ModelSerializer):
'mediawiki_username', '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): def update(self, instance, validated_data):
instance.user.email = validated_data.get('email', instance.user.email) instance.user.email = validated_data.get('email', instance.user.email)
instance.user.save() instance.user.save()

View File

@ -539,6 +539,11 @@ export function AdminMemberInfo(props) {
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
<Table.Row>
<Table.Cell>Protocoin:</Table.Cell>
<Table.Cell>&thinsp;{member.protocoin.toFixed(2)}</Table.Cell>
</Table.Row>
<Table.Row> <Table.Row>
<Table.Cell>Application Date:</Table.Cell> <Table.Cell>Application Date:</Table.Cell>
<Table.Cell>{member.application_date}</Table.Cell> <Table.Cell>{member.application_date}</Table.Cell>

View File

@ -48,6 +48,10 @@ function MemberInfo(props) {
{member.status || 'Unknown'} {member.status || 'Unknown'}
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
<Table.Row>
<Table.Cell>Protocoin:</Table.Cell>
<Table.Cell>&thinsp;{member.protocoin.toFixed(2)}</Table.Cell>
</Table.Row>
<Table.Row> <Table.Row>
<Table.Cell>Expiry:</Table.Cell> <Table.Cell>Expiry:</Table.Cell>
<Table.Cell>{member.expire_date ? moment(member.expire_date).format('ll') : 'Unknown'}</Table.Cell> <Table.Cell>{member.expire_date ? moment(member.expire_date).format('ll') : 'Unknown'}</Table.Cell>
@ -126,7 +130,7 @@ function MemberInfo(props) {
<Link to={'/transactions/'+x.id}>{moment(x.date).format('ll')}</Link> <Link to={'/transactions/'+x.id}>{moment(x.date).format('ll')}</Link>
</Table.Cell> </Table.Cell>
<Table.Cell>{x.account_type}</Table.Cell> <Table.Cell>{x.account_type}</Table.Cell>
<Table.Cell>${x.amount}</Table.Cell> <Table.Cell>{x.protocoin !== '0.00' ? '₱ ' + x.protocoin : '$' + x.amount}</Table.Cell>
</Table.Row> </Table.Row>
) )
: :

View File

@ -159,7 +159,6 @@ export function TransactionEditor(props) {
{...makeProps('memo')} {...makeProps('memo')}
/> />
</Form.Group> </Form.Group>
</div> </div>
); );
}; };
@ -304,7 +303,7 @@ export function TransactionList(props) {
x.member_name x.member_name
} }
</Table.Cell>} </Table.Cell>}
<Table.Cell>${x.amount}</Table.Cell> <Table.Cell style={{ minWidth: '8rem' }}>{x.protocoin !== '0.00' ? '₱ ' + x.protocoin : '$' + x.amount}</Table.Cell>
<Table.Cell>{x.account_type}</Table.Cell> <Table.Cell>{x.account_type}</Table.Cell>
{!noCategory && <Table.Cell>{x.category}</Table.Cell>} {!noCategory && <Table.Cell>{x.category}</Table.Cell>}
<Table.Cell>{x.memo || x.report_memo}</Table.Cell> <Table.Cell>{x.memo || x.report_memo}</Table.Cell>
@ -361,8 +360,12 @@ class TransactionTable extends React.Component {
</Table.Row> </Table.Row>
<Table.Row> <Table.Row>
<Table.Cell>Amount:</Table.Cell> <Table.Cell>Amount:</Table.Cell>
<Table.Cell>${transaction.amount}</Table.Cell> <Table.Cell>${transaction.amount}</Table.Cell>
</Table.Row> </Table.Row>
{transaction.protocoin !== '0.00' && <Table.Row>
<Table.Cell>Protocoin:</Table.Cell>
<Table.Cell>{transaction.protocoin}</Table.Cell>
</Table.Row>}
<Table.Row> <Table.Row>
<Table.Cell>Category:</Table.Cell> <Table.Cell>Category:</Table.Cell>
<Table.Cell>{transaction.category}</Table.Cell> <Table.Cell>{transaction.category}</Table.Cell>