Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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

BrightProductsController is meant to expose Exposes a single read endpoint that returns the price products and / tariffs for atied to

specific one customer and service + 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 numbersystem, shaped into the "Bright" product contract. On any thrown exception logs the error and returns HTTP 500 with the exception message in the body.

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

...

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

1) GetProducts

   Route:    GET / bright/products

   Auth:     [Authorize] (controller-level)

   Params:  

  • customerId (
  • required
  • query)
  • serviceId (
  • required
  • query)
  • DateFrom (
  • required
  • query DateTime)
  •             
  • DateTo (
  • required
  • query DateTime)
  •   
  • (CancellationToken)
  • CancellationToken ct

   Body:     none

   Returns: 

  • 200 OK -> IEnumerable<BrightProduct>
  • 400
  • BadRequest   
  • BadRequest (declared
  • ; see note below
  • , never produced - see NOTE)
  • 401 Unauthorized (from auth middleware, not the action)

   What it does:

     Return the list of price products/tariffs for a given customer

     and service that are valid within the requested date range.

     It first validates the customer exists , then asks

     the repository for the matching DbProduct rows, groups them by ServiceId,

...

  • 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 ALL failures surface as HTTP 500 with a message:

     Customer validation (AccountValidator.ValidateAccountGetOutput):

    Customer not found           
  •        no customer row              -> "No customer found with Id '
  • {id}
  • <id>'."
  • More
  •        more than one
  • active customer 
  • row            -> "Unique active customer could not be identified. Id: '
  • {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 (real: g.First().ValidFrom, commented out)

    "ValidTo": null,               // Hardcoded null (real: g.First().ValidTo, commented out)

    "ServiceId": "<serviceId>",    // Echo of request serviceId param (real: g.Key/AftageNr, commented out)

    "Prices": [

      {

        "Value": 0,                // Hardcoded 0 (real: r.Value, commented out; also not selected by SQL)

        "Type": "kWh",             // Hardcoded "kWh" (real: r.Type, commented out; not selected by SQL)

        "Note": null,              // Hardcoded null (real: r.Note, commented out; not selected by SQL)

        "Name": "kWhPrice",        // Hardcoded "kWhPrice" (real: r.Name, commented out; not selected by SQL)

        "ValidFrom": "<datetime>", // Data: AFORBKONTR.FRADATO (DbProduct.ContractActiveFrom)

        "ValidTo": "<datetime>",   // Data: AFORBKONTR.TILDATO (DbProduct.ContractActiveTo); null = open-ended

        "ValidHours": [],          // Not mapped (TODO: pending DB schema)

        "ValidMonth": [],          // Not mapped (TODO: pending DB schema)

        "ValidPart": null,         // Not mapped (TODO: pending DB schema)

        "ValidFraction": null,     // Not mapped (TODO: pending DB schema)

        "SubtractMeasurement": null// Not mapped (TODO: pending DB schema)

      }

    ]

  }

]


Notes:

  - Property names above are the C# names; no [JsonPropertyName] attributes are

    present, so serialization uses the configured default casing.

  - Only TWO fields carry real data: Price.ValidFrom and Price.ValidTo (the

    contract's FRADATO/TILDATO). Everything else is hard-coded, null, or unmapped.

  - Product.ServiceId is the request input, so the output ServiceId always equals

    the requested serviceId by construction.

  - Grouping is by DbProduct.AftageNr (EANNR). Since the WHERE clause pins a

    single @EanNumber, expect a single product group in normal use.

  - The output validator rejects rows whose AftageNr differs from the requested

    serviceId; given the SQL filter (kontr.EANNR = @EanNumber, AftageNr = EANNR)

    this should not normally trigger.


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

 DATA SOURCE (SQL: ProductSql.GetActiveProducts)

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

Bound parameters (BrightProductRepository.GetActiveProductsAsync):

    @CompanyId  = Settings.CompanyId

    @CustomerId = customerId (request)

    @EanNumber  = serviceId  (request, "edielNumber")

    @StartDate  = DateFrom   (request)

    @EndDate    = DateTo     (request)


Tables / joins:

    FROM Sonlinc.AFORD ford                              (consumer)

    LEFT JOIN Sonlinc.AFORBKONTR kontr                   (contract link)

        ON ford.FIRMANR = kontr.FIRMANR AND ford.FORBNR = kontr.FORBNR

    INNER JOIN Sonlinc.BAKONTRAKT bKontr                 (actual contracts)

        ON bKontr.FIRMANR = ford.FIRMANR AND bKontr.KONTRAKT = kontr.KONTRAKT

    INNER JOIN Sonlinc.AUDEBFORS fors                    (supply type)

        ON fors.FIRMANR/INSTNR/FORBNR = ford.*

    INNER JOIN Sonlinc.audeb deb                         (debtor; electrical only)

        ON deb.* = ford.* AND deb.UDEBNR = fors.UDEBNR

    (A commented-out DGF/energikilde LEFT JOIN block is left in place for future use.)


WHERE:

    ford.FIRMANR = @CompanyId

    AND ford.KUNDENR = @CustomerId

    AND kontr.EANNR = @EanNumber

    AND ISNULL(ford.STATUS2N,0) < 1            (active consumer)

    AND ISNULL(fors.FORSYNINGSART,0) = 0       (electrical supply)

    AND (kontr.TILDATO IS NULL OR kontr.TILDATO >= @StartDate)

    AND kontr.FRADATO <= @EndDate

    ORDER BY ContractName


Selected columns -> DbProduct:

    ford.KUNDENR              -> CustomerId         (unused by mapper)

    ISNULL(kontr.kontrakt,'') -> ContractName       (unused by mapper)

    kontr.FRADATO             -> ContractActiveFrom -> Price.ValidFrom

    kontr.TILDATO             -> ContractActiveTo   -> Price.ValidTo

    ISNULL(bkontr.TYP,'')     -> ContractType       (unused by mapper)

    ISNULL(bkontr.LINTYPE,'') -> LineType           (unused by mapper)

    kontr.EANNR               -> AftageNr           (grouping key + output validation)


DbProduct columns NOT selected by the SQL (always null/default):

    Value, Type, Name, Note, PriceValidFrom, PriceValidTo

================================================================================Run claude !