feature/allow-users #1

Merged
Ghasemi merged 2 commits from feature/allow-users into master 2026-08-03 09:24:15 -04:00
3 changed files with 79 additions and 3 deletions
Showing only changes of commit c4ff4cf4cb - Show all commits

View file

@ -2,7 +2,7 @@ from functools import update_wrapper
from django.contrib import admin
from .models import Promotion, Plan, Event, Recipient, EventSaver
from .models import Promotion, Plan, Event, Recipient, EventSaver, AllowedUser
class PromotionAdmin(admin.ModelAdmin):
list_display = ['user_uuid', 'plan', 'promotion_amount', 'recipient', 'event', 'created_at']
@ -10,9 +10,13 @@ class PromotionAdmin(admin.ModelAdmin):
class PlanAdmin(admin.ModelAdmin):
list_display = ['uuid', 'title', 'balance']
class AllowedUserAdmin(admin.ModelAdmin):
list_display = ['user', 'recipient', 'created_at']
admin.site.register(Plan, PlanAdmin)
admin.site.register(Promotion, PromotionAdmin)
admin.site.register(Event)
admin.site.register(EventSaver)
admin.site.register(Recipient)
admin.site.register(AllowedUser, AllowedUserAdmin)

View file

@ -0,0 +1,40 @@
import uuid
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('promotions', '0008_plan_balance_holder_alter_plan_description_details'),
]
operations = [
migrations.AddField(
model_name='recipient',
name='access_type',
field=models.CharField(choices=[('restricted', 'restricted'), ('public', 'public')],
db_index=True, default='public', max_length=64, verbose_name='access type'),
),
migrations.CreateModel(
name='AllowedUser',
fields=[
('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True,
serialize=False, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('updated_at', models.DateTimeField(auto_now=True, db_index=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
related_name='allowed_recipients', to=settings.AUTH_USER_MODEL)),
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
related_name='allowed_users', to='promotions.recipient')),
],
options={
'constraints': [
models.UniqueConstraint(fields=('user', 'recipient'), name='unique_allowed_user_recipient'),
],
},
),
]

View file

@ -135,9 +135,11 @@ class Plan(BaseModel):
def __str__(self):
return self.title
def get_configured_promotion_amount(self):
def get_configured_promotion_amount(self, user_uuid=None):
amount = 0
for recipient in self.recipients.filter(recipient_uuid_field='->event:user'): #TODO: instead of hard code use better soloution
if not recipient.is_user_allowed(user_uuid):
continue
base_amount, promotion_amount = recipient.get_promotion_amount()
amount += promotion_amount
return amount
@ -180,12 +182,19 @@ class Plan(BaseModel):
return updated
class RecipientTypeChoices(models.TextChoices):
RESTRICTED = 'restricted', _('restricted')
PUBLIC = 'public', _('public')
class Recipient(BaseModel):
label = models.CharField(max_length=255, db_index=True)
plan = models.ForeignKey(Plan, on_delete=models.PROTECT, related_name='recipients', null=True, blank=True)
wallet_uuid = models.UUIDField(null=True, blank=True)
promotion_type = models.CharField(max_length=64, verbose_name=_('promotion type'), db_index=True,
choices=PromotionTypeChoices.choices, null=True, blank=True)
access_type = models.CharField(max_length=64, verbose_name=_('access type'), db_index=True,
choices=RecipientTypeChoices.choices, default=RecipientTypeChoices.PUBLIC)
recipient_uuid_field = models.CharField(max_length=255, db_index=True)
base_amount_field = models.CharField(max_length=255, db_index=True)
@ -207,6 +216,13 @@ class Recipient(BaseModel):
def get_wallet_category_uuid(self):
return self.wallet_uuid or settings.WALLET_PROMOTION_CATEGORY_UUID
def is_user_allowed(self, user_uuid):
if self.access_type != RecipientTypeChoices.RESTRICTED:
return True
if not user_uuid:
return False
return self.allowed_users.filter(user__uuid=user_uuid).exists()
def get_recipient_uuid(self, plan=None, event=None):
if ':' in self.recipient_uuid_field and not self.recipient_uuid_field.startswith('QS:'):
filters_key_values, recipient_candidate = self.recipient_uuid_field.split('->')
@ -315,6 +331,9 @@ class Recipient(BaseModel):
if not recipient:
return None
if not self.is_user_allowed(recipient):
return None
base_amount, promotion_amount = self.get_promotion_amount(plan=plan, event=event)
promotion, created = Promotion.objects.get_or_create(
promotion_amount=promotion_amount,
@ -341,6 +360,19 @@ class Recipient(BaseModel):
return None
class AllowedUser(BaseModel):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='allowed_recipients')
recipient = models.ForeignKey(Recipient, on_delete=models.CASCADE, related_name='allowed_users')
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'recipient'], name='unique_allowed_user_recipient')
]
def __str__(self):
return f"{self.user} -> {self.recipient}"
class PaymentStateChoices(models.IntegerChoices):
CREATED = 1, _('created')
DELAYED = 2, _('delayed') # delayed as user wish
@ -365,7 +397,7 @@ def get_event_status_for_user(user_uuid, event_label):
if not processed:
plan = Plan.objects.filter(event_list__icontains=event_label).first()
if plan:
promotion_amount = plan.get_configured_promotion_amount()
promotion_amount = plan.get_configured_promotion_amount(user_uuid=user_uuid)
return {
'event_label': event_label,
'processed': processed,