Files
www/membership/members/models.py
T
2026-10-29 13:28:11 -04:00

93 lines
4.1 KiB
Python
Executable File

import uuid
from django.db import models
class Account(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
uid = models.CharField(max_length=48, unique=True)
email = models.EmailField()
ldap_dn = models.CharField(max_length=1024, blank=True)
ldap_uuid = models.CharField(max_length=64, unique=True, null=True, blank=True)
oidc_issuer = models.CharField(max_length=2048, blank=True, default="")
oidc_sub = models.CharField(max_length=512, unique=True, null=True)
membership_level = models.PositiveSmallIntegerField(default=0)
stripe_customer = models.CharField(max_length=128, unique=True, null=True)
checkout_id = models.CharField(max_length=128, blank=True)
checkout_key = models.UUIDField(default=uuid.uuid4)
last_billing_sync = models.DateTimeField(null=True)
last_shkeeper_sync = models.DateTimeField(null=True)
last_ldap_sync = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Registration(models.Model):
token_hash = models.CharField(max_length=64, primary_key=True)
uid = models.CharField(max_length=48)
email = models.EmailField()
expires_at = models.DateTimeField(db_index=True)
class ProfileEmailChange(models.Model):
token_hash = models.CharField(max_length=64, primary_key=True)
account = models.ForeignKey(Account, on_delete=models.CASCADE)
email = models.EmailField()
ldap_uuid = models.CharField(max_length=64)
expires_at = models.DateTimeField(db_index=True)
class RateWindow(models.Model):
key = models.CharField(max_length=64, primary_key=True)
count = models.PositiveIntegerField(default=0)
expires_at = models.DateTimeField(db_index=True)
class Subscription(models.Model):
stripe_id = models.CharField(max_length=128, primary_key=True)
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="subscriptions")
tier = models.CharField(max_length=48, blank=True)
status = models.CharField(max_length=32)
paid_until = models.DateTimeField(null=True)
cancel_at_period_end = models.BooleanField(default=False)
class ManagedGroup(models.Model):
"""Retain old mappings so removing a tier from config also revokes its group."""
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name="managed_groups")
dn = models.CharField(max_length=1024)
class Meta:
constraints = [models.UniqueConstraint(fields=["account", "dn"], name="unique_account_group")]
class CryptoInvoice(models.Model):
"""Immutable prepaid order; one canonical invoice can grant one month only."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
account = models.ForeignKey(Account, on_delete=models.PROTECT)
tier = models.CharField(max_length=48)
usd_amount = models.DecimalField(max_digits=12, decimal_places=2)
crypto = models.CharField(max_length=32)
provider_id = models.CharField(max_length=128, blank=True)
wallet = models.CharField(max_length=512, blank=True)
crypto_amount = models.CharField(max_length=128, blank=True)
# SHKeeper invoices do not expire. Keep incomplete orders available for late
# payments; never silently discard a deposit address with money on the way.
status = models.CharField(max_length=24, default="creating")
credited_at = models.DateTimeField(null=True)
access_start = models.DateTimeField(null=True)
access_end = models.DateTimeField(null=True, db_index=True)
last_checked_at = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [models.UniqueConstraint(fields=["account"], condition=models.Q(credited_at__isnull=True), name="one_uncredited_crypto_invoice")]
class WebhookEvent(models.Model):
stripe_id = models.CharField(max_length=128, primary_key=True)
account = models.ForeignKey(Account, on_delete=models.CASCADE, null=True)
received_at = models.DateTimeField(auto_now_add=True)
processed_at = models.DateTimeField(null=True)