diff --git a/apiserver/apiserver/api/utils_paypal.py b/apiserver/apiserver/api/utils_paypal.py index f0660b9..b5078cf 100644 --- a/apiserver/apiserver/api/utils_paypal.py +++ b/apiserver/apiserver/api/utils_paypal.py @@ -250,7 +250,7 @@ def check_training(data, training_id, amount): if training.attendance_status == 'Waiting for payment': training.attendance_status = 'Confirmed' - training.paid_date = datetime.date.today() + training.paid_date = utils.today_alberta_tz() training.save() logger.info('IPN - Amount valid for training cost, id: ' + str(training.id)) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index c789afd..766e910 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -1157,6 +1157,8 @@ class ProtocoinViewSet(Base): source_user = self.request.user source_member = source_user.member + training = None + try: balance = float(request.data['balance']) except KeyError: @@ -1175,9 +1177,33 @@ class ProtocoinViewSet(Base): category = str(request.data['category']) except KeyError: raise exceptions.ValidationError(dict(category='This field is required.')) - if category not in ['Consumables', 'Donation']: + if category not in ['Consumables', 'Donation', 'OnAcct']: raise exceptions.ValidationError(dict(category='Invalid category.')) + if category == 'OnAcct': + try: + training_id = int(request.data['training']) + except KeyError: + raise exceptions.ValidationError(dict(training='This field is required.')) + except ValueError: + raise exceptions.ValidationError(dict(training='Invalid number.')) + + training = get_object_or_404(models.Training, id=training_id) + + if not training.session: + raise exceptions.ValidationError(dict(training='Invalid session.')) + + if training.session.is_cancelled: + raise exceptions.ValidationError(dict(training='Class is cancelled.')) + + if training.paid_date: + raise exceptions.ValidationError(dict(training='Already paid.')) + + if training.session.cost != amount: + msg = 'Protocoin training payment amount mismatch:\n' + str(request.data.dict()) + utils.alert_tanner(msg) + raise exceptions.ValidationError(dict(training='Class cost doesn\'t match amount.')) + memo = str(request.data.get('memo', '')) # also prevents negative spending @@ -1193,11 +1219,19 @@ class ProtocoinViewSet(Base): if source_user_balance < amount: raise exceptions.ValidationError(dict(amount='Insufficient funds.')) - tx_memo = 'Protocoin - Transaction spent ₱ {} on {}{}'.format( - amount, - category, - ', memo: ' + memo if memo else '' - ) + if training: + tx_memo = 'Protocoin - Transaction spent ₱ {} on {}, session: {}, training: {}'.format( + amount, + training.session.course.name, + str(training.session.id), + str(training.id), + ) + else: + tx_memo = 'Protocoin - Transaction spent ₱ {} on {}{}'.format( + amount, + category, + ', memo: ' + memo if memo else '' + ) tx = models.Transaction.objects.create( user=source_user, @@ -1211,6 +1245,12 @@ class ProtocoinViewSet(Base): ) utils.log_transaction(tx) + if training: + if training.attendance_status == 'Waiting for payment': + training.attendance_status = 'Confirmed' + training.paid_date = utils.today_alberta_tz() + training.save() + return Response(200) except OperationalError: self.spend_request(request) diff --git a/webclient/src/Classes.js b/webclient/src/Classes.js index 1518a44..8e48b04 100644 --- a/webclient/src/Classes.js +++ b/webclient/src/Classes.js @@ -7,6 +7,7 @@ import { apiUrl, isAdmin, getInstructor, BasicTable, requester, useIsMobile } fr import { NotFound } from './Misc.js'; import { InstructorClassDetail, InstructorClassAttendance } from './InstructorClasses.js'; import { PayPalPayNow } from './PayPal.js'; +import { PayWithProtocoin } from './Paymaster.js'; import { tags } from './Courses.js'; function ClassTable(props) { @@ -693,6 +694,18 @@ export function ClassDetail(props) { name={clazz.course_data.name} custom={JSON.stringify({ training: userTraining.id })} /> + +

+ + { + refreshUser(); + refreshClass(); + }} + custom={{ category: 'OnAcct', training: userTraining.id }} + /> } diff --git a/webclient/src/Paymaster.js b/webclient/src/Paymaster.js index f88a0d4..0a8508f 100644 --- a/webclient/src/Paymaster.js +++ b/webclient/src/Paymaster.js @@ -7,7 +7,7 @@ import { MembersDropdown } from './Members.js'; import { requester } from './utils.js'; export function PayWithProtocoin(props) { - const { token, user, refreshUser, amount, setAmount, custom } = props; + const { token, user, refreshUser, amount, onSuccess, custom } = props; const member = user.member; const [error, setError] = useState({}); const [loading, setLoading] = useState(false); @@ -23,7 +23,9 @@ export function PayWithProtocoin(props) { .then(res => { setLoading(false); setSuccess(true); - setAmount(''); + if (onSuccess) { + onSuccess(); + } setError({}); refreshUser(); }) @@ -232,7 +234,7 @@ export function Paymaster(props) { setConsumables('')} custom={{ category: 'Consumables', memo: consumablesMemo }} /> @@ -274,7 +276,7 @@ export function Paymaster(props) { setDonate('')} custom={{ category: 'Donation', memo: memo }} />