remove FK from ShopTicket to Order and replace with an FK to OrderProductRelation
This commit is contained in:
parent
4c6eca2a6d
commit
517f29940a
|
@ -95,6 +95,9 @@ Enjoy!
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
### Running tests
|
||||||
|
If your database user in your dev setup is not a postgres superuser you will encounter permission errors when the migrations try to create extensions "btree_gist" and "postgis". You can solve this by connecting to the "template1" database as the postgres superuser and creating the extensions there, which means they will be automatically loaded for all newly created databases.
|
||||||
|
|
||||||
### Add a camp
|
### Add a camp
|
||||||
|
|
||||||
Add a new camp by running:
|
Add a new camp by running:
|
||||||
|
|
|
@ -65,7 +65,7 @@ Scan | {{ block.super }}
|
||||||
<td>
|
<td>
|
||||||
<strong>Quantity:</strong>
|
<strong>Quantity:</strong>
|
||||||
<td>
|
<td>
|
||||||
{{ ticket.orp.quantity }}
|
{{ ticket.opr.quantity }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -44,7 +44,7 @@ Your Tickets | {{ block.super }}
|
||||||
<td>
|
<td>
|
||||||
{{ ticket.product.name }}
|
{{ ticket.product.name }}
|
||||||
<td>
|
<td>
|
||||||
{% if ticket.ticket_type.single_ticket_per_product %}{{ ticket.orp.quantity }} × {% endif %}
|
{% if ticket.ticket_type.single_ticket_per_product %}{{ ticket.opr.quantity }} × {% endif %}
|
||||||
{{ ticket.product.price|currency }}
|
{{ ticket.product.price|currency }}
|
||||||
<td>
|
<td>
|
||||||
{% if ticket.used %}
|
{% if ticket.used %}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from tickets.admin import ShopTicketInline
|
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
CoinifyAPICallback,
|
CoinifyAPICallback,
|
||||||
CoinifyAPIInvoice,
|
CoinifyAPIInvoice,
|
||||||
|
@ -138,7 +136,7 @@ class OrderAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
exclude = ["products"]
|
exclude = ["products"]
|
||||||
|
|
||||||
inlines = [ProductInline, ShopTicketInline]
|
inlines = [ProductInline]
|
||||||
|
|
||||||
actions = [
|
actions = [
|
||||||
"mark_order_as_paid",
|
"mark_order_as_paid",
|
||||||
|
|
|
@ -222,7 +222,9 @@ class Order(CreatedUpdatedModel):
|
||||||
|
|
||||||
if order_product.product.ticket_type.single_ticket_per_product:
|
if order_product.product.ticket_type.single_ticket_per_product:
|
||||||
# This ticket type is one where we only create one ticket
|
# This ticket type is one where we only create one ticket
|
||||||
ticket, created = self.shoptickets.get_or_create(**query_kwargs)
|
ticket, created = order_product.shoptickets.get_or_create(
|
||||||
|
**query_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
msg = (
|
msg = (
|
||||||
|
@ -244,7 +246,7 @@ class Order(CreatedUpdatedModel):
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
else:
|
else:
|
||||||
# We should create a number of tickets equal to OrderProductRelation quantity
|
# We should create a number of tickets equal to OrderProductRelation quantity
|
||||||
already_created_tickets = self.shoptickets.filter(
|
already_created_tickets = order_product.shoptickets.filter(
|
||||||
**query_kwargs
|
**query_kwargs
|
||||||
).count()
|
).count()
|
||||||
tickets_to_create = max(
|
tickets_to_create = max(
|
||||||
|
@ -256,7 +258,7 @@ class Order(CreatedUpdatedModel):
|
||||||
for i in range(
|
for i in range(
|
||||||
0, (order_product.quantity - already_created_tickets)
|
0, (order_product.quantity - already_created_tickets)
|
||||||
):
|
):
|
||||||
ticket = self.shoptickets.create(**query_kwargs)
|
ticket = order_product.shoptickets.create(**query_kwargs)
|
||||||
tickets.append(ticket)
|
tickets.append(ticket)
|
||||||
|
|
||||||
msg = "Created %s tickets of type: %s" % (
|
msg = "Created %s tickets of type: %s" % (
|
||||||
|
|
|
@ -43,16 +43,16 @@ Details for Order #{{ order.id }} | {{ block.super }}
|
||||||
Total
|
Total
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for orp in order.orderproductrelation_set.all %}
|
{% for opr in order.orderproductrelation_set.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{ orp.product.name }}
|
{{ opr.product.name }}
|
||||||
<td>
|
<td>
|
||||||
{{ orp.quantity }}
|
{{ opr.quantity }}
|
||||||
<td>
|
<td>
|
||||||
{{ orp.product.price|currency }}
|
{{ opr.product.price|currency }}
|
||||||
<td>
|
<td>
|
||||||
{{ orp.total|currency }}
|
{{ opr.total|currency }}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -383,7 +383,7 @@ class TestTicketCreation(TestCase):
|
||||||
OrderProductRelationFactory(order=order, product=product, quantity=5)
|
OrderProductRelationFactory(order=order, product=product, quantity=5)
|
||||||
order.mark_as_paid()
|
order.mark_as_paid()
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
ShopTicket.objects.filter(product=product, order=order).count(), 5
|
ShopTicket.objects.filter(product=product, opr__order=order).count(), 5
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_single_ticket_created(self):
|
def test_single_ticket_created(self):
|
||||||
|
@ -394,5 +394,5 @@ class TestTicketCreation(TestCase):
|
||||||
OrderProductRelationFactory(order=order, product=product, quantity=5)
|
OrderProductRelationFactory(order=order, product=product, quantity=5)
|
||||||
order.mark_as_paid()
|
order.mark_as_paid()
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
ShopTicket.objects.filter(product=product, order=order).count(), 1
|
ShopTicket.objects.filter(product=product, opr__order=order).count(), 1
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from shop.models import OrderProductRelation
|
|
||||||
|
|
||||||
from .models import DiscountTicket, ShopTicket, SponsorTicket, TicketType
|
from .models import DiscountTicket, ShopTicket, SponsorTicket, TicketType
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,18 +55,18 @@ class ShopTicketAdmin(BaseTicketAdmin):
|
||||||
"product_quantity",
|
"product_quantity",
|
||||||
]
|
]
|
||||||
|
|
||||||
list_filter = ["ticket_type__camp", "used", "ticket_type", "order", "product"]
|
list_filter = ["ticket_type__camp", "used", "ticket_type", "opr__order", "product"]
|
||||||
|
|
||||||
search_fields = ["uuid", "order__id", "order__user__email", "name", "email"]
|
search_fields = [
|
||||||
|
"uuid",
|
||||||
|
"opr__order__id",
|
||||||
|
"opr__order__user__email",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
]
|
||||||
|
|
||||||
def product_quantity(self, ticket):
|
def product_quantity(self, ticket):
|
||||||
orp = OrderProductRelation.objects.get(
|
return str(ticket.opr.quantity)
|
||||||
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"
|
product_quantity.short_description = "Quantity"
|
||||||
|
|
||||||
|
|
25
src/tickets/migrations/0015_shopticket_opr.py
Normal file
25
src/tickets/migrations/0015_shopticket_opr.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 3.2.5 on 2021-07-26 19:37
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("shop", "0064_add_product_comment_and_cost"),
|
||||||
|
("tickets", "0014_auto_20190803_2241"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="shopticket",
|
||||||
|
name="opr",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="shoptickets",
|
||||||
|
to="shop.orderproductrelation",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
21
src/tickets/migrations/0016_populate_opr.py
Normal file
21
src/tickets/migrations/0016_populate_opr.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 3.2.5 on 2021-07-26 19:38
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def populate_opr(apps, schema_editor):
|
||||||
|
OrderProductRelation = apps.get_model("shop", "OrderProductRelation")
|
||||||
|
ShopTicket = apps.get_model("tickets", "ShopTicket")
|
||||||
|
|
||||||
|
for st in ShopTicket.objects.all():
|
||||||
|
st.opr = OrderProductRelation.objects.get(product=st.product, order=st.order)
|
||||||
|
st.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("tickets", "0015_shopticket_opr"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [migrations.RunPython(populate_opr)]
|
24
src/tickets/migrations/0017_shopticket_opr_not_null.py
Normal file
24
src/tickets/migrations/0017_shopticket_opr_not_null.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 3.2.5 on 2021-07-26 19:52
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("shop", "0064_add_product_comment_and_cost"),
|
||||||
|
("tickets", "0016_populate_opr"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="shopticket",
|
||||||
|
name="opr",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
related_name="shoptickets",
|
||||||
|
to="shop.orderproductrelation",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
17
src/tickets/migrations/0018_remove_shopticket_order.py
Normal file
17
src/tickets/migrations/0018_remove_shopticket_order.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 3.2.5 on 2021-07-26 20:00
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("tickets", "0017_shopticket_opr_not_null"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="shopticket",
|
||||||
|
name="order",
|
||||||
|
),
|
||||||
|
]
|
|
@ -9,7 +9,6 @@ from django.db import models
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from shop.models import OrderProductRelation
|
|
||||||
from utils.models import CampRelatedModel, UUIDModel
|
from utils.models import CampRelatedModel, UUIDModel
|
||||||
from utils.pdf import generate_pdf_letter
|
from utils.pdf import generate_pdf_letter
|
||||||
|
|
||||||
|
@ -104,7 +103,7 @@ class BaseTicket(CampRelatedModel, UUIDModel):
|
||||||
formatdict = {"ticket": self}
|
formatdict = {"ticket": self}
|
||||||
|
|
||||||
if self.ticket_type.single_ticket_per_product and self.shortname == "shop":
|
if self.ticket_type.single_ticket_per_product and self.shortname == "shop":
|
||||||
formatdict["quantity"] = self.orp.quantity
|
formatdict["quantity"] = self.opr.quantity
|
||||||
|
|
||||||
return generate_pdf_letter(
|
return generate_pdf_letter(
|
||||||
filename="{}_ticket_{}.pdf".format(self.shortname, self.pk),
|
filename="{}_ticket_{}.pdf".format(self.shortname, self.pk),
|
||||||
|
@ -138,11 +137,12 @@ class DiscountTicket(BaseTicket):
|
||||||
|
|
||||||
|
|
||||||
class ShopTicket(BaseTicket):
|
class ShopTicket(BaseTicket):
|
||||||
"""Why doesn't this have an FK to OrderProductRelation instead of the fk to Order?"""
|
opr = models.ForeignKey(
|
||||||
|
"shop.OrderProductRelation",
|
||||||
order = models.ForeignKey(
|
related_name="shoptickets",
|
||||||
"shop.Order", related_name="shoptickets", on_delete=models.PROTECT
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
product = models.ForeignKey("shop.Product", on_delete=models.PROTECT)
|
product = models.ForeignKey("shop.Product", on_delete=models.PROTECT)
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
|
@ -178,5 +178,5 @@ class ShopTicket(BaseTicket):
|
||||||
return "shop"
|
return "shop"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def orp(self):
|
def order(self):
|
||||||
return OrderProductRelation.objects.get(product=self.product, order=self.order)
|
return self.opr.order
|
||||||
|
|
|
@ -10,12 +10,11 @@ class TicketTests(TestCase):
|
||||||
def test_correct_token_and_badge_token_are_different(self):
|
def test_correct_token_and_badge_token_are_different(self):
|
||||||
|
|
||||||
ticket_type = TicketTypeFactory()
|
ticket_type = TicketTypeFactory()
|
||||||
|
opr = OrderProductRelationFactory()
|
||||||
orp = OrderProductRelationFactory()
|
|
||||||
shop_ticket = ShopTicket.objects.create(
|
shop_ticket = ShopTicket.objects.create(
|
||||||
ticket_type=ticket_type,
|
ticket_type=ticket_type,
|
||||||
product=orp.product,
|
product=opr.product,
|
||||||
order=orp.order,
|
opr=opr,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertNotEqual(shop_ticket.token, shop_ticket.badge_token)
|
self.assertNotEqual(shop_ticket.token, shop_ticket.badge_token)
|
||||||
|
|
Loading…
Reference in a new issue