LLD: Fetch Audit Summary API
1. Overview
Returns pre-computed audit summary (TC counts, loss buckets, failed reasons) for a given month and location. Used to populate the summary cards and filter counts on the AI Audit page.
2. Endpoint
| Route | Method |
|---|---|
/audit/summary | GET |
Single route for all user types. Behaviour is determined from request.user.userType:
- AE — auto-scoped to their section (location params ignored)
- Admin (CIO / SUPER_ADMIN) — can query any location level via query params
3. Request
3.1 Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <sessionToken> |
3.2 Query Parameters
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
month | string | No | Current month (YYYY-MM) | Month to fetch summary for |
sectionCode | string | No | — | Filter at section level (admin only) |
subDivisionCode | string | No | — | Filter at sub-division level (admin only) |
divisionCode | string | No | — | Filter at division level (admin only) |
circleCode | string | No | — | Filter at circle level (admin only) |
zoneCode | string | No | — | Filter at zone level (admin only) |
Location resolution priority (most specific wins):
sectionCode > subDivisionCode > divisionCode > circleCode > zoneCode > MESCOM (no filter)
For AE users: all location params are ignored. Location is auto-resolved from the user's profile.
4. Response
4.1 Success (200)
{
"success": true,
"message": "Audit summary fetched successfully",
"data": {
"level": "SECTION",
"month": "2026-01",
"location": {
"sectionCode": "SEC-001",
"sectionName": "Section 1",
"subDivisionCode": "SD-001",
"subDivisionName": "Sub Division 1",
"divisionCode": "DIV-001",
"divisionName": "Division 1",
"circleCode": "CIR-001",
"circleName": "Circle 1",
"zoneCode": "ZN-001",
"zoneName": "Zone 1"
},
"totalTCCount": 1245,
"auditedCount": 980,
"failedCount": 265,
"lossBuckets": {
"belowMinus5": 12,
"minus5to0": 34,
"zeroTo5": 567,
"fiveTo10": 189,
"tenTo15": 90,
"fifteenTo20": 45,
"above20": 43
},
"failedReasons": {
"noMeterConstant": 50,
"withoutInstallationConsumption": 80,
"noTcConsumptionWithRemarks": 70,
"noTcConsumptionNoRemarks": 65
},
"lastAuditedOn": "2026-01-15T10:30:00.000Z"
}
}
4.2 Not Found (404)
When no summary exists for the given month + location:
{
"success": false,
"message": "No audit summary found for the given month and location"
}
4.3 Bad Request (400)
{
"success": false,
"message": ["month must be in YYYY-MM format"]
}
5. Business Logic
5.1 Determine Level and Filter
routeType = (request.user.userType === "AE") ? "ae" : "admin"
function resolveLocationFilter(routeType, requestUser, queryParams):
IF routeType is "ae":
1. Fetch user doc from `users` collection using request.user.userId
2. Extract user.location.sectionCode
3. Return { level: "SECTION", "location.sectionCode": sectionCode }
IF routeType is "admin":
IF queryParams.sectionCode → { level: "SECTION", "location.sectionCode": value }
ELSE IF queryParams.subDivisionCode → { level: "SUB_DIVISION", "location.subDivisionCode": value }
ELSE IF queryParams.divisionCode → { level: "DIVISION", "location.divisionCode": value }
ELSE IF queryParams.circleCode → { level: "CIRCLE", "location.circleCode": value }
ELSE IF queryParams.zoneCode → { level: "ZONE", "location.zoneCode": value }
ELSE → { level: "MESCOM" }
5.2 Default Month
IF month not provided:
month = current date formatted as "YYYY-MM" (e.g., "2026-03")
5.3 DB Query
Single document lookup from audit-summaries collection:
auditSummaryModel.findOne({
level: resolvedLevel,
month: month,
...locationFilter // e.g., "location.sectionCode": "SEC-001"
}).lean()
Index used: The partial unique index for the corresponding level (e.g., { level, location.sectionCode, month } for SECTION).
5.4 Projection
Return all fields except _id, __v, createdAt, updatedAt.
6. Validation Schema (AJV)
6.1 Query String Schema
{
type: "object",
properties: {
month: {
type: "string",
pattern: "^\\d{4}-(0[1-9]|1[0-2])$",
errorMessage: { pattern: "month must be in YYYY-MM format" }
},
sectionCode: { type: "string", transform: ["trim"] },
subDivisionCode: { type: "string", transform: ["trim"] },
divisionCode: { type: "string", transform: ["trim"] },
circleCode: { type: "string", transform: ["trim"] },
zoneCode: { type: "string", transform: ["trim"] }
},
additionalProperties: false
}
6.2 Response Serialization
{
"2xx": responseSerializer("2xx", {
type: "object",
properties: {
level: { type: "string" },
month: { type: "string" },
location: {
type: "object",
properties: {
sectionCode: { type: "string" },
sectionName: { type: "string" },
subDivisionCode: { type: "string" },
subDivisionName: { type: "string" },
divisionCode: { type: "string" },
divisionName: { type: "string" },
circleCode: { type: "string" },
circleName: { type: "string" },
zoneCode: { type: "string" },
zoneName: { type: "string" }
}
},
totalTCCount: { type: "number" },
auditedCount: { type: "number" },
failedCount: { type: "number" },
lossBuckets: {
type: "object",
properties: {
belowMinus5: { type: "number" },
minus5to0: { type: "number" },
zeroTo5: { type: "number" },
fiveTo10: { type: "number" },
tenTo15: { type: "number" },
fifteenTo20: { type: "number" },
above20: { type: "number" }
}
},
failedReasons: {
type: "object",
properties: {
noMeterConstant: { type: "number" },
withoutInstallationConsumption: { type: "number" },
noTcConsumptionWithRemarks: { type: "number" },
noTcConsumptionNoRemarks: { type: "number" }
}
},
lastAuditedOn: { type: ["string", "null"] }
}
}),
"4xx": responseSerializer("4xx"),
"5xx": responseSerializer("5xx")
}
7. Route Config
{
routeId: "fetchAuditSummary",
authentication: {
// No excludeFrom — requires full auth (validation + verification)
},
authorization: {
resource: "auditSummary",
action: "view",
id: "idNotApplicable"
}
}
8. File Structure
api/src/entities/audit/
audit.route.v1.js — Route definition (single route, user type from session)
controller/
fetchAuditSummary.js — Handler, schema, config exports
helpers/
resolveLocationFilter.js — Shared location resolution logic (used by both APIs)
9. Error Handling
| Scenario | Status | Error Type |
|---|---|---|
| Invalid month format | 400 | ValidationError (AJV) |
| Invalid/expired session | 401 | UnauthorizedError |
| AE user doc not found in DB | 500 | DatabaseError |
| No summary doc found | 404 | NotFoundError |
| DB connection failure | 500 | DatabaseError |
10. Performance Notes
- Single document read — no aggregation needed since summaries are pre-computed at every level.
- AE users require one extra DB call to fetch user's location from
userscollection. Consider caching user location in Redis alongside session data in a future optimization. - All queries hit unique partial indexes — O(1) lookup.