Compare commits

...

4 commits

Author SHA1 Message Date
1a63acee32 Merge pull request 'FIX(promotions): default event status promotion_amount to 0' (#2) from feature/allow-users into master
Reviewed-on: #2
2026-08-03 11:04:17 -04:00
54de7fdb48 PERF(promotions): collapse allowed-user check into a single query
Fold the restricted-recipient access check into the recipients
queryset itself via Q(public OR restricted-and-allowed), joining
against AllowedUser instead of running a separate lookup query.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 18:21:23 +03:30
33aa0ed1b4 PERF(promotions): batch allowed-user lookup in get_configured_promotion_amount
Checking each recipient via Recipient.is_user_allowed() inside the
loop issued one AllowedUser query per restricted recipient (N+1).
Fetch all matching AllowedUser rows for the plan's recipients in a
single query up front instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 17:55:42 +03:30
ac37f15bea FIX(promotions): default event status promotion_amount to 0
get_event_status_for_user returned None whenever the event was
already processed, no plan matched, or the user lacked access to the
recipient. Callers now always get a concrete amount: the real payout
if accessible, 0 otherwise.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 17:33:59 +03:30

View file

@ -3,7 +3,7 @@ import uuid
from django.conf import settings from django.conf import settings
from django.contrib.postgres.indexes import GinIndex from django.contrib.postgres.indexes import GinIndex
from django.db import models, transaction from django.db import models, transaction
from django.db.models import F from django.db.models import F, Q
from django.utils import timezone from django.utils import timezone
from django_minio_backend import MinioBackend, iso_date_prefix from django_minio_backend import MinioBackend, iso_date_prefix
@ -136,10 +136,16 @@ class Plan(BaseModel):
return self.title return self.title
def get_configured_promotion_amount(self, user_uuid=None): def get_configured_promotion_amount(self, user_uuid=None):
access_filter = Q(access_type=RecipientTypeChoices.PUBLIC)
if user_uuid:
access_filter |= Q(access_type=RecipientTypeChoices.RESTRICTED, allowed_users__user__uuid=user_uuid)
recipients = self.recipients.filter(
recipient_uuid_field='->event:user' #TODO: instead of hard code use better soloution
).filter(access_filter).distinct()
amount = 0 amount = 0
for recipient in self.recipients.filter(recipient_uuid_field='->event:user'): #TODO: instead of hard code use better soloution for recipient in recipients:
if not recipient.is_user_allowed(user_uuid):
continue
base_amount, promotion_amount = recipient.get_promotion_amount() base_amount, promotion_amount = recipient.get_promotion_amount()
amount += promotion_amount amount += promotion_amount
return amount return amount
@ -393,7 +399,7 @@ class PromotionQuerySet(models.QuerySet):
def get_event_status_for_user(user_uuid, event_label): def get_event_status_for_user(user_uuid, event_label):
processed = Promotion.objects.is_processed_for_user(user_uuid, event_label) processed = Promotion.objects.is_processed_for_user(user_uuid, event_label)
promotion_amount = None promotion_amount = 0
if not processed: if not processed:
plan = Plan.objects.filter(event_list__icontains=event_label).first() plan = Plan.objects.filter(event_list__icontains=event_label).first()
if plan: if plan: