Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ coverage
.idea
.yalc
yalc.lock
.epilot-docs
3 changes: 3 additions & 0 deletions src/__tests__/fixtures/price-getag.samples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ export const compositePriceGetAG: PriceItemDto = {
tax: [tax19percent],
get_ag: {
category: 'power',
type: 'work_price',
markup_amount: 10,
markup_amount_decimal: '0.10',
},
Expand Down Expand Up @@ -390,6 +391,7 @@ export const compositePriceTieredFlatFeeGetAG: PriceItemDto = {
tax: [tax19percent],
get_ag: {
category: 'power',
type: 'work_price',
markup_amount: 10,
markup_amount_decimal: '0.10',
},
Expand Down Expand Up @@ -510,6 +512,7 @@ export const compositePriceGetAGWithZeroInputMapping: PriceItemDto = {
tax: [tax19percent],
get_ag: {
category: 'power',
type: 'work_price',
markup_amount: 10,
markup_amount_decimal: '0.10',
},
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/fixtures/price.samples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,7 @@ export const compositePriceWithTaxExclusiveComponent: CompositePriceItemDto = {
_updated_at: '2023-02-08T11:01:50.179Z',
variable_price: true,
unit: 'kwh',
pricing_model: 'per_unit',
tax: [
[
{
Expand Down Expand Up @@ -3205,6 +3206,7 @@ export const compositePriceWithNumberInputEqualsToZero: CompositePriceItemDto =
_updated_at: '2023-02-08T11:01:50.179Z',
variable_price: true,
unit: 'kwh',
pricing_model: 'per_unit',
tax: [
[
{
Expand Down
3 changes: 2 additions & 1 deletion src/computations/compute-price-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { DEFAULT_CURRENCY } from '../money/constants';
import { PricingModel } from '../prices/constants';
import { convertPriceItemWithCouponAppliedToPriceItemDto } from '../prices/convert-precision';
import { getPriceTax } from '../prices/get-price-tax';
import { isVariablePrice } from '../prices/is-variable-price';
import { mapToProductSnapshot, mapToPriceSnapshot } from '../prices/map-to-snapshots';
import { normalizePriceMappingInput } from '../prices/mapping';
import type { PriceItemsTotals } from '../prices/types';
Expand Down Expand Up @@ -51,7 +52,7 @@ const computeExternalFee = (
export const computeQuantities = (price: Price | undefined, quantity: number, priceMapping?: PriceInputMapping) => {
const safeQuantity = getSafeQuantity(quantity);

if (!price?.variable_price) {
if (!price || !isVariablePrice(price)) {
return {
safeQuantity,
unitAmountMultiplier: safeQuantity,
Expand Down
2 changes: 2 additions & 0 deletions src/exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const expectedNamedExports = [
'extractGetAgConfig',
'getAmountWithTax',
'getTaxValue',
'isVariablePrice',
'isVariablePriceItem',
];

/**
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { computeQuantities } from './computations/compute-price-item';
export { extractPricingEntitiesBySlug, extractCouponsFromItem } from './prices/extract-pricing-entities-by-slug';
export { computeAggregatedAndPriceTotals } from './computations/compute-totals';
export { PricingModel } from './prices/constants';
export { isVariablePrice, isVariablePriceItem } from './prices/is-variable-price';
export { getDisplayTierByQuantity, getDisplayTiersByQuantity, getTierDescription } from './tiers/utils';
export { computeCumulativeValue } from './tiers/compute-cumulative-value';
export type {
Expand Down
91 changes: 91 additions & 0 deletions src/prices/__tests__/is-variable-price.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type { CompositePrice, CompositePriceItem, Price, PriceItem } from '../../shared/types';
import { isVariablePrice, isVariablePriceItem } from '../is-variable-price';

const makePrice = (overrides: Partial<Price> = {}): Price =>
({
pricing_model: 'per_unit',
...overrides,
}) as Price;

describe('isVariablePrice', () => {
it('returns true for per_unit with variable_price true', () => {
expect(isVariablePrice(makePrice({ pricing_model: 'per_unit', variable_price: true }))).toBe(true);
});

it('returns false for per_unit with variable_price false', () => {
expect(isVariablePrice(makePrice({ pricing_model: 'per_unit', variable_price: false }))).toBe(false);
});

it('returns false for per_unit without variable_price', () => {
expect(isVariablePrice(makePrice({ pricing_model: 'per_unit' }))).toBe(false);
});

it.each(['tiered_volume', 'tiered_graduated', 'tiered_flatfee'] as const)(
'returns true for tiered model: %s',
(pricing_model) => {
expect(isVariablePrice(makePrice({ pricing_model }))).toBe(true);
},
);

it('returns true for dynamic_tariff', () => {
expect(isVariablePrice(makePrice({ pricing_model: 'dynamic_tariff' }))).toBe(true);
});

it('returns true for external_getag with work_price', () => {
expect(isVariablePrice(makePrice({ pricing_model: 'external_getag', get_ag: { type: 'work_price' } }))).toBe(true);
});

it('returns true for external_getag with base_price and tiered_flatfee markup', () => {
expect(
isVariablePrice(
makePrice({
pricing_model: 'external_getag',
get_ag: { type: 'base_price', markup_pricing_model: 'tiered_flatfee' },
}),
),
).toBe(true);
});

it('returns false for external_getag with base_price and non-tiered markup', () => {
expect(isVariablePrice(makePrice({ pricing_model: 'external_getag', get_ag: { type: 'base_price' } }))).toBe(false);
});

it('returns false for external_getag without get_ag', () => {
expect(isVariablePrice(makePrice({ pricing_model: 'external_getag' }))).toBe(false);
});

it('returns false for CompositePrice', () => {
const composite = { price_components: [makePrice()] } as unknown as CompositePrice;

expect(isVariablePrice(composite)).toBe(false);
});
});

describe('isVariablePriceItem', () => {
it('returns true when _price is variable', () => {
const item = { _price: makePrice({ pricing_model: 'tiered_volume' }) } as PriceItem;

expect(isVariablePriceItem(item)).toBe(true);
});

it('returns false when _price is not variable', () => {
const item = { _price: makePrice({ pricing_model: 'per_unit', variable_price: false }) } as PriceItem;

expect(isVariablePriceItem(item)).toBe(false);
});

it('returns false for CompositePriceItem', () => {
const item = {
is_composite_price: true,
_price: { is_composite_price: true, price_components: [] },
} as unknown as CompositePriceItem;

expect(isVariablePriceItem(item)).toBe(false);
});

it('returns false when _price is missing', () => {
const item = {} as PriceItem;

expect(isVariablePriceItem(item)).toBe(false);
});
});
36 changes: 36 additions & 0 deletions src/prices/is-variable-price.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { CompositePrice, CompositePriceItem, Price, PriceItem } from '../shared/types';
import { MarkupPricingModel, PricingModel, TypeGetAg } from './constants';

const isTieredPrice = (price: Price): boolean => {
return (
price.pricing_model === PricingModel.tieredVolume ||
price.pricing_model === PricingModel.tieredGraduated ||
price.pricing_model === PricingModel.tieredFlatFee
);
};

export const isVariablePrice = (price: Price | CompositePrice): boolean => {
if (price.is_composite_price) {
return false;
}

const p = price as Price;

if (isTieredPrice(p)) return true;
if (p.pricing_model === PricingModel.dynamicTariff) return true;
if (p.pricing_model === PricingModel.externalGetAG) {
if (p.get_ag?.type === TypeGetAg.workPrice) return true;
if (p.get_ag?.type === TypeGetAg.basePrice && p.get_ag?.markup_pricing_model === MarkupPricingModel.tieredFlatFee) {
return true;
}
}
if (p.pricing_model === PricingModel.perUnit && p.variable_price) return true;

return false;
};

export const isVariablePriceItem = (priceItem: PriceItem | CompositePriceItem): boolean => {
if (!priceItem._price) return false;

return isVariablePrice(priceItem._price);
};
2 changes: 2 additions & 0 deletions src/variables/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,15 @@ describe('getQuantity', () => {
const baseVariableItem = {
price_id: 'price_id',
_price: {
pricing_model: 'per_unit',
variable_price: true,
},
};

const baseNotVariableItem = {
price_id: 'price_id',
_price: {
pricing_model: 'per_unit',
variable_price: false,
},
};
Expand Down
6 changes: 4 additions & 2 deletions src/variables/process-order-table-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Currency } from 'dinero.js';
import { formatPriceUnit } from '../money/formatters';
import { PricingModel } from '../prices/constants';
import { getRecurrencesWithEstimatedPrices } from '../prices/get-recurrences-with-estimated-prices';
import { isVariablePrice, isVariablePriceItem } from '../prices/is-variable-price';
import { isCompositePrice } from '../prices/utils';
import { isTruthy } from '../shared/is-truthy';
import type {
Expand Down Expand Up @@ -414,7 +415,7 @@ export const processOrderTableData = (data: any, i18n: I18n) => {
/**
* Process Quantity data
*/
if (item._price?.variable_price && !isCoupon) {
if (isVariablePriceItem(item) && !isCoupon) {
const itemPriceMapping = (item.parent_item ?? item).price_mappings?.find(
(mapping: any) => mapping.price_id === item.price_id,
);
Expand All @@ -428,7 +429,8 @@ export const processOrderTableData = (data: any, i18n: I18n) => {
if (item.external_fees_metadata) {
const unit =
isCompositePrice(item) && Array.isArray(item._price?.price_components)
? item._price?.price_components.find((component: Price) => component.variable_price && component.unit)?.unit
? item._price?.price_components.find((component: Price) => isVariablePrice(component) && component.unit)
?.unit
: item._price?.unit;

item.external_fees_details = processExternalFeesDetails(
Expand Down
7 changes: 4 additions & 3 deletions src/variables/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { formatAmount, formatAmountFromString, formatPriceUnit } from '../money/formatters';
import { PricingModel } from '../prices/constants';
import { isVariablePriceItem } from '../prices/is-variable-price';
import { isCompositePrice } from '../prices/utils';
import { isTruthy } from '../shared/is-truthy';
import type {
Expand Down Expand Up @@ -469,7 +470,7 @@ export const getFormattedTieredDetails = (
};

export const getQuantity = (item: PriceItem, parentItem?: PriceItem) => {
if (!parentItem && item._price?.variable_price) {
if (!parentItem && isVariablePriceItem(item)) {
const itemPriceMapping = item.price_mappings?.find((mapping) => mapping.price_id === item.price_id);
const quantity =
typeof itemPriceMapping?.value === 'number'
Expand All @@ -478,10 +479,10 @@ export const getQuantity = (item: PriceItem, parentItem?: PriceItem) => {

return item.quantity == 1 ? quantity : `${item.quantity} x ${quantity}`;
}
if (parentItem && !item._price?.variable_price) {
if (parentItem && !isVariablePriceItem(item)) {
return parentItem.quantity == 1 ? `${item.quantity}` : `${parentItem.quantity} x ${item.quantity}`;
}
if (parentItem && item._price?.variable_price) {
if (parentItem && isVariablePriceItem(item)) {
const itemPriceMapping = parentItem.price_mappings?.find((mapping) => mapping.price_id === item.price_id);

const quantity =
Expand Down