feat(billing): period usage aggregation by metric function
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -58,3 +58,27 @@ class FusionBillingUsage(models.Model):
|
|||||||
existing.write({'quantity': quantity})
|
existing.write({'quantity': quantity})
|
||||||
return existing
|
return existing
|
||||||
return self.create(vals)
|
return self.create(vals)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _aggregate(self, subscription, metric, period_start, period_end):
|
||||||
|
"""Aggregate stored usage for a subscription+metric within [period_start, period_end)
|
||||||
|
using the metric's aggregation function."""
|
||||||
|
rows = self.search([
|
||||||
|
('subscription_id', '=', subscription.id),
|
||||||
|
('metric_id', '=', metric.id),
|
||||||
|
('period_start', '>=', period_start),
|
||||||
|
('period_end', '<=', period_end),
|
||||||
|
])
|
||||||
|
qtys = rows.mapped('quantity')
|
||||||
|
if not qtys:
|
||||||
|
return 0.0
|
||||||
|
agg = metric.aggregation
|
||||||
|
if agg == 'sum':
|
||||||
|
return sum(qtys)
|
||||||
|
if agg == 'max':
|
||||||
|
return max(qtys)
|
||||||
|
if agg == 'last':
|
||||||
|
return rows.sorted('period_start')[-1].quantity
|
||||||
|
if agg == 'unique_count':
|
||||||
|
return float(len(set(qtys)))
|
||||||
|
return sum(qtys)
|
||||||
|
|||||||
@@ -31,3 +31,22 @@ class TestUsageIngestion(TransactionCase):
|
|||||||
rows = self.Usage.search([('idempotency_key', '=', k)])
|
rows = self.Usage.search([('idempotency_key', '=', k)])
|
||||||
self.assertEqual(len(rows), 1) # no duplicate
|
self.assertEqual(len(rows), 1) # no duplicate
|
||||||
self.assertEqual(rows.quantity, 175.0) # last value wins for the same key
|
self.assertEqual(rows.quantity, 175.0) # last value wins for the same key
|
||||||
|
|
||||||
|
def test_aggregate_sum(self):
|
||||||
|
for i, q in enumerate([10.0, 20.0, 30.0]):
|
||||||
|
self.Usage._record_usage(self.sub, 'cpu_seconds', q,
|
||||||
|
'2026-05-01', '2026-06-01', idem='cpu-%d' % i)
|
||||||
|
total = self.Usage._aggregate(self.sub, self.metric, '2026-05-01', '2026-06-01')
|
||||||
|
self.assertEqual(total, 60.0)
|
||||||
|
|
||||||
|
def test_aggregate_max(self):
|
||||||
|
self.metric.aggregation = 'max'
|
||||||
|
for i, q in enumerate([10.0, 55.0, 30.0]):
|
||||||
|
self.Usage._record_usage(self.sub, 'cpu_seconds', q,
|
||||||
|
'2026-05-01', '2026-06-01', idem='m-%d' % i)
|
||||||
|
self.assertEqual(self.Usage._aggregate(self.sub, self.metric, '2026-05-01', '2026-06-01'), 55.0)
|
||||||
|
|
||||||
|
def test_aggregate_excludes_other_periods(self):
|
||||||
|
self.Usage._record_usage(self.sub, 'cpu_seconds', 99.0, '2026-04-01', '2026-05-01', idem='apr')
|
||||||
|
self.Usage._record_usage(self.sub, 'cpu_seconds', 5.0, '2026-05-01', '2026-06-01', idem='may')
|
||||||
|
self.assertEqual(self.Usage._aggregate(self.sub, self.metric, '2026-05-01', '2026-06-01'), 5.0)
|
||||||
|
|||||||
Reference in New Issue
Block a user