added utilities

This commit is contained in:
John Yeo
2026-02-06 10:16:27 -08:00
parent 52cd454f9a
commit acaf05d92a
3 changed files with 125 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
import { InternalError } from "@api/errors/base/InternalError.js";
import type { FullCusProduct } from "../../../models/cusProductModels/cusProductModels";
import type { Feature } from "../../../models/featureModels/featureModels";
import type { Price } from "../../../models/productModels/priceModels/priceModels";
import {
isFixedPrice,
isPrepaidPrice,
priceOnFeature,
} from "../../productUtils/priceUtils/classifyPriceUtils";
import { cusProductToPrices } from "../convertCusProduct";
// Overload: errorOnNotFound = true → guaranteed Price
export function customerProductToBasePrice(params: {
customerProduct: FullCusProduct;
errorOnNotFound: true;
}): Price;
// Overload: errorOnNotFound = false/undefined → Price | undefined
export function customerProductToBasePrice(params: {
customerProduct: FullCusProduct;
errorOnNotFound?: false;
}): Price | undefined;
// Implementation
export function customerProductToBasePrice({
customerProduct,
errorOnNotFound,
}: {
customerProduct: FullCusProduct;
errorOnNotFound?: boolean;
}): Price | undefined {
const prices = cusProductToPrices({ cusProduct: customerProduct });
const result = prices.find((p) => isFixedPrice(p));
if (errorOnNotFound && !result) {
throw new InternalError({
message: `Base price not found for customer product ${customerProduct.id}`,
});
}
return result;
}
// Overload: errorOnNotFound = true → guaranteed Price
export function customerProductToPrepaidPrice(params: {
customerProduct: FullCusProduct;
feature?: Feature;
errorOnNotFound: true;
}): Price;
// Overload: errorOnNotFound = false/undefined → Price | undefined
export function customerProductToPrepaidPrice(params: {
customerProduct: FullCusProduct;
feature?: Feature;
errorOnNotFound?: false;
}): Price | undefined;
// Implementation
export function customerProductToPrepaidPrice({
customerProduct,
feature,
errorOnNotFound,
}: {
customerProduct: FullCusProduct;
feature?: Feature;
errorOnNotFound?: boolean;
}): Price | undefined {
const prices = cusProductToPrices({ cusProduct: customerProduct });
const result = prices.find((p) => {
const featureMatch = feature
? priceOnFeature({ price: p, feature })
: true;
return isPrepaidPrice(p) && featureMatch;
});
if (errorOnNotFound && !result) {
const featureMsg = feature ? ` for feature ${feature.id}` : "";
throw new InternalError({
message: `Prepaid price not found${featureMsg} for customer product ${customerProduct.id}`,
});
}
return result;
}

View File

@@ -32,3 +32,29 @@ export const findMainActiveCustomerProductByGroup = ({
return activeMainCusProduct; return activeMainCusProduct;
}; };
export const findActiveCustomerProductById = ({
fullCus,
productId,
internalEntityId,
}: {
fullCus: FullCustomer;
productId: string;
internalEntityId?: string;
}) => {
if (!internalEntityId) {
internalEntityId = fullCus.entity?.internal_id;
}
const cusProducts = fullCus.customer_products;
const activeMainCusProduct = cusProducts.find((customerProduct) => {
const { valid } = cp(customerProduct)
.hasProductId({ productId })
.onEntity({ internalEntityId });
return valid;
});
return activeMainCusProduct;
};

View File

@@ -1,3 +1,4 @@
import type { Feature } from "../../../models/featureModels/featureModels";
import { BillingInterval } from "../../../models/productModels/intervals/billingInterval"; import { BillingInterval } from "../../../models/productModels/intervals/billingInterval";
import type { FixedPriceConfig } from "../../../models/productModels/priceModels/priceConfig/fixedPriceConfig"; import type { FixedPriceConfig } from "../../../models/productModels/priceModels/priceConfig/fixedPriceConfig";
import type { UsagePriceConfig } from "../../../models/productModels/priceModels/priceConfig/usagePriceConfig"; import type { UsagePriceConfig } from "../../../models/productModels/priceModels/priceConfig/usagePriceConfig";
@@ -73,3 +74,16 @@ export const isPrepaidPrice = (
const billingType = getBillingType(price.config); const billingType = getBillingType(price.config);
return billingType === BillingType.UsageInAdvance; return billingType === BillingType.UsageInAdvance;
}; };
export const priceOnFeature = ({
price,
feature,
}: {
price: Price;
feature: Feature;
}) => {
return (
price.config.internal_feature_id === feature.internal_id ||
price.config.feature_id === feature.id
);
};