From 517f29940a00c3369bee1860acd740ede084f611 Mon Sep 17 00:00:00 2001 From: Thomas Steen Rasmussen Date: Thu, 29 Jul 2021 09:03:52 +0200 Subject: [PATCH] remove FK from ShopTicket to Order and replace with an FK to OrderProductRelation --- README.md | 3 +++ src/backoffice/templates/info_desk/scan.html | 2 +- .../templates/tickets/ticket_list.html | 2 +- src/shop/admin.py | 4 +-- src/shop/models.py | 8 +++--- src/shop/templates/order_review.html | 10 ++++---- src/shop/tests.py | 4 +-- src/tickets/admin.py | 20 +++++++-------- src/tickets/migrations/0015_shopticket_opr.py | 25 +++++++++++++++++++ src/tickets/migrations/0016_populate_opr.py | 21 ++++++++++++++++ .../0017_shopticket_opr_not_null.py | 24 ++++++++++++++++++ .../0018_remove_shopticket_order.py | 17 +++++++++++++ src/tickets/models.py | 16 ++++++------ src/tickets/tests.py | 7 +++--- 14 files changed, 125 insertions(+), 38 deletions(-) create mode 100644 src/tickets/migrations/0015_shopticket_opr.py create mode 100644 src/tickets/migrations/0016_populate_opr.py create mode 100644 src/tickets/migrations/0017_shopticket_opr_not_null.py create mode 100644 src/tickets/migrations/0018_remove_shopticket_order.py diff --git a/README.md b/README.md index ae019ec8..7a238fc7 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,9 @@ Enjoy! ## 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 new camp by running: diff --git a/src/backoffice/templates/info_desk/scan.html b/src/backoffice/templates/info_desk/scan.html index 018a326f..cac5e43e 100644 --- a/src/backoffice/templates/info_desk/scan.html +++ b/src/backoffice/templates/info_desk/scan.html @@ -65,7 +65,7 @@ Scan | {{ block.super }} Quantity: - {{ ticket.orp.quantity }} + {{ ticket.opr.quantity }} {% endif %} diff --git a/src/profiles/templates/tickets/ticket_list.html b/src/profiles/templates/tickets/ticket_list.html index a32b1d31..08d2eb40 100644 --- a/src/profiles/templates/tickets/ticket_list.html +++ b/src/profiles/templates/tickets/ticket_list.html @@ -44,7 +44,7 @@ Your Tickets | {{ block.super }} {{ ticket.product.name }} - {% 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 }} {% if ticket.used %} diff --git a/src/shop/admin.py b/src/shop/admin.py index 514b1730..ea87fbd1 100644 --- a/src/shop/admin.py +++ b/src/shop/admin.py @@ -1,7 +1,5 @@ from django.contrib import admin -from tickets.admin import ShopTicketInline - from .models import ( CoinifyAPICallback, CoinifyAPIInvoice, @@ -138,7 +136,7 @@ class OrderAdmin(admin.ModelAdmin): exclude = ["products"] - inlines = [ProductInline, ShopTicketInline] + inlines = [ProductInline] actions = [ "mark_order_as_paid", diff --git a/src/shop/models.py b/src/shop/models.py index 332fe2b7..c29c904d 100644 --- a/src/shop/models.py +++ b/src/shop/models.py @@ -222,7 +222,9 @@ class Order(CreatedUpdatedModel): 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) + ticket, created = order_product.shoptickets.get_or_create( + **query_kwargs + ) if created: msg = ( @@ -244,7 +246,7 @@ class Order(CreatedUpdatedModel): messages.success(request, msg) else: # 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 ).count() tickets_to_create = max( @@ -256,7 +258,7 @@ class Order(CreatedUpdatedModel): for i in range( 0, (order_product.quantity - already_created_tickets) ): - ticket = self.shoptickets.create(**query_kwargs) + ticket = order_product.shoptickets.create(**query_kwargs) tickets.append(ticket) msg = "Created %s tickets of type: %s" % ( diff --git a/src/shop/templates/order_review.html b/src/shop/templates/order_review.html index c4e87208..691beee4 100644 --- a/src/shop/templates/order_review.html +++ b/src/shop/templates/order_review.html @@ -43,16 +43,16 @@ Details for Order #{{ order.id }} | {{ block.super }} Total - {% for orp in order.orderproductrelation_set.all %} + {% for opr in order.orderproductrelation_set.all %} - {{ orp.product.name }} + {{ opr.product.name }} - {{ orp.quantity }} + {{ opr.quantity }} - {{ orp.product.price|currency }} + {{ opr.product.price|currency }} - {{ orp.total|currency }} + {{ opr.total|currency }} {% endfor %} diff --git a/src/shop/tests.py b/src/shop/tests.py index e0b78bab..cf289af0 100644 --- a/src/shop/tests.py +++ b/src/shop/tests.py @@ -383,7 +383,7 @@ class TestTicketCreation(TestCase): OrderProductRelationFactory(order=order, product=product, quantity=5) order.mark_as_paid() 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): @@ -394,5 +394,5 @@ class TestTicketCreation(TestCase): OrderProductRelationFactory(order=order, product=product, quantity=5) order.mark_as_paid() self.assertEquals( - ShopTicket.objects.filter(product=product, order=order).count(), 1 + ShopTicket.objects.filter(product=product, opr__order=order).count(), 1 ) diff --git a/src/tickets/admin.py b/src/tickets/admin.py index a14c0e37..bb23291a 100644 --- a/src/tickets/admin.py +++ b/src/tickets/admin.py @@ -1,7 +1,5 @@ from django.contrib import admin -from shop.models import OrderProductRelation - from .models import DiscountTicket, ShopTicket, SponsorTicket, TicketType @@ -57,18 +55,18 @@ class ShopTicketAdmin(BaseTicketAdmin): "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): - orp = OrderProductRelation.objects.get( - product=ticket.product, order=ticket.order - ) - - return ( - str(orp.quantity) if ticket.ticket_type.single_ticket_per_product else "1" - ) + return str(ticket.opr.quantity) product_quantity.short_description = "Quantity" diff --git a/src/tickets/migrations/0015_shopticket_opr.py b/src/tickets/migrations/0015_shopticket_opr.py new file mode 100644 index 00000000..d3c7b487 --- /dev/null +++ b/src/tickets/migrations/0015_shopticket_opr.py @@ -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", + ), + ), + ] diff --git a/src/tickets/migrations/0016_populate_opr.py b/src/tickets/migrations/0016_populate_opr.py new file mode 100644 index 00000000..13d6a686 --- /dev/null +++ b/src/tickets/migrations/0016_populate_opr.py @@ -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)] diff --git a/src/tickets/migrations/0017_shopticket_opr_not_null.py b/src/tickets/migrations/0017_shopticket_opr_not_null.py new file mode 100644 index 00000000..90e92b4d --- /dev/null +++ b/src/tickets/migrations/0017_shopticket_opr_not_null.py @@ -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", + ), + ), + ] diff --git a/src/tickets/migrations/0018_remove_shopticket_order.py b/src/tickets/migrations/0018_remove_shopticket_order.py new file mode 100644 index 00000000..8caea32b --- /dev/null +++ b/src/tickets/migrations/0018_remove_shopticket_order.py @@ -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", + ), + ] diff --git a/src/tickets/models.py b/src/tickets/models.py index dad743cd..92524c90 100644 --- a/src/tickets/models.py +++ b/src/tickets/models.py @@ -9,7 +9,6 @@ from django.db import models from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from shop.models import OrderProductRelation from utils.models import CampRelatedModel, UUIDModel from utils.pdf import generate_pdf_letter @@ -104,7 +103,7 @@ class BaseTicket(CampRelatedModel, UUIDModel): formatdict = {"ticket": self} 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( filename="{}_ticket_{}.pdf".format(self.shortname, self.pk), @@ -138,11 +137,12 @@ class DiscountTicket(BaseTicket): class ShopTicket(BaseTicket): - """Why doesn't this have an FK to OrderProductRelation instead of the fk to Order?""" - - order = models.ForeignKey( - "shop.Order", related_name="shoptickets", on_delete=models.PROTECT + opr = models.ForeignKey( + "shop.OrderProductRelation", + related_name="shoptickets", + on_delete=models.PROTECT, ) + product = models.ForeignKey("shop.Product", on_delete=models.PROTECT) name = models.CharField( @@ -178,5 +178,5 @@ class ShopTicket(BaseTicket): return "shop" @property - def orp(self): - return OrderProductRelation.objects.get(product=self.product, order=self.order) + def order(self): + return self.opr.order diff --git a/src/tickets/tests.py b/src/tickets/tests.py index 7a44a7e4..b6fcb3f2 100644 --- a/src/tickets/tests.py +++ b/src/tickets/tests.py @@ -10,12 +10,11 @@ class TicketTests(TestCase): def test_correct_token_and_badge_token_are_different(self): ticket_type = TicketTypeFactory() - - orp = OrderProductRelationFactory() + opr = OrderProductRelationFactory() shop_ticket = ShopTicket.objects.create( ticket_type=ticket_type, - product=orp.product, - order=orp.order, + product=opr.product, + opr=opr, ) self.assertNotEqual(shop_ticket.token, shop_ticket.badge_token)