LLD: Fetch Transformer Detail API
1. Overview
Returns the full detail of a single transformer centre (TC) for a given month. Used to populate the TC Detail view on the AI Audit page — including billing health breakdown, revenue parameters, meter readings, and GPS coordinates (fields excluded from the list API).
2. Endpoint
| Route | Method |
|---|---|
GET /audit/transformers/:id | GET |
Single route for all user types. Behaviour is determined from request.user.userType:
- AE — auto-scoped to their section (validates TC belongs to section)
- Admin (CIO / SUPER_ADMIN) — can query any TC
3. Request
3.1 Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <sessionToken> |
3.2 Path Parameters
| Param | Type | Required | Description |
|---|---|---|---|
id | string | Yes | The id field of the transformer document |
3.3 Query Parameters
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
month | string | No | Current month (YYYY-MM) | Month to fetch TC detail for |
4. Response
4.1 Success (200)
{
"success": true,
"message": "Transformer detail fetched successfully",
"data": {
"transformer": {
"id": "tc-uuid-001",
"number": "TC856896541556325623",
"name": "TC856896541556325623...",
"month": "2026-01",
"tcCapacity": 100,
"meterConstant": 1,
"readingDay": 15,
"readingMR": "MR-001",
"totalInstallationSanctionedLoad": 450,
"gpsCoordinates": {
"latitude": 12.8765,
"longitude": 74.8421
},
"reading": {
"initialValue": 1000,
"finalValue": 4010
},
"installationCount": 753,
"untaggedInstallationCount": 5,
"tcConsumption": 3010,
"installationConsumption": 1650,
"lossPercentage": 20,
"previousMonthLossPercentage": 15,
"previousToPreviousMonthLossPercentage": 25,
"auditStatus": "AUDITED",
"auditedByAI": true,
"remark": null,
"overloaded": true,
"billingCount": {
"unbilled": 10,
"mnr": 5,
"vacant": 3,
"zeroConsumption": 8,
"doorlock": 2,
"abnormal": 1,
"subnormal": 4,
"billCancellation": 0
},
"revenueParams": {
"arr": 500000,
"atc": 450000,
"demand": 480000,
"collection": 460000,
"billingEfficiency": 96,
"collectionEfficiency": 92
},
"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"
}
}
}
}
4.2 Not Found (404)
{
"success": false,
"message": "Transformer not found"
}
4.3 Forbidden (403)
When AE requests a TC outside their section:
{
"success": false,
"message": "TC does not belong to your assigned section"
}
5. Business Logic
5.1 Default Month
IF month not provided:
month = current date formatted as "YYYY-MM"
5.2 Fetch Transformer
const transformer = await transformerModel
.findOne({ id: tcId, month }, { _id: 0, __v: 0, createdAt: 0, updatedAt: 0 })
.lean();
if (!transformer) {
return 404 — "Transformer not found";
}
5.3 AE Section Validation
Verify the TC belongs to the user's section after fetching the transformer:
IF userType === "AE":
1. Fetch user doc from `users` collection using request.user.userId
2. Extract user.location.sectionCode
3. Verify transformer.location.sectionCode === user.location.sectionCode
4. If mismatch → 403 Forbidden
For Admin users: no section validation.
5.4 Projection
Return all fields except internal Mongoose fields:
const projection = {
_id: 0,
__v: 0,
createdAt: 0,
updatedAt: 0,
activatedOn: 0,
deletedOn: 0,
auditedOn: 0,
};
Fields returned:
- Identity:
id,number,name,month - TC properties:
tcCapacity,meterConstant,readingDay,readingMR,totalInstallationSanctionedLoad - GPS:
gpsCoordinates(latitude,longitude) — Note: DB stores GeoJSON format ({ type: "Point", coordinates: [longitude, latitude] }). The API transforms this to{ latitude, longitude }in the response. - Meter readings:
reading(initialValue,finalValue) - Counts:
installationCount,untaggedInstallationCount - Audit data:
tcConsumption,installationConsumption,lossPercentage,previousMonthLossPercentage,previousToPreviousMonthLossPercentage - Status:
auditStatus,auditedByAI,remark,overloaded - Billing breakdown:
billingCount(all 8 categories) - Revenue:
revenueParams(all 6 fields) - Location: all 10 fields
6. Validation Schema (AJV)
6.1 Path Parameters Schema
{
type: "object",
properties: {
id: {
type: "string",
transform: ["trim"],
}
},
required: ["id"]
}
6.2 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" }
}
},
additionalProperties: false
}
6.3 Response Serialization
{
"2xx": responseSerializer("2xx", {
type: "object",
properties: {
transformer: {
type: "object",
properties: {
id: { type: "string" },
number: { type: "string" },
name: { type: "string" },
month: { type: "string" },
tcCapacity: { type: ["number", "null"] },
meterConstant: { type: ["number", "null"] },
readingDay: { type: ["number", "null"] },
readingMR: { type: ["string", "null"] },
totalInstallationSanctionedLoad: { type: ["number", "null"] },
gpsCoordinates: {
type: "object",
properties: {
latitude: { type: ["number", "null"] },
longitude: { type: ["number", "null"] }
}
},
reading: {
type: "object",
properties: {
initialValue: { type: ["number", "null"] },
finalValue: { type: ["number", "null"] }
}
},
installationCount: { type: ["number", "null"] },
untaggedInstallationCount: { type: ["number", "null"] },
tcConsumption: { type: ["number", "null"] },
installationConsumption: { type: ["number", "null"] },
lossPercentage: { type: ["number", "null"] },
previousMonthLossPercentage: { type: ["number", "null"] },
previousToPreviousMonthLossPercentage: { type: ["number", "null"] },
auditStatus: { type: ["string", "null"] },
auditedByAI: { type: "boolean" },
remark: { type: ["string", "null"] },
overloaded: { type: "boolean" },
billingCount: {
type: "object",
properties: {
unbilled: { type: "number" },
mnr: { type: "number" },
vacant: { type: "number" },
zeroConsumption: { type: "number" },
doorlock: { type: "number" },
abnormal: { type: "number" },
subnormal: { type: "number" },
billCancellation: { type: "number" }
}
},
revenueParams: {
type: "object",
properties: {
arr: { type: ["number", "null"] },
atc: { type: ["number", "null"] },
demand: { type: ["number", "null"] },
collection: { type: ["number", "null"] },
billingEfficiency: { type: ["number", "null"] },
collectionEfficiency: { type: ["number", "null"] }
}
},
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" }
}
}
}
}
}
}),
"4xx": responseSerializer("4xx"),
"5xx": responseSerializer("5xx")
}
7. Route Config
{
routeId: "fetchTransformerDetail",
authentication: {
// No excludeFrom — requires full auth
},
authorization: {
resource: "transformer",
action: "view",
id: "idNotApplicable"
}
}
8. File Structure
api/src/entities/audit/
audit.route.v1.js — Route definitions
controller/
fetchAuditSummary.js — Existing
fetchTransformers.js — Existing
fetchInstallations.js — Existing
fetchTransformerDetail.js — Handler, schema, config exports
helpers/
resolveLocationFilter.js — Existing (reused for AE section lookup)
9. Error Handling
| Scenario | Status | Error Type |
|---|---|---|
| Invalid month format | 400 | ValidationError (AJV) |
| Invalid/expired session | 401 | UnauthorizedError |
| AE accessing TC outside their section | 403 | ForbiddenError |
TC not found for given id + month | 404 | NotFoundError |
| AE user doc not found | 500 | DatabaseError |
| DB connection failure | 500 | DatabaseError |
10. Performance Notes
- Single
findOnequery — no pagination, no count. This is the simplest handler in the audit module. - Index used:
{ id: 1, month: 1 }— not a defined index. If querying byid+monthis slow, add a compound index{ id: 1, month: 1 }. - AE users require one extra DB call (user lookup via
resolveLocationFilter) — same as other audit APIs. - No aggregation needed — all fields are pre-computed and stored on the transformer document.