fix: add ledger refund uniqueness guard
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-02 08:37
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('billing', '0002_exchangerate_pricingrule'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddConstraint(
|
||||
model_name='pointsledger',
|
||||
constraint=models.UniqueConstraint(fields=('ref_call', 'change_type'), name='unique_ledger_change_type_per_call'),
|
||||
),
|
||||
]
|
||||
@@ -192,6 +192,10 @@ class PointsLedger(models.Model):
|
||||
condition=~Q(points_delta=0),
|
||||
name="points_ledger_points_delta_non_zero",
|
||||
),
|
||||
models.UniqueConstraint(
|
||||
fields=("ref_call", "change_type"),
|
||||
name="unique_ledger_change_type_per_call",
|
||||
),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=("user", "created_at")),
|
||||
|
||||
@@ -143,6 +143,41 @@ class BillingCoreModelTests(TestCase):
|
||||
balance_after=-1,
|
||||
)
|
||||
|
||||
def test_points_ledger_allows_consume_and_refund_but_rejects_duplicate_refund(self):
|
||||
call = CallRecord.objects.create(
|
||||
user=self.user,
|
||||
operation_type=CallRecord.OperationType.TITLE,
|
||||
alias="title-standard",
|
||||
model_used="gpt-5.5",
|
||||
points_cost=2,
|
||||
status=CallRecord.Status.FAILED,
|
||||
)
|
||||
|
||||
PointsLedger.objects.create(
|
||||
user=self.user,
|
||||
change_type=PointsLedger.ChangeType.CONSUME,
|
||||
points_delta=-2,
|
||||
balance_after=98,
|
||||
ref_call=call,
|
||||
)
|
||||
PointsLedger.objects.create(
|
||||
user=self.user,
|
||||
change_type=PointsLedger.ChangeType.REFUND,
|
||||
points_delta=2,
|
||||
balance_after=100,
|
||||
ref_call=call,
|
||||
)
|
||||
|
||||
with self.assertRaises(IntegrityError):
|
||||
with transaction.atomic():
|
||||
PointsLedger.objects.create(
|
||||
user=self.user,
|
||||
change_type=PointsLedger.ChangeType.REFUND,
|
||||
points_delta=2,
|
||||
balance_after=102,
|
||||
ref_call=call,
|
||||
)
|
||||
|
||||
def test_billing_models_are_registered_in_admin(self):
|
||||
self.assertIn(UserWallet, admin.site._registry)
|
||||
self.assertIn(ApiKey, admin.site._registry)
|
||||
|
||||
Reference in New Issue
Block a user