Files
cfw-autumn/shared/utils/fullSubjectUtils/mergeCustomerBillingControlsForCheck.ts
2026-04-24 21:12:04 +01:00

63 lines
2.0 KiB
TypeScript

import type { ApiCustomerV5 } from "../../api/customers/apiCustomerV5.js";
import type { ApiEntityV2 } from "../../api/entities/apiEntityV2.js";
/**
* Build a new entity apiSubject whose billing_controls inherit the customer's
* spend_limits and overage_allowed entries per feature_id. Entity's own entry
* always wins per feature; customer's entries fill any gaps.
*
* Used at check time so `apiSubjectToSpendLimit` / `apiSubjectToOverageAllowedControl`
* (which read from `subject.billing_controls`) see the inherited controls
* without needing to know about the customer separately.
*
* Pure — does not mutate inputs.
*/
export const mergeCustomerBillingControlsForCheck = ({
entityApiSubject,
customerApiSubject,
}: {
entityApiSubject: ApiEntityV2;
customerApiSubject: ApiCustomerV5;
}): ApiEntityV2 => {
const entitySpendLimits = entityApiSubject.billing_controls?.spend_limits ?? [];
const entityOverageAllowed =
entityApiSubject.billing_controls?.overage_allowed ?? [];
const customerSpendLimits =
customerApiSubject.billing_controls?.spend_limits ?? [];
const customerOverageAllowed =
customerApiSubject.billing_controls?.overage_allowed ?? [];
const entitySpendLimitFeatureIds = new Set(
entitySpendLimits
.map((entry) => entry.feature_id)
.filter((id): id is string => !!id),
);
const entityOverageAllowedFeatureIds = new Set(
entityOverageAllowed.map((entry) => entry.feature_id),
);
const inheritedSpendLimits = customerSpendLimits.filter(
(entry) =>
!!entry.feature_id && !entitySpendLimitFeatureIds.has(entry.feature_id),
);
const inheritedOverageAllowed = customerOverageAllowed.filter(
(entry) => !entityOverageAllowedFeatureIds.has(entry.feature_id),
);
if (
inheritedSpendLimits.length === 0 &&
inheritedOverageAllowed.length === 0
) {
return entityApiSubject;
}
return {
...entityApiSubject,
billing_controls: {
...entityApiSubject.billing_controls,
spend_limits: [...entitySpendLimits, ...inheritedSpendLimits],
overage_allowed: [...entityOverageAllowed, ...inheritedOverageAllowed],
},
};
};