Skip to main content

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

RouteMethod
/audit/summaryGET

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

HeaderRequiredDescription
AuthorizationYesBearer <sessionToken>

3.2 Query Parameters

ParamTypeRequiredDefaultDescription
monthstringNoCurrent month (YYYY-MM)Month to fetch summary for
sectionCodestringNoFilter at section level (admin only)
subDivisionCodestringNoFilter at sub-division level (admin only)
divisionCodestringNoFilter at division level (admin only)
circleCodestringNoFilter at circle level (admin only)
zoneCodestringNoFilter 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

ScenarioStatusError Type
Invalid month format400ValidationError (AJV)
Invalid/expired session401UnauthorizedError
AE user doc not found in DB500DatabaseError
No summary doc found404NotFoundError
DB connection failure500DatabaseError

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 users collection. Consider caching user location in Redis alongside session data in a future optimization.
  • All queries hit unique partial indexes — O(1) lookup.