================================================================================

 BrightLocationsController - Summary & Response Structures

 File: SonWinCommonAPI/Controllers/BrightLocationsController.cs

================================================================================


--------------------------------------------------------------------------------

 WHAT THE CONTROLLER DOES

--------------------------------------------------------------------------------

BrightLocationsController exposes a customer's locations (installations /

"installationer") and the supply-type services attached to them from the

SonWin/Sonlinc system, shaped into the "Bright" location contract. It requires

authorization ([Authorize]) and, on any thrown exception, goes through

CustomBase.HandleError which logs the error and returns HTTP 500 with the

exception message in the body.

--------------------------------------------------------------------------------

 METHODS

--------------------------------------------------------------------------------


1) GetLocations

   Route:    GET /bright/locations

   Params:  

   Returns: 

   What it does:

     Returns the list of active locations for a customer, each with its
     electricity service(s).

      It first validates the customer exists and validates input
     (customerId required; limit, if supplied, must be 1-1000, default 100).
     It then loads location headers, loads the electricity services for those
     installations, attaches each service's current supply start/end dates, and
     maps everything to the BrightLocation response.

   Validation that affects the result (all surface as HTTP 500 with a message):

       (Every returned service MUST have a supply start date or the whole call fails. The start/end dates come from the SWMarket supply-range query, which only returns the CURRENTLY active supply: FromDate <= today and ToDate is null or in the future.)


   NOTE on status codes: Every error (bad input, not found, missing dates,

   DB failure) currently comes back as HTTP 500 with the message in the body.

--------------------------------------------------------------------------------

 RESPONSE STRUCTURE (BrightLocation)

--------------------------------------------------------------------------------

Returned as a JSON ARRAY (IEnumerable<BrightLocation>), one element per active

installation for the customer.

[
  {
    "Id":        <value>     // Data: SonWin installation number (AINSD.INSTNR), as string
    "Address":   <value>     // Data: street + house number (AINSD.ADRESSE + AINSD.HUSNUMMER)
    "ZipCode":   <value>     // Data: postal code (AINSD.POSTNUM)
    "City":      <value>     // Data: city name (IPOST.NAVN, falling back to AINSD.STED; empty string if both null)
    "Properties": null       // Hardcoded null: BrightLocationProperties is never populated by the mapper
                             //   (PropertyType, NumberOfResidents, Size, HouseAge, Cars[], Heating,
                             //    HeatingSecondary, Gadgets[] are all unused today)
    "PriceZone": <value>     // Derived: from ZipCode -> "DK1" if zip >= 6000, "DK2" if zip < 6000,
                             //   null if the zip code cannot be parsed
    "Services": [            // Data: the electricity service(s) for this installation (empty array if none)
      {
        "Id":                    <value>  // Data: metering point id / EAN (IndividualMPoints.MeteringPointId, aka AftageNr)
        "Type":                  <value>  // Derived: mapped from SonWin forsyningsart. For electricity this is
                                          //   always "consumption_trade". (Other types map to water/heating/
                                          //   cooling/tv/broadband/waste/gas_trade, but only El is queried today.)
        "Status":                "Active" // Hardcoded: BrightServiceStatus.Active.ToString().
                                          //   NOTE: this emits the enum NAME "Active" (capital A), NOT the
                                          //   display value "active". TODO in code: derive real status from SonWin.
        "MeasurementResolution": <value>  // Derived from forsyningsart: electricity => "hour"
                                          //   (broadband => "none"; other types default to "none" - TODO).
        "MeasurementId":         <value>  // Data: metering point id (same value as Service.Id / AftageNr)
        "DeliveryPointId":       null     // Hardcoded null: never set by the mapper
        "CostResolution":        null     // Hardcoded null: never set by the mapper
        "CostId":                null     // Hardcoded null: never set by the mapper
        "Properties":            null     // Hardcoded null: BrightServiceProperties (ShowCost,
                                          //   RealtimeMeasurements) is never populated
        "StartDate":             <value>  // Data: current supply start date (SWMarket MeteringPointRoleOccurrence.FromDate),
                                          //   as DateTimeOffset (UTC/zero offset). Required - null fails validation.
        "EndDate":               <value>  // Data: current supply end date (SWMarket ...ToDate), as DateTimeOffset.
                                          //   OMITTED from the JSON entirely when null (JsonIgnore WhenWritingNull);
                                          //   null/absent means the supply has no scheduled end (still active).
      }
    ],
    "Longitude": null        // Hardcoded null
    "Latitude":  null        // Hardcoded null
  }
]

Notes:

  - Properties, Longitude and Latitude on the location are always null/absent.

  - On a service, DeliveryPointId, CostResolution, CostId and Properties are

    always null; Id and MeasurementId always carry the same metering point id.

  - Service.Status is the only hardcoded non-null literal and is currently the

    enum NAME "Active" rather than the lowercase display string "active".

  - EndDate is the only field deliberately omitted (not null) when it has no

    value, due to [JsonIgnore(WhenWritingNull)].

  - A location can have an empty Services array, but if a service IS present it

    must have a StartDate or the whole request fails validation.