Procurement - Analytics Service
Location: api/nextango/apps/procurement/analytics_service.py
Last Updated: 2026-07-09
Overview
Provides aggregated procurement and supplier metrics for the analytics dashboard. The service reads from the replica database (when USE_REPLICA_DATABASE=True) and caches results with a 5-minute TTL to reduce load on reporting queries. Two of the three methods are called by ViewSet actions bound at /purchase-orders/analytics/summary/ and /suppliers/analytics/performance/.
Class: ProcurementAnalyticsService
Aggregation methods for procurement and supplier analytics. All methods are @staticmethod and accept optional date range parameters.
Method Reference
get_procurement_summary()
Purpose: Returns headline KPIs for all purchase orders in the system, optionally filtered to a date range.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
start_date | date | No | Filter POs created on or after this date |
end_date | date | No | Filter POs created on or before this date |
Returns:
{
'total_purchase_orders': int, # Total PO count in period
'total_po_value': Decimal, # Sum of total_cost across all POs
'by_status': [ # Per-status breakdown
{
'status': str, # e.g. 'Pending', 'Received'
'count': int,
'value': Decimal,
},
...
],
'open_po_count': int, # POs in Pending, Submitted, Partially Received
'open_po_value': Decimal, # Sum of total_cost for open POs
}
Example:
from nextango.apps.procurement.analytics_service import ProcurementAnalyticsService
from datetime import date
result = ProcurementAnalyticsService.get_procurement_summary(
start_date=date(2026, 1, 1),
end_date=date(2026, 1, 31),
)
# result['total_purchase_orders'] -> 42
# result['by_status'] -> [{'status': 'Received', 'count': 30, 'value': Decimal('15000.00')}, ...]
get_supplier_performance()
Purpose: Returns a ranked list of suppliers by total PO value, with order counts and on-time receipt metrics.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
start_date | date | No | Filter POs created on or after this date |
end_date | date | No | Filter POs created on or before this date |
Returns:
{
'data': [
{
'supplier_name': str, # Supplier.name
'supplier_code': str, # Supplier.supplier_code
'stated_lead_time': int, # Supplier.lead_time_days
'total_orders': int, # PO count for this supplier
'total_value': Decimal, # Sum of PO total_cost
'received_count': int, # POs with status='Received'
'active_pos': int, # POs in open statuses
},
...
]
}
Rows are ordered by total_value descending (highest-spend supplier first).
Example:
from nextango.apps.procurement.analytics_service import ProcurementAnalyticsService
result = ProcurementAnalyticsService.get_supplier_performance()
for supplier in result['data']:
print(f"{supplier['supplier_name']}: £{supplier['total_value']}")
get_supplier_lead_time_performance()
Purpose: Returns per-supplier on-time receipt rate and lead time variance against the supplier's stated lead_time_days. Not currently bound to a view or URL; call it directly from Python.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
start_date | date | No | Filter POs created on or after this date |
end_date | date | No | Filter POs created on or before this date |
Only purchase orders with status='Received' and a non-null received_date are included.
Returns:
{
'data': [
{
'supplier_name': str, # Supplier.name
'supplier_code': str, # Supplier.supplier_code
'stated_lead_time': int, # Supplier.lead_time_days
'received_count': int, # Received POs for this supplier in range
'on_time_count': int, # Received POs with received_date <= expected_date
'avg_actual_lead_days': float, # Average days from created_at to received_date
'lead_time_variance_days': float, # avg_actual_lead_days minus stated_lead_time
'on_time_rate_pct': float, # on_time_count / received_count * 100
},
...
]
}
Rows are ordered by received_count descending.
Caching
All three methods cache their results for 300 seconds (5 minutes), scoped per method and parameter set. Results refresh automatically when the window expires.