================================================================================
BrightProductsController - Summary & Response Structures
File: SonWinCommonAPI/Controllers/BrightProductsController.cs
================================================================================
--------------------------------------------------------------------------------
WHAT THE CONTROLLER DOES
--------------------------------------------------------------------------------
Exposes a single read endpoint that returns the price products / tariffs tied to
one customer + one service (metering point / EAN number) within a date range.
The data is read from the SonWin (Sonlinc) contract tables;
The query selects the contract "lines" for the requested EAN number.
--------------------------------------------------------------------------------
METHODS
--------------------------------------------------------------------------------
1) GetProducts
Route: GET bright/products
Auth: [Authorize] (controller-level)
Params:
- customerId (query)
- serviceId (query)
- DateFrom (query DateTime)
- DateTo (query DateTime)
- CancellationToken ct
Body: none
Returns:
- 200 OK -> IEnumerable<BrightProduct>
- 400 BadRequest (declared, never produced - see NOTE)
- 401 Unauthorized (from auth middleware, not the action)
What it does:
- Validates the customer
- validates the inputs
- queries active contracts for the customer + EAN within the date range
- validates that every returned row belongs to the requested serviceId
- Groups by AftageNr
- Maps each group to a BrightProduct with a list of BrightPrice lines.
Validation that affects the result (ALL failures surface as HTTP 500:
Customer validation (AccountValidator.ValidateAccountGetOutput):
- no customer row -> "No customer found with Id '<id>'."
- more than one row -> "Unique active customer could not be identified. Id: '<id>'."
- row has empty CustomerId -> "The customer returned did not have any CustomerId."
- returned id != requested -> "Returned customer '<x>' does not match requested '<id>'."
Input validation (ProductsValidator.ValidateGetProductsAsyncInput):
- customerId blank -> "Missing parameter 'CustomerId'."
- serviceId blank -> "Missing parameter 'ServiceId'."
- serviceId non-digit -> "Parameter 'ServiceId' must contain digits only."
- DateFrom == default -> "Missing parameter 'DateFrom'."
- DateTo == default -> "Missing parameter 'DateTo'."
- DateFrom >= DateTo -> "'DateFrom' must be before 'DateTo'."
Output validation (ProductsValidator.ValidateGetProductsAsyncOutput):
- Any row.AftageNr != serviceId -> "Products belong to wrong ServiceId(s):
- <ids>. Expected ServiceId: <id>.
- CustomerId <id>, daterange: <from>-<to>"
--------------------------------------------------------------------------------
RESPONSE STRUCTURE (IEnumerable<BrightProduct>)
--------------------------------------------------------------------------------
Top level is a JSON array of BrightProduct. One element per distinct AftageNr
(EAN). Because the query filters on a single @EanNumber, in practice there is
one group, and each contract row becomes one entry in that group's Prices list.
[
{
"ValidFrom": null, // Hardcoded null
"ValidTo": null, // Hardcoded null
"ServiceId": "<serviceId>", // Echo of request serviceId param (MeteringPointId)
"Prices": [
{
"Value": 0, // Hardcoded 0
"Type": "kWh", // Hardcoded "kWh"
"Note": null, // Hardcoded null
"Name": "kWhPrice", // Hardcoded "kWhPrice"
"ValidFrom": "<datetime>", // Data: AFORBKONTR.FRADATO (DbProduct.ContractActiveFrom)
"ValidTo": "<datetime>", // Data: AFORBKONTR.TILDATO (DbProduct.ContractActiveTo); null = open-ended
"ValidHours": [], // Not mapped
"ValidMonth": [], // Not mapped
"ValidPart": null, // Not mapped
"ValidFraction": null, // Not mapped
"SubtractMeasurement": null// Not mapped
}
]
}
]