feat: derive event lifecycle status from dates and flags
Replace persisted event statuses with publication and cancellation flags, and compute public lifecycle labels from event timing and registration capacity. Keep filters, registration checks, and profile responses aligned with the derived status.
This commit is contained in:
@@ -367,7 +367,7 @@ def create_payment(request, payload: CreatePaymentIn):
|
||||
pk=payload.event_id,
|
||||
is_deleted=False,
|
||||
)
|
||||
if event.status == Event.StatusChoices.CANCELLED:
|
||||
if event.is_cancelled:
|
||||
raise HttpError(400, "این رویداد لغو شده است و امکان پرداخت ندارد.")
|
||||
|
||||
keyed_payment = Payment.objects.filter(
|
||||
@@ -440,14 +440,8 @@ def create_payment(request, payload: CreatePaymentIn):
|
||||
|
||||
needs_new_checkout = not registration or registration.status == Registration.StatusChoices.CANCELLED
|
||||
if needs_new_checkout:
|
||||
if event.status != Event.StatusChoices.PUBLISHED:
|
||||
if event.get_display_status(now=now) != "registration_open":
|
||||
raise HttpError(400, "ثبتنام این رویداد فعال نیست.")
|
||||
if event.registration_start_date and event.registration_start_date > now:
|
||||
raise HttpError(400, "زمان ثبتنام هنوز آغاز نشده است")
|
||||
if event.registration_end_date and event.registration_end_date < now:
|
||||
raise HttpError(400, "مهلت ثبتنام به پایان رسیدهاست")
|
||||
if not event.has_available_slots:
|
||||
raise HttpError(400, "ظرفیت شرکتکنندگان تکمیل است")
|
||||
if not registration or registration.status == Registration.StatusChoices.CANCELLED:
|
||||
registration = Registration.objects.create(
|
||||
event=event,
|
||||
@@ -604,7 +598,7 @@ def create_payment(request, payload: CreatePaymentIn):
|
||||
if (
|
||||
registration is not None
|
||||
and registration.status == Registration.StatusChoices.CANCELLED
|
||||
) or event.status == Event.StatusChoices.CANCELLED:
|
||||
) or event.is_cancelled:
|
||||
if registration is not None:
|
||||
queue_checkout_reconciliation(registration)
|
||||
|
||||
@@ -653,7 +647,7 @@ def _checkout_may_be_confirmed(event, registration, paid_at, now):
|
||||
Registration.StatusChoices.ATTENDED,
|
||||
):
|
||||
return True
|
||||
if event.status == Event.StatusChoices.CANCELLED:
|
||||
if event.is_cancelled:
|
||||
return False
|
||||
|
||||
expiry = registration.checkout_expires_at
|
||||
@@ -736,7 +730,7 @@ def _mark_payment_paid(payment_id: int, verification_data: dict | None = None):
|
||||
|
||||
now = timezone.now()
|
||||
if registration and registration.status == Registration.StatusChoices.PENDING:
|
||||
if event.status == Event.StatusChoices.CANCELLED:
|
||||
if event.is_cancelled:
|
||||
registration.status = Registration.StatusChoices.CANCELLED
|
||||
registration.cancellation_reason = Registration.CancellationReasonChoices.EVENT_CANCELLED
|
||||
registration.save(update_fields=["status", "cancellation_reason", "updated_at"])
|
||||
|
||||
@@ -23,7 +23,7 @@ class PaymentTestMixin:
|
||||
@staticmethod
|
||||
def _create_event(**overrides):
|
||||
now = timezone.now()
|
||||
defaults = {'title': 'Sample', 'description': 'Desc', 'start_time': now, 'end_time': now + timedelta(hours=2), 'registration_start_date': now - timedelta(days=1), 'registration_end_date': now + timedelta(days=5), 'slug': f'event-{uuid.uuid4().hex[:6]}', 'price': 100000, 'capacity': 10, 'status': Event.StatusChoices.PUBLISHED}
|
||||
defaults = {'title': 'Sample', 'description': 'Desc', 'start_time': now + timedelta(days=2), 'end_time': now + timedelta(days=2, hours=2), 'registration_start_date': now - timedelta(days=1), 'registration_end_date': now + timedelta(days=1), 'slug': f'event-{uuid.uuid4().hex[:6]}', 'price': 100000, 'capacity': 10, 'is_published': True}
|
||||
defaults.update(overrides)
|
||||
return Event.objects.create(**defaults)
|
||||
|
||||
|
||||
@@ -243,8 +243,8 @@ def test_late_success_after_user_or_event_cancellation_is_refunded(
|
||||
registration.cancellation_reason = reason
|
||||
registration.save(update_fields=["cancellation_reason"])
|
||||
if cancel_event:
|
||||
payment.event.status = payment.event.StatusChoices.CANCELLED
|
||||
payment.event.save(update_fields=["status"])
|
||||
payment.event.is_cancelled = True
|
||||
payment.event.save(update_fields=["is_cancelled"])
|
||||
mocker.patch("apps.payments.tasks.refund_payment.apply_async")
|
||||
paid_at = (registration.checkout_expires_at - timedelta(seconds=1)).isoformat()
|
||||
|
||||
|
||||
@@ -22,14 +22,16 @@ class TestPaymentsAPIIntegrationTests:
|
||||
|
||||
def setup_method(self):
|
||||
type(self).setUpTestData()
|
||||
self.event = Event.objects.create(title='Pay Event', description='Payment event', start_time=timezone.now(), end_time=timezone.now() + timedelta(hours=2), registration_start_date=timezone.now() - timedelta(days=1), registration_end_date=timezone.now() + timedelta(days=1), slug='pay-event', price=50000, capacity=10, status=Event.StatusChoices.PUBLISHED)
|
||||
now = timezone.now()
|
||||
self.event = Event.objects.create(title='Pay Event', description='Payment event', start_time=now + timedelta(days=2), end_time=now + timedelta(days=2, hours=2), registration_start_date=now - timedelta(days=1), registration_end_date=now + timedelta(days=1), slug='pay-event', price=50000, capacity=10, is_published=True)
|
||||
self.token = create_jwt_token(self.user)
|
||||
|
||||
def _headers(self):
|
||||
return {'HTTP_AUTHORIZATION': f'Bearer {self.token}'}
|
||||
|
||||
def _create_paid_event(self):
|
||||
return Event.objects.create(title='Paid Event', description='Paid', start_time=timezone.now(), end_time=timezone.now() + timedelta(hours=1), registration_start_date=timezone.now() - timedelta(days=1), registration_end_date=timezone.now() + timedelta(days=2), slug=f'paid-{timezone.now().timestamp()}', price=20000, capacity=5, status=Event.StatusChoices.PUBLISHED)
|
||||
now = timezone.now()
|
||||
return Event.objects.create(title='Paid Event', description='Paid', start_time=now + timedelta(days=2), end_time=now + timedelta(days=2, hours=1), registration_start_date=now - timedelta(days=1), registration_end_date=now + timedelta(days=1), slug=f'paid-{now.timestamp()}', price=20000, capacity=5, is_published=True)
|
||||
|
||||
def _create_discount_code(self, event):
|
||||
code = DiscountCode.objects.create(code='DISC50', value=50, is_active=True)
|
||||
@@ -37,7 +39,8 @@ class TestPaymentsAPIIntegrationTests:
|
||||
return code
|
||||
|
||||
def test_create_payment_for_free_event(self):
|
||||
free = Event.objects.create(title='Free', description='Zero', start_time=timezone.now(), end_time=timezone.now() + timedelta(hours=1), registration_start_date=timezone.now() - timedelta(days=1), registration_end_date=timezone.now() + timedelta(days=1), slug='free-event', price=0, capacity=10, status=Event.StatusChoices.PUBLISHED)
|
||||
now = timezone.now()
|
||||
free = Event.objects.create(title='Free', description='Zero', start_time=now + timedelta(days=2), end_time=now + timedelta(days=2, hours=1), registration_start_date=now - timedelta(days=1), registration_end_date=now + timedelta(days=1), slug='free-event', price=0, capacity=10, is_published=True)
|
||||
response = self.client.post('/api/payments/create', data=json.dumps({'event_id': free.id, 'description': 'Free registration'}), content_type='application/json', **self._headers())
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
Reference in New Issue
Block a user