Add testmode for gift cards

This commit is contained in:
Raphael Michel
2019-10-17 18:05:04 +02:00
parent 302966808e
commit 4b2f25ce8a
14 changed files with 62 additions and 7 deletions
+9 -2
View File
@@ -17,6 +17,7 @@ id integer Internal ID of
secret string Gift card code (can not be modified later)
value money (string) Current gift card value
currency string Currency of the value (can not be modified later)
testmode boolean Whether this is a test gift card
===================================== ========================== =======================================================
Endpoints
@@ -51,6 +52,7 @@ Endpoints
"id": 1,
"secret": "HLBYVELFRC77NCQY",
"currency": "EUR",
"testmode": false,
"value": "13.37"
}
]
@@ -86,6 +88,7 @@ Endpoints
"id": 1,
"secret": "HLBYVELFRC77NCQY",
"currency": "EUR",
"testmode": false,
"value": "13.37"
}
@@ -125,6 +128,7 @@ Endpoints
{
"id": 1,
"secret": "HLBYVELFRC77NCQY",
"testmode": false,
"currency": "EUR",
"value": "13.37"
}
@@ -141,8 +145,9 @@ Endpoints
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
want to change.
You can change all fields of the resource except the ``id``, ``secret``, and ``currency`` fields. Be careful when
modifying the ``value`` field to avoid race conditions. We recommend to use the ``transact`` method described below.
You can change all fields of the resource except the ``id``, ``secret``, ``testmode``, and ``currency`` fields. Be
careful when modifying the ``value`` field to avoid race conditions. We recommend to use the ``transact`` method
described below.
**Example request**:
@@ -169,6 +174,7 @@ Endpoints
{
"id": 1,
"secret": "HLBYVELFRC77NCQY",
"testmode": false,
"currency": "EUR",
"value": "14.00"
}
@@ -211,6 +217,7 @@ Endpoints
"id": 1,
"secret": "HLBYVELFRC77NCQY",
"currency": "EUR",
"testmode": false,
"value": "15.37"
}
+1 -1
View File
@@ -46,4 +46,4 @@ class GiftCardSerializer(I18nAwareModelSerializer):
class Meta:
model = GiftCard
fields = ('id', 'secret', 'issuance', 'value', 'currency')
fields = ('id', 'secret', 'issuance', 'value', 'currency', 'testmode')
+2 -1
View File
@@ -117,7 +117,8 @@ class GiftCardViewSet(viewsets.ModelViewSet):
GiftCard.objects.select_for_update().get(pk=self.get_object().pk)
old_value = serializer.instance.value
value = serializer.validated_data.pop('value')
inst = serializer.save(secret=serializer.instance.secret, currency=serializer.instance.currency)
inst = serializer.save(secret=serializer.instance.secret, currency=serializer.instance.currency,
testmode=serializer.instance.testmode)
diff = value - old_value
inst.transactions.create(value=diff)
inst.log_action(
@@ -32,6 +32,7 @@ class Migration(migrations.Migration):
('issuer',
models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='issued_gift_cards',
to='pretixbase.Organizer')),
('testmode', django.db.models.BooleanField(default=False)),
],
options={
'unique_together': {('secret', 'issuer')},
+4
View File
@@ -52,6 +52,10 @@ class GiftCard(LoggedModel):
db_index=True,
verbose_name=_('Gift card code'),
)
testmode = models.BooleanField(
verbose_name=_('Test mode card'),
default=False
)
CURRENCY_CHOICES = [(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES]
currency = models.CharField(max_length=10, choices=CURRENCY_CHOICES)
+5 -1
View File
@@ -201,7 +201,7 @@ class Order(LockModel, LoggedModel):
return self.full_code
def gracefully_delete(self, user=None, auth=None):
from . import Voucher
from . import Voucher, GiftCard, GiftCardTransaction
if not self.testmode:
raise TypeError("Only test mode orders can be deleted.")
@@ -217,6 +217,10 @@ class Order(LockModel, LoggedModel):
if position.voucher:
Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1))
GiftCardTransaction.objects.filter(payment__in=self.payments.all()).update(payment=None)
GiftCardTransaction.objects.filter(refund__in=self.refunds.all()).update(refund=None)
GiftCardTransaction.objects.filter(order=self).update(order=None)
GiftCard.objects.filter(issued_in__in=self.positions.all()).update(issued_in=None)
OrderPosition.all.filter(order=self, addon_to__isnull=False).delete()
OrderPosition.all.filter(order=self).delete()
OrderFee.all.filter(order=self).delete()
+18
View File
@@ -906,6 +906,10 @@ class GiftCardPayment(BasePaymentProvider):
del f['_invoice_text']
return f
@property
def test_mode_message(self) -> str:
return _("In test mode, only test cards will work.")
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
return super().is_allowed(request, total) and self.event.organizer.has_gift_cards
@@ -958,6 +962,12 @@ class GiftCardPayment(BasePaymentProvider):
if gc.currency != self.event.currency:
messages.error(request, _("This gift card does not support this currency."))
return
if gc.testmode and not self.event.testmode:
messages.error(request, _("This gift card can only be used in test mode."))
return
if not gc.testmode and self.event.testmode:
messages.error(request, _("Only test gifts cards can be used in test mode."))
return
if gc.value <= Decimal("0.00"):
messages.error(request, _("All credit on this gift card has been used."))
return
@@ -997,6 +1007,12 @@ class GiftCardPayment(BasePaymentProvider):
if gc.currency != self.event.currency:
messages.error(request, _("This gift card does not support this currency."))
return
if gc.testmode and not self.event.testmode:
messages.error(request, _("This gift card can only be used in test mode."))
return
if not gc.testmode and self.event.testmode:
messages.error(request, _("Only test gift cards can be used in test mode."))
return
if gc.value <= Decimal("0.00"):
messages.error(request, _("All credit on this gift card has been used."))
return
@@ -1027,6 +1043,8 @@ class GiftCardPayment(BasePaymentProvider):
raise PaymentException(_("This gift card does not support this currency."))
if not gc.accepted_by(self.event.organizer):
raise PaymentException(_("This gift card is not accepted by this event organizer."))
if gc.testmode != payment.order.testmode:
raise PaymentException(_("Only the gift card or only the order are created in test mode."))
if payment.amount > gc.value:
raise PaymentException(_("This gift card was used in the meantime. Please try again"))
trans = gc.transactions.create(
+5
View File
@@ -971,9 +971,13 @@ def get_fees(event, request, total, invoice_address, provider):
cs = cart_session(request)
if cs.get('gift_cards'):
gcs = cs['gift_cards']
gc_qs = event.organizer.accepted_gift_cards.filter(pk__in=cs.get('gift_cards'), currency=event.currency)
summed = 0
for gc in gc_qs:
if gc.testmode != event.testmode:
gcs.remove(gc.pk)
continue
fval = Decimal(gc.value) # TODO: don't require an extra query
fval = min(fval, total - summed)
if fval > 0:
@@ -988,6 +992,7 @@ def get_fees(event, request, total, invoice_address, provider):
tax_value=Decimal('0.00'),
tax_rule=TaxRule.zero()
))
cs['gift_cards'] = gcs
if provider and total != 0:
provider = event.get_payment_providers().get(provider)
+5 -1
View File
@@ -600,6 +600,10 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
for gc in gc_qs:
if gc.currency != event.currency:
raise OrderError(_("This gift card does not support this currency."))
if gc.testmode and not event.testmode:
raise OrderError(_("This gift card can only be used in test mode."))
if not gc.testmode and event.testmode:
raise OrderError(_("Only test gift cards can be used in test mode."))
if not gc.accepted_by(event.organizer):
raise OrderError(_("This gift card is not accepted by this event organizer."))
checked_gift_cards.append(gc)
@@ -1687,7 +1691,7 @@ def signal_listener_issue_giftcards(sender: Event, order: Order, **kwargs):
for p in order.positions.all():
if p.item.issue_giftcard:
gc = sender.organizer.issued_gift_cards.create(
currency=sender.currency, issued_in=p
currency=sender.currency, issued_in=p, testmode=order.testmode
)
gc.transactions.create(value=p.price, order=order)
any_giftcards = True
+1 -1
View File
@@ -355,4 +355,4 @@ class GiftCardCreateForm(forms.ModelForm):
class Meta:
model = GiftCard
fields = ['secret', 'currency']
fields = ['secret', 'currency', 'testmode']
@@ -7,6 +7,9 @@
{% blocktrans trimmed with card=card.secret %}
Gift card: {{ card }}
{% endblocktrans %}
{% if card.testmode %}
<span class="label label-warning">{% trans "TEST MODE" %}</span>
{% endif %}
</h1>
<div class="panel panel-primary items">
<div class="panel-heading">
@@ -9,6 +9,7 @@
{% bootstrap_field form.secret layout="control" %}
{% bootstrap_field form.value layout="control" %}
{% bootstrap_field form.currency layout="control" %}
{% bootstrap_field form.testmode layout="control" %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
@@ -54,6 +54,9 @@
<a href="{% url "control:organizer.giftcard" organizer=request.organizer.slug giftcard=g.id %}">
<strong>{{ g.secret }}</strong>
</a>
{% if g.testmode %}
<span class="label label-warning">{% trans "TEST MODE" %}</span>
{% endif %}
</td>
<td>{{ g.issuance|date:"SHORT_DATETIME_FORMAT" }}</td>
<td class="text-right">
+4
View File
@@ -18,6 +18,7 @@ TEST_GC_RES = {
"id": 1,
"secret": "ABCDEF",
"value": "23.00",
"testmode": False,
"currency": "EUR"
}
@@ -46,6 +47,7 @@ def test_giftcard_detail(token_client, organizer, event, giftcard):
TEST_GIFTCARD_CREATE_PAYLOAD = {
"secret": "DEFABC",
"value": "12.00",
"testmode": False,
"currency": "EUR",
}
@@ -84,6 +86,7 @@ def test_giftcard_patch(token_client, organizer, event, giftcard):
{
'secret': 'foo',
'value': '10.00',
'testmode': True,
'currency': 'USD'
},
format='json'
@@ -93,6 +96,7 @@ def test_giftcard_patch(token_client, organizer, event, giftcard):
assert giftcard.value == Decimal('10.00')
assert giftcard.secret == "ABCDEF"
assert giftcard.currency == "EUR"
assert not giftcard.testmode
@pytest.mark.django_db