Users - Analytics Service
Location: api/nextango/apps/users/analytics_service.py
Last Updated: 2026-07-08
Overview
Provides aggregated customer and employee metrics for the analytics dashboard. Two service classes cover separate concerns: CustomerAnalyticsService for customer KPIs (acquisition, retention, lifetime value, loyalty) and EmployeeAnalyticsService for staff performance (sales totals, transaction counts, cash drawer activity). All methods use the replica database when USE_REPLICA_DATABASE=True and cache results for 5 minutes.
Class: CustomerAnalyticsService
Aggregation methods for customer analytics derived from Customer, User, and SaleTransaction records.
get_customer_summary()
Purpose: Returns headline customer KPIs: total and new customers, returning customers, average lifetime value, loyalty points and store credit outstanding, and marketing opt-in rate.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter transaction-based metrics by store UUID or Sanity ID |
start_date | date | No | Start of period (inclusive) |
end_date | date | No | End of period (inclusive) |
Returns:
{
'total_customers': int,
'new_customers': int, # Customers created within the date range
'returning_customers': int, # Customers with 2+ transactions in period
'average_lifetime_value': Decimal, # All-time avg spend per customer
'total_loyalty_points_outstanding': int,
'total_store_credit_outstanding': Decimal,
'marketing_opt_in_rate': float, # Percentage (0–100)
}
Example:
from nextango.apps.users.analytics_service import CustomerAnalyticsService
from datetime import date
result = CustomerAnalyticsService.get_customer_summary(
store_id='store-uuid',
start_date=date(2026, 1, 1),
end_date=date(2026, 1, 31),
)
print(f"New customers this month: {result['new_customers']}")
get_top_customers()
Purpose: Returns the top N customers by total spend in a period, with order count, average order value, and last purchase date.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID or Sanity ID |
start_date | date | No | Start of period |
end_date | date | No | End of period |
limit | int | No | Max results (default: 10) |
Returns:
{
'data': [
{
'customer_id': UUID,
'name': str,
'email': str,
'total_spent': Decimal,
'order_count': int,
'avg_order_value': Decimal,
'last_purchase': str, # ISO 8601 datetime string
},
...
]
}
Rows are ordered by total_spent descending.
Example:
result = CustomerAnalyticsService.get_top_customers(limit=5)
for customer in result['data']:
print(f"{customer['name']}: £{customer['total_spent']}")
Class: EmployeeAnalyticsService
Aggregation methods for employee performance derived from SaleTransaction and CashDrawerEvent records.
get_employee_performance()
Purpose: Returns per-employee sales totals, transaction counts, average transaction value, and items sold for completed transactions in a period.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID or Sanity ID |
start_date | date | No | Start of period |
end_date | date | No | End of period |
Returns:
{
'data': [
{
'employee_id': UUID,
'name': str,
'total_sales': Decimal,
'transaction_count': int,
'avg_transaction_value': Decimal,
'items_sold': int,
},
...
]
}
Rows are ordered by total_sales descending.
Example:
from nextango.apps.users.analytics_service import EmployeeAnalyticsService
result = EmployeeAnalyticsService.get_employee_performance(store_id='store-uuid')
for emp in result['data']:
print(f"{emp['name']}: {emp['transaction_count']} transactions")
get_cash_drawer_summary()
Purpose: Returns per-employee cash drawer activity (open/close counts, total cash sales, deposits, no-sale events) for a period.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID or Sanity ID |
start_date | date | No | Start of period |
end_date | date | No | End of period |
Returns:
{
'data': [
{
'employee_name': str,
'open_count': int,
'close_count': int,
'total_cash_sales': Decimal,
'total_deposits': Decimal,
'no_sale_count': int,
'total_events': int,
},
...
]
}
Rows are ordered by total_cash_sales descending.
get_rfm_scores()
Purpose: Scores all customers using the RFM model (Recency, Frequency, Monetary). Each dimension is ranked into quintiles (1–5), and the combined score ranks customers by overall value.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID or Sanity ID |
Returns:
{
'data': [
{
'customer_id': UUID,
'r_score': int, # 1–5, higher = more recent purchase
'f_score': int, # 1–5, higher = more frequent
'm_score': int, # 1–5, higher = more spend
'rfm_score': int, # r_score + f_score + m_score (3–15)
'total_spend': float,
'order_count': int,
'days_since_last_purchase': int,
},
...
]
}
Rows are ordered by rfm_score descending. Only customers with at least one completed transaction are included.
Scoring method: Values are sorted into quintiles using rank position. The lowest 20% of days-since-last-purchase (i.e., most recent) receives a recency score of 5. Frequency and monetary scores rank highest values highest.
get_cohort_retention()
Purpose: Builds a cohort retention matrix. Customers are grouped by the calendar month of their first completed transaction. Each cell shows the percentage of that cohort who made at least one additional purchase in that offset month.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID or Sanity ID |
Returns:
{
'cohorts': [
{
'cohort_month': str, # 'YYYY-MM'
'size': int, # Total customers in this cohort
'retention': {
0: float, # Month 0 (always 100%: acquisition month)
1: float, # % still active in month 1
2: float, # % still active in month 2
# ...
}
},
...
]
}
Cohorts are ordered chronologically. Retention percentages are rounded to one decimal place. Months with no activity for a given cohort are absent from the retention dict (not zero).
Implementation note: Uses TruncMonth with UTC timezone to prevent off-by-one cohort assignment at month boundaries.
get_repeat_purchase_rate()
Purpose: Returns the percentage of customers with 2+ completed transactions in a period, plus supporting metrics.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
store_id | str | No | Filter by store UUID or Sanity ID |
start_date | date | No | Start of period (inclusive) |
end_date | date | No | End of period (inclusive) |
Returns:
{
'repeat_rate_pct': float, # Percentage of customers with 2+ orders
'repeat_customers': int, # Count of customers with 2+ orders
'total_customers_with_orders': int, # All customers with at least 1 order in period
'avg_orders_per_repeat_customer': float, # Avg order count among repeat buyers only
}
Caching
Every method caches its result for 300 seconds (5 minutes), scoped per parameter set: two calls with the same arguments within that window return the cached result, and a call with different arguments computes fresh.