diff --git a/.changeset/getag-fees-details-without-inputs.md b/.changeset/getag-fees-details-without-inputs.md new file mode 100644 index 0000000..1e399b9 --- /dev/null +++ b/.changeset/getag-fees-details-without-inputs.md @@ -0,0 +1,16 @@ +--- +'@epilot/pricing': patch +--- + +Render the GetAG fees breakdown when `external_fees_metadata.inputs` is absent. + +`inputs` is annotated client-side by the Journey renderer after the GetAG compute +call — the pricing API never returns it — so price items coming from carts submitted +through the public API carry fee metadata without it. `processExternalFeesDetails` +dereferenced `inputs.type` and `inputs.consumptionHT/NT` unguarded and threw +`Cannot read properties of undefined`, taking down the whole order table variable for +those items. + +The reads are now optional-chained and `ExternalFeesMetadata['inputs']` is typed as +optional to match. Consumption-based yearly amounts already fall back to `-` when the +consumption is unknown, so the breakdown renders with everything except those figures. diff --git a/src/variables/getag/network-fees-details.ts b/src/variables/getag/network-fees-details.ts index 13e65e8..8ce3ca9 100644 --- a/src/variables/getag/network-fees-details.ts +++ b/src/variables/getag/network-fees-details.ts @@ -13,7 +13,7 @@ export const processNetworkOperatingFeesDetails = ( tax?: Tax | TaxItem, variableUnit?: string, ) => { - const type = externalFeesMetadata.inputs.type || 'power'; + const type = externalFeesMetadata.inputs?.type || 'power'; if (!result.groups) { result.groups = {}; diff --git a/src/variables/getag/other-fees-details.ts b/src/variables/getag/other-fees-details.ts index 1e74abe..b114023 100644 --- a/src/variables/getag/other-fees-details.ts +++ b/src/variables/getag/other-fees-details.ts @@ -15,7 +15,7 @@ export const processOtherFeesDetails = ( tax?: Tax | TaxItem, variableUnit?: string, ) => { - const type = externalFeesMetadata.inputs.type || 'power'; + const type = externalFeesMetadata.inputs?.type || 'power'; if (!result.groups) { result.groups = {}; diff --git a/src/variables/getag/utils.test.ts b/src/variables/getag/utils.test.ts index 2650ccd..dc9014d 100644 --- a/src/variables/getag/utils.test.ts +++ b/src/variables/getag/utils.test.ts @@ -214,6 +214,82 @@ describe('getMarkupDetailsFee', () => { }, }; + describe('when the metadata carries no inputs', () => { + /** + * `inputs` is annotated by the Journey renderer after the GetAG compute call, + * so it is absent on carts submitted through the public API. The breakdown must + * still render — only the consumption-based yearly amounts are unknown. + */ + const metadataWithoutInputs = { + billing_period: 'monthly', + breakdown: { + static: {}, + variable: {}, + variable_ht: {}, + }, + } as ExternalFeesMetadata; + + it('should render the work price markup without yearly amounts', () => { + const priceGetAgConfig: PriceGetAg = { + category: 'power', + markup_amount: 10, + markup_amount_decimal: '0.10', + markup_amount_gross_decimal: '0.10', + unit_amount_gross: 0, + unit_amount_net: 0, + }; + + const result = getMarkupDetailsFee({ + ...defaultParams, + priceGetAgConfig, + externalFeesMetadata: metadataWithoutInputs, + options: { type: 'work_price', tariffType: 'HT' as TariffTypeGetAg }, + }); + + expect(result).toEqual({ + label: 'Work Price Markup', + amount: '10.00 cents/kWh', + amount_decimal: '0.10', + amount_yearly_decimal: '0', + amount_yearly: '-', + }); + }); + + it('should render the procurement markup without yearly amounts', () => { + const priceGetAgConfig: PriceGetAg = { + category: 'power', + markup_amount: 10, + markup_amount_decimal: '0.10', + markup_amount_gross_decimal: '0.10', + unit_amount_gross: 0, + unit_amount_net: 0, + additional_markups_enabled: true, + additional_markups: { + procurement: { + amount: 5, + amount_decimal: '0.05', + amount_gross_decimal: '0.05', + }, + }, + }; + + const result = getMarkupDetailsFee({ + ...defaultParams, + priceGetAgConfig, + externalFeesMetadata: metadataWithoutInputs, + options: { type: 'additional_markup', tariffType: 'NT' as TariffTypeGetAg, key: 'procurement' }, + }); + + expect(result).toEqual({ + label: 'Procurement Markup', + amount: '5.00 cents/kWh', + amount_decimal: '0.05', + amount_yearly_decimal: '0', + amount_yearly: '-', + }); + }); + }); + describe('when priceGetAgConfig is undefined', () => { it('should return undefined', () => { const result = getMarkupDetailsFee({ diff --git a/src/variables/getag/utils.ts b/src/variables/getag/utils.ts index dc7b0a8..e982472 100644 --- a/src/variables/getag/utils.ts +++ b/src/variables/getag/utils.ts @@ -198,8 +198,8 @@ const getMarkupDetailsFee = ({ ? getConsumptionBasedAmounts( procurementMarkup?.amount_gross_decimal, options.tariffType === 'HT' - ? externalFeesMetadata.inputs.consumptionHT - : externalFeesMetadata.inputs.consumptionNT, + ? externalFeesMetadata.inputs?.consumptionHT + : externalFeesMetadata.inputs?.consumptionNT, billingPeriod, ).yearlyAmountDecimal : undefined; @@ -266,8 +266,8 @@ const getMarkupDetailsFee = ({ ? getConsumptionBasedAmounts( priceGetAgConfig?.markup_amount_gross_decimal, options.tariffType === 'HT' - ? externalFeesMetadata.inputs.consumptionHT - : externalFeesMetadata.inputs.consumptionNT, + ? externalFeesMetadata.inputs?.consumptionHT + : externalFeesMetadata.inputs?.consumptionNT, billingPeriod, currency, ).yearlyAmountDecimal diff --git a/src/variables/types.ts b/src/variables/types.ts index 445204e..7f70f17 100644 --- a/src/variables/types.ts +++ b/src/variables/types.ts @@ -23,7 +23,12 @@ export type GetTieredUnitAmountOptions = { export type ExternalFeesMetadata = { billing_period: string; - inputs: { + /** + * Client-side annotation of the GetAG compute request. The pricing API never + * returns it, so it is absent whenever the price item was not built by the + * Journey renderer (e.g. carts submitted through the public API). + */ + inputs?: { consumptionHT?: number; consumptionNT?: number; type?: 'power' | 'gas';