Merge pull request #370 from bornhack/ticket_type_create_single_ticket_option
Add "single_ticket_per_product" boolean to ticket types
This commit is contained in:
commit
7dc82ee4ee
36
src/camps/factories.py
Normal file
36
src/camps/factories.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import factory
|
||||
from django.utils import timezone
|
||||
|
||||
from factory.django import DjangoModelFactory
|
||||
from psycopg2._range import DateTimeTZRange
|
||||
|
||||
|
||||
class CampFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = "camps.Camp"
|
||||
|
||||
read_only = False
|
||||
|
||||
title = factory.Faker("word")
|
||||
tagline = factory.Faker("sentence")
|
||||
slug = factory.Faker("slug")
|
||||
shortslug = factory.Faker("slug")
|
||||
|
||||
buildup = factory.LazyFunction(
|
||||
lambda: DateTimeTZRange(
|
||||
lower=timezone.now() - timezone.timedelta(3), upper=timezone.now()
|
||||
)
|
||||
)
|
||||
|
||||
camp = factory.LazyFunction(
|
||||
lambda: DateTimeTZRange(lower=timezone.now(), upper=timezone.now())
|
||||
)
|
||||
|
||||
teardown = factory.LazyFunction(
|
||||
lambda: DateTimeTZRange(
|
||||
lower=timezone.now() + timezone.timedelta(8),
|
||||
upper=timezone.now() + timezone.timedelta(11),
|
||||
)
|
||||
)
|
||||
|
||||
colour = factory.Faker("hex_color")
|
|
@ -30,6 +30,7 @@ class ProductFactory(DjangoModelFactory):
|
|||
lower=timezone.now(), upper=timezone.now() + timezone.timedelta(31)
|
||||
)
|
||||
)
|
||||
ticket_type = factory.SubFactory("tickets.factories.TicketTypeFactory")
|
||||
|
||||
|
||||
class OrderFactory(DjangoModelFactory):
|
||||
|
|
|
@ -208,6 +208,7 @@ class Order(CreatedUpdatedModel):
|
|||
return str(reverse_lazy("shop:order_detail", kwargs={"pk": self.pk}))
|
||||
|
||||
def create_tickets(self, request=None):
|
||||
tickets = []
|
||||
for order_product in self.orderproductrelation_set.all():
|
||||
# if this is a Ticket product?
|
||||
if order_product.product.ticket_type:
|
||||
|
@ -216,32 +217,57 @@ class Order(CreatedUpdatedModel):
|
|||
ticket_type=order_product.product.ticket_type,
|
||||
)
|
||||
|
||||
already_created_tickets = self.shoptickets.filter(
|
||||
**query_kwargs
|
||||
).count()
|
||||
tickets_to_create = max(
|
||||
0, order_product.quantity - already_created_tickets
|
||||
)
|
||||
if order_product.product.ticket_type.single_ticket_per_product:
|
||||
# This ticket type is one where we only create one ticket
|
||||
ticket, created = self.shoptickets.get_or_create(**query_kwargs)
|
||||
|
||||
# create the number of tickets required
|
||||
if tickets_to_create > 0:
|
||||
for _ in range(
|
||||
0, (order_product.quantity - already_created_tickets)
|
||||
):
|
||||
self.shoptickets.create(**query_kwargs)
|
||||
if created:
|
||||
msg = (
|
||||
"Created ticket for product %s on order %s (quantity: %s)"
|
||||
% (
|
||||
order_product.product,
|
||||
order_product.order.pk,
|
||||
order_product.quantity,
|
||||
)
|
||||
)
|
||||
tickets.append(ticket)
|
||||
else:
|
||||
msg = "Ticket already created for product %s on order %s" % (
|
||||
order_product.product,
|
||||
order_product.order.pk,
|
||||
)
|
||||
|
||||
msg = "Created %s tickets of type: %s" % (
|
||||
order_product.quantity,
|
||||
order_product.product.ticket_type.name,
|
||||
)
|
||||
if request:
|
||||
messages.success(request, msg)
|
||||
else:
|
||||
print(msg)
|
||||
else:
|
||||
# We should create a number of tickets equal to OrderProductRelation quantity
|
||||
already_created_tickets = self.shoptickets.filter(
|
||||
**query_kwargs
|
||||
).count()
|
||||
tickets_to_create = max(
|
||||
0, order_product.quantity - already_created_tickets
|
||||
)
|
||||
|
||||
# and mark the OPR as ticket_generated=True
|
||||
order_product.ticket_generated = True
|
||||
order_product.save()
|
||||
# create the number of tickets required
|
||||
if tickets_to_create > 0:
|
||||
for _ in range(
|
||||
0, (order_product.quantity - already_created_tickets)
|
||||
):
|
||||
ticket = self.shoptickets.create(**query_kwargs)
|
||||
tickets.append(ticket)
|
||||
|
||||
msg = "Created %s tickets of type: %s" % (
|
||||
order_product.quantity,
|
||||
order_product.product.ticket_type.name,
|
||||
)
|
||||
if request:
|
||||
messages.success(request, msg)
|
||||
|
||||
# and mark the OPR as ticket_generated=True
|
||||
order_product.ticket_generated = True
|
||||
order_product.save()
|
||||
|
||||
return tickets
|
||||
|
||||
def mark_as_paid(self, request=None):
|
||||
self.paid = True
|
||||
|
|
|
@ -4,6 +4,8 @@ from django.utils import timezone
|
|||
from psycopg2.extras import DateTimeTZRange
|
||||
|
||||
from shop.forms import OrderProductRelationForm
|
||||
from tickets.factories import TicketTypeFactory
|
||||
from tickets.models import ShopTicket
|
||||
from utils.factories import UserFactory
|
||||
from .factories import ProductFactory, OrderProductRelationFactory, OrderFactory
|
||||
|
||||
|
@ -371,3 +373,27 @@ class TestOrderListView(TestCase):
|
|||
path = reverse("shop:order_list")
|
||||
response = self.client.get(path)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
|
||||
class TestTicketCreation(TestCase):
|
||||
def test_multiple_tickets_created(self):
|
||||
user = UserFactory()
|
||||
ticket_type = TicketTypeFactory(single_ticket_per_product=False)
|
||||
product = ProductFactory(ticket_type=ticket_type)
|
||||
order = OrderFactory(user=user)
|
||||
OrderProductRelationFactory(order=order, product=product, quantity=5)
|
||||
order.mark_as_paid()
|
||||
self.assertEquals(
|
||||
ShopTicket.objects.filter(product=product, order=order).count(), 5
|
||||
)
|
||||
|
||||
def test_single_ticket_created(self):
|
||||
user = UserFactory()
|
||||
ticket_type = TicketTypeFactory(single_ticket_per_product=True)
|
||||
product = ProductFactory(ticket_type=ticket_type)
|
||||
order = OrderFactory(user=user)
|
||||
OrderProductRelationFactory(order=order, product=product, quantity=5)
|
||||
order.mark_as_paid()
|
||||
self.assertEquals(
|
||||
ShopTicket.objects.filter(product=product, order=order).count(), 1
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from shop.models import OrderProductRelation
|
||||
from .models import TicketType, SponsorTicket, DiscountTicket, ShopTicket
|
||||
|
||||
|
||||
|
@ -51,12 +52,24 @@ class ShopTicketAdmin(BaseTicketAdmin):
|
|||
"order",
|
||||
"product",
|
||||
"used",
|
||||
"product_quantity",
|
||||
]
|
||||
|
||||
list_filter = ["ticket_type__camp", "used", "ticket_type", "order", "product"]
|
||||
|
||||
search_fields = ["uuid", "order__id", "order__user__email", "name", "email"]
|
||||
|
||||
def product_quantity(self, ticket):
|
||||
orp = OrderProductRelation.objects.get(
|
||||
product=ticket.product, order=ticket.order
|
||||
)
|
||||
|
||||
return (
|
||||
str(orp.quantity) if ticket.ticket_type.single_ticket_per_product else "1"
|
||||
)
|
||||
|
||||
product_quantity.short_description = "Quantity"
|
||||
|
||||
|
||||
class ShopTicketInline(admin.TabularInline):
|
||||
model = ShopTicket
|
||||
|
|
18
src/tickets/factories.py
Normal file
18
src/tickets/factories.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
import factory
|
||||
|
||||
from factory.django import DjangoModelFactory
|
||||
|
||||
|
||||
class TicketTypeFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = "tickets.TicketType"
|
||||
|
||||
name = factory.Faker("sentence")
|
||||
camp = factory.SubFactory("camps.factories.CampFactory")
|
||||
|
||||
|
||||
class ShopTicketFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = "tickets.ShopTicket"
|
||||
|
||||
ticket_type = factory.SubFactory(TicketTypeFactory)
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.3 on 2019-07-30 20:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0012_auto_20190724_2037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='tickettype',
|
||||
name='single_ticket_per_product',
|
||||
field=models.BooleanField(default=False, help_text='Only create one ticket for a product/order pair no matter the quantity. Useful for products which are bought in larger quantity (ie. village chairs)'),
|
||||
),
|
||||
]
|
|
@ -18,6 +18,13 @@ class TicketType(CampRelatedModel, UUIDModel):
|
|||
name = models.TextField()
|
||||
camp = models.ForeignKey("camps.Camp", on_delete=models.PROTECT)
|
||||
includes_badge = models.BooleanField(default=False)
|
||||
single_ticket_per_product = models.BooleanField(
|
||||
default=False,
|
||||
help_text=(
|
||||
"Only create one ticket for a product/order pair no matter the quantity. "
|
||||
"Useful for products which are bought in larger quantity (ie. village chairs)"
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "{} ({})".format(self.name, self.camp.title)
|
||||
|
|
Loading…
Reference in a new issue