Bugfixes for emails and bugfixes for trade acceptance quantities being checked on create, closes #1
This commit is contained in:
parent
32da8157a6
commit
bd7a65975f
21 changed files with 95 additions and 86 deletions
|
|
@ -26,28 +26,22 @@ class TradeOfferManager(models.Manager):
|
|||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset().select_related(
|
||||
"initiated_by",
|
||||
"initiated_by__user",
|
||||
).prefetch_related(
|
||||
"trade_offer_have_cards__card",
|
||||
"trade_offer_want_cards__card",
|
||||
"acceptances",
|
||||
"acceptances__accepted_by",
|
||||
"acceptances__requested_card",
|
||||
"acceptances__offered_card",
|
||||
"acceptances__accepted_by__user",
|
||||
)
|
||||
|
||||
cutoff = timezone.now() - timedelta(days=28)
|
||||
qs = qs.filter(created_at__gte=cutoff)
|
||||
return qs.order_by("-updated_at")
|
||||
|
||||
class TradeOfferAllManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
# Return all trade offers without filtering by the cutoff.
|
||||
return super().get_queryset()
|
||||
|
||||
class TradeOffer(models.Model):
|
||||
objects = TradeOfferManager()
|
||||
all_offers = TradeOfferAllManager()
|
||||
|
||||
id = models.AutoField(primary_key=True)
|
||||
is_closed = models.BooleanField(default=False, db_index=True)
|
||||
|
|
@ -106,6 +100,17 @@ class TradeOffer(models.Model):
|
|||
# Use super().save() here to avoid recursion.
|
||||
super(TradeOffer, self).save(update_fields=["rarity_level", "rarity_icon"])
|
||||
|
||||
# New derived properties for available cards
|
||||
@property
|
||||
def have_cards_available(self):
|
||||
# Returns the list of have_cards (through objects) that still have available quantity.
|
||||
return [item for item in self.trade_offer_have_cards.all() if item.quantity > item.qty_accepted]
|
||||
|
||||
@property
|
||||
def want_cards_available(self):
|
||||
# Returns the list of want_cards (through objects) that still have available quantity.
|
||||
return [item for item in self.trade_offer_want_cards.all() if item.quantity > item.qty_accepted]
|
||||
|
||||
class TradeOfferHaveCard(models.Model):
|
||||
"""
|
||||
Through model for TradeOffer.have_cards.
|
||||
|
|
@ -175,6 +180,16 @@ class TradeAcceptance(models.Model):
|
|||
THANKED_BY_BOTH = 'THANKED_BY_BOTH', 'Thanked by Both'
|
||||
REJECTED_BY_INITIATOR = 'REJECTED_BY_INITIATOR', 'Rejected by Initiator'
|
||||
REJECTED_BY_ACCEPTOR = 'REJECTED_BY_ACCEPTOR', 'Rejected by Acceptor'
|
||||
|
||||
# DRY improvement: define active states once as a class-level constant.
|
||||
ACTIVE_STATES = [
|
||||
AcceptanceState.ACCEPTED,
|
||||
AcceptanceState.SENT,
|
||||
AcceptanceState.RECEIVED,
|
||||
AcceptanceState.THANKED_BY_INITIATOR,
|
||||
AcceptanceState.THANKED_BY_ACCEPTOR,
|
||||
AcceptanceState.THANKED_BY_BOTH,
|
||||
]
|
||||
|
||||
trade_offer = models.ForeignKey(
|
||||
TradeOffer,
|
||||
|
|
@ -187,13 +202,11 @@ class TradeAcceptance(models.Model):
|
|||
on_delete=models.PROTECT,
|
||||
related_name='trade_acceptances'
|
||||
)
|
||||
# The acceptor selects one card the initiator is offering (from have_cards)
|
||||
requested_card = models.ForeignKey(
|
||||
"cards.Card",
|
||||
on_delete=models.PROTECT,
|
||||
related_name='accepted_requested'
|
||||
)
|
||||
# And one card from the initiator's wanted cards (from want_cards)
|
||||
offered_card = models.ForeignKey(
|
||||
"cards.Card",
|
||||
on_delete=models.PROTECT,
|
||||
|
|
@ -266,40 +279,25 @@ class TradeAcceptance(models.Model):
|
|||
return not self.is_completed_or_rejected
|
||||
|
||||
def clean(self):
|
||||
# Validate that the requested and offered cards exist in the through tables.
|
||||
from django.core.exceptions import ValidationError
|
||||
try:
|
||||
have_through_obj = self.trade_offer.trade_offer_have_cards.get(card_id=self.requested_card_id)
|
||||
have_card = self.trade_offer.trade_offer_have_cards.get(card_id=self.requested_card_id)
|
||||
except TradeOfferHaveCard.DoesNotExist:
|
||||
raise ValidationError("The requested card must be one of the trade offer's available cards (have_cards).")
|
||||
|
||||
try:
|
||||
want_through_obj = self.trade_offer.trade_offer_want_cards.get(card_id=self.offered_card_id)
|
||||
want_card = self.trade_offer.trade_offer_want_cards.get(card_id=self.offered_card_id)
|
||||
except TradeOfferWantCard.DoesNotExist:
|
||||
raise ValidationError("The offered card must be one of the trade offer's requested cards (want_cards).")
|
||||
|
||||
if not self.pk and self.trade_offer.is_closed:
|
||||
raise ValidationError("This trade offer is closed. No more acceptances are allowed.")
|
||||
|
||||
active_states = [
|
||||
self.AcceptanceState.ACCEPTED,
|
||||
self.AcceptanceState.SENT,
|
||||
self.AcceptanceState.RECEIVED,
|
||||
self.AcceptanceState.THANKED_BY_INITIATOR,
|
||||
self.AcceptanceState.THANKED_BY_ACCEPTOR,
|
||||
self.AcceptanceState.THANKED_BY_BOTH,
|
||||
]
|
||||
|
||||
active_acceptances = self.trade_offer.acceptances.filter(state__in=active_states)
|
||||
if self.pk:
|
||||
active_acceptances = active_acceptances.exclude(pk=self.pk)
|
||||
|
||||
requested_count = active_acceptances.filter(requested_card_id=self.requested_card_id).count()
|
||||
if requested_count >= have_through_obj.quantity:
|
||||
raise ValidationError("This requested card has been fully accepted.")
|
||||
|
||||
offered_count = active_acceptances.filter(offered_card_id=self.offered_card_id).count()
|
||||
if offered_count >= want_through_obj.quantity:
|
||||
raise ValidationError("This offered card has already been fully used.")
|
||||
# Only perform these validations on creation (when self.pk is None).
|
||||
if self.pk is None:
|
||||
if self.trade_offer.is_closed:
|
||||
raise ValidationError("This trade offer is closed. No more acceptances are allowed.")
|
||||
# Use direct comparison with qty_accepted and quantity.
|
||||
if have_card.qty_accepted >= have_card.quantity:
|
||||
raise ValidationError("The requested card has no available quantity.")
|
||||
if want_card.qty_accepted >= want_card.quantity:
|
||||
raise ValidationError("The offered card has no available quantity.")
|
||||
|
||||
def get_step_number(self):
|
||||
if self.state in [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue