dfccfbff51
Add-on purchases (DLC, Season Pass, Battle Pass) previously linked to a parent *purchase* via the `related_purchase` self-FK. When the base game was bought inside a multi-game purchase (e.g. a bundle), there was no per-game purchase to point at — only the whole bundle. Replace it with a `related_game` FK (Game -> Game): an add-on belongs to a *game*, which is unambiguous regardless of how the base game was bought. - models: drop `related_purchase`; add `related_game` (SET_NULL, related_name="addon_purchases"); require it for non-GAME types in `save()`. - forms: replace the parent-purchase picker with a flat `related_game` game search (reusing SearchSelectWidget/_game_options); drop the now unused related_purchase_queryset/RelatedPurchaseChoiceField. - views/urls: remove the obsolete related_purchase_by_game endpoint. - add_purchase.js: drop the parent-dropdown refetch; keep platform auto-fill; retarget the type toggle to #id_related_game. - migration 0020: add -> backfill (related_game = parent's first game by sort_name) -> remove related_purchase. - tests: model validation unit tests + an e2e test for the flat picker. related_game is deliberately game->game so it can later be synced from IGDB's parent_game without schema changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
1.7 KiB
Python
51 lines
1.7 KiB
Python
from datetime import date
|
|
|
|
from django.core.exceptions import ValidationError
|
|
from django.test import TestCase
|
|
|
|
from games.models import Game, Platform, Purchase
|
|
|
|
|
|
class PurchaseRelatedGameTest(TestCase):
|
|
def setUp(self):
|
|
self.platform = Platform.objects.create(name="PC", icon="pc", group="PC")
|
|
self.base_game = Game.objects.create(name="Base Game", platform=self.platform)
|
|
self.dlc_game = Game.objects.create(name="The DLC", platform=self.platform)
|
|
|
|
def test_non_game_purchase_requires_related_game(self):
|
|
purchase = Purchase(
|
|
price=10.0,
|
|
price_currency="USD",
|
|
date_purchased=date(2025, 1, 1),
|
|
type=Purchase.SEASONPASS,
|
|
name="Season Pass",
|
|
)
|
|
with self.assertRaises(ValidationError):
|
|
purchase.save()
|
|
|
|
def test_non_game_purchase_saves_with_related_game(self):
|
|
purchase = Purchase(
|
|
price=10.0,
|
|
price_currency="USD",
|
|
date_purchased=date(2025, 1, 1),
|
|
type=Purchase.SEASONPASS,
|
|
name="Season Pass",
|
|
related_game=self.base_game,
|
|
)
|
|
purchase.save()
|
|
purchase.games.add(self.dlc_game)
|
|
|
|
self.assertEqual(purchase.related_game, self.base_game)
|
|
# Reverse accessor: the base game lists its add-on purchases.
|
|
self.assertIn(purchase, self.base_game.addon_purchases.all())
|
|
|
|
def test_plain_game_purchase_needs_no_related_game(self):
|
|
purchase = Purchase(
|
|
price=50.0,
|
|
price_currency="USD",
|
|
date_purchased=date(2025, 1, 1),
|
|
type=Purchase.GAME,
|
|
)
|
|
purchase.save() # must not raise
|
|
self.assertIsNone(purchase.related_game)
|