Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/getag-fees-details-without-inputs.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion src/variables/getag/network-fees-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down
2 changes: 1 addition & 1 deletion src/variables/getag/other-fees-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down
76 changes: 76 additions & 0 deletions src/variables/getag/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
8 changes: 4 additions & 4 deletions src/variables/getag/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion src/variables/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Loading