Allow paying course fees with Protocoin
This commit is contained in:
parent
cfbbe2095d
commit
c8378374b0
|
@ -250,7 +250,7 @@ def check_training(data, training_id, amount):
|
||||||
|
|
||||||
if training.attendance_status == 'Waiting for payment':
|
if training.attendance_status == 'Waiting for payment':
|
||||||
training.attendance_status = 'Confirmed'
|
training.attendance_status = 'Confirmed'
|
||||||
training.paid_date = datetime.date.today()
|
training.paid_date = utils.today_alberta_tz()
|
||||||
training.save()
|
training.save()
|
||||||
|
|
||||||
logger.info('IPN - Amount valid for training cost, id: ' + str(training.id))
|
logger.info('IPN - Amount valid for training cost, id: ' + str(training.id))
|
||||||
|
|
|
@ -1157,6 +1157,8 @@ class ProtocoinViewSet(Base):
|
||||||
source_user = self.request.user
|
source_user = self.request.user
|
||||||
source_member = source_user.member
|
source_member = source_user.member
|
||||||
|
|
||||||
|
training = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
balance = float(request.data['balance'])
|
balance = float(request.data['balance'])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -1175,9 +1177,33 @@ class ProtocoinViewSet(Base):
|
||||||
category = str(request.data['category'])
|
category = str(request.data['category'])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise exceptions.ValidationError(dict(category='This field is required.'))
|
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.'))
|
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', ''))
|
memo = str(request.data.get('memo', ''))
|
||||||
|
|
||||||
# also prevents negative spending
|
# also prevents negative spending
|
||||||
|
@ -1193,6 +1219,14 @@ class ProtocoinViewSet(Base):
|
||||||
if source_user_balance < amount:
|
if source_user_balance < amount:
|
||||||
raise exceptions.ValidationError(dict(amount='Insufficient funds.'))
|
raise exceptions.ValidationError(dict(amount='Insufficient funds.'))
|
||||||
|
|
||||||
|
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(
|
tx_memo = 'Protocoin - Transaction spent ₱ {} on {}{}'.format(
|
||||||
amount,
|
amount,
|
||||||
category,
|
category,
|
||||||
|
@ -1211,6 +1245,12 @@ class ProtocoinViewSet(Base):
|
||||||
)
|
)
|
||||||
utils.log_transaction(tx)
|
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)
|
return Response(200)
|
||||||
except OperationalError:
|
except OperationalError:
|
||||||
self.spend_request(request)
|
self.spend_request(request)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { apiUrl, isAdmin, getInstructor, BasicTable, requester, useIsMobile } fr
|
||||||
import { NotFound } from './Misc.js';
|
import { NotFound } from './Misc.js';
|
||||||
import { InstructorClassDetail, InstructorClassAttendance } from './InstructorClasses.js';
|
import { InstructorClassDetail, InstructorClassAttendance } from './InstructorClasses.js';
|
||||||
import { PayPalPayNow } from './PayPal.js';
|
import { PayPalPayNow } from './PayPal.js';
|
||||||
|
import { PayWithProtocoin } from './Paymaster.js';
|
||||||
import { tags } from './Courses.js';
|
import { tags } from './Courses.js';
|
||||||
|
|
||||||
function ClassTable(props) {
|
function ClassTable(props) {
|
||||||
|
@ -693,6 +694,18 @@ export function ClassDetail(props) {
|
||||||
name={clazz.course_data.name}
|
name={clazz.course_data.name}
|
||||||
custom={JSON.stringify({ training: userTraining.id })}
|
custom={JSON.stringify({ training: userTraining.id })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<p/>
|
||||||
|
|
||||||
|
<PayWithProtocoin
|
||||||
|
token={token} user={user} refreshUser={refreshUser}
|
||||||
|
amount={clazz.cost}
|
||||||
|
onSuccess={() => {
|
||||||
|
refreshUser();
|
||||||
|
refreshClass();
|
||||||
|
}}
|
||||||
|
custom={{ category: 'OnAcct', training: userTraining.id }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { MembersDropdown } from './Members.js';
|
||||||
import { requester } from './utils.js';
|
import { requester } from './utils.js';
|
||||||
|
|
||||||
export function PayWithProtocoin(props) {
|
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 member = user.member;
|
||||||
const [error, setError] = useState({});
|
const [error, setError] = useState({});
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
@ -23,7 +23,9 @@ export function PayWithProtocoin(props) {
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
setAmount('');
|
if (onSuccess) {
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
setError({});
|
setError({});
|
||||||
refreshUser();
|
refreshUser();
|
||||||
})
|
})
|
||||||
|
@ -232,7 +234,7 @@ export function Paymaster(props) {
|
||||||
<PayWithProtocoin
|
<PayWithProtocoin
|
||||||
token={token} user={user} refreshUser={refreshUser}
|
token={token} user={user} refreshUser={refreshUser}
|
||||||
amount={consumables}
|
amount={consumables}
|
||||||
setAmount={setConsumables}
|
onSuccess={() => setConsumables('')}
|
||||||
custom={{ category: 'Consumables', memo: consumablesMemo }}
|
custom={{ category: 'Consumables', memo: consumablesMemo }}
|
||||||
/>
|
/>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
|
@ -274,7 +276,7 @@ export function Paymaster(props) {
|
||||||
<PayWithProtocoin
|
<PayWithProtocoin
|
||||||
token={token} user={user} refreshUser={refreshUser}
|
token={token} user={user} refreshUser={refreshUser}
|
||||||
amount={donate}
|
amount={donate}
|
||||||
setAmount={setDonate}
|
onSuccess={() => setDonate('')}
|
||||||
custom={{ category: 'Donation', memo: memo }}
|
custom={{ category: 'Donation', memo: memo }}
|
||||||
/>
|
/>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user