fix: convert metered tier upTo from internal to display credits in listPlans (#18108)

## Summary
- The `listPlans` GraphQL query was returning metered tier `upTo` values
in internal credit units (1000x the display value), causing "Credits by
period" and "Credit Plan" dropdowns to show "50M" instead of "50k"
- Applied the existing `INTERNAL_CREDITS_PER_DISPLAY_CREDIT` (1000)
divisor in `formatBillingDatabasePriceToMeteredPriceDTO`, matching the
conversion already used in `getMeteredProductsUsage` resolver
- Updated frontend mock data to reflect the corrected display-unit
values

## Test plan
- [x] Unit tests pass for `format-database-product-to-graphql-dto.util`
- [x] Unit tests pass for `metered-credit.service` and
`billing-credit-rollover.service`
- [ ] Verify billing page shows correct credit amounts (50k, not 50M)
for "Credits by period" and "Credit Plan"


Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
This commit is contained in:
Félix Malfait
2026-02-20 09:44:11 +01:00
committed by GitHub
parent 783e57f5d8
commit 726b1373d9
3 changed files with 82 additions and 28 deletions
@@ -58,7 +58,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 999000,
unitAmount: null,
upTo: 7500000000,
upTo: 7500000,
},
{
__typename: 'BillingPriceTier',
@@ -78,7 +78,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 399000,
unitAmount: null,
upTo: 2600000000,
upTo: 2600000,
},
{
__typename: 'BillingPriceTier',
@@ -98,7 +98,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 199000,
unitAmount: null,
upTo: 1200000000,
upTo: 1200000,
},
{
__typename: 'BillingPriceTier',
@@ -118,7 +118,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 99000,
unitAmount: null,
upTo: 540000000,
upTo: 540000,
},
{
__typename: 'BillingPriceTier',
@@ -138,7 +138,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 29000,
unitAmount: null,
upTo: 130000000,
upTo: 130000,
},
{
__typename: 'BillingPriceTier',
@@ -158,7 +158,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 0,
unitAmount: null,
upTo: 50000000,
upTo: 50000,
},
{
__typename: 'BillingPriceTier',
@@ -178,7 +178,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 99900,
unitAmount: null,
upTo: 700000000,
upTo: 700000,
},
{
__typename: 'BillingPriceTier',
@@ -198,7 +198,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 39900,
unitAmount: null,
upTo: 240000000,
upTo: 240000,
},
{
__typename: 'BillingPriceTier',
@@ -218,7 +218,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 19900,
unitAmount: null,
upTo: 110000000,
upTo: 110000,
},
{
__typename: 'BillingPriceTier',
@@ -238,7 +238,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 9900,
unitAmount: null,
upTo: 50000000,
upTo: 50000,
},
{
__typename: 'BillingPriceTier',
@@ -258,7 +258,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 2900,
unitAmount: null,
upTo: 10000000,
upTo: 10000,
},
{
__typename: 'BillingPriceTier',
@@ -278,7 +278,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 0,
unitAmount: null,
upTo: 5000000,
upTo: 5000,
},
{
__typename: 'BillingPriceTier',
@@ -348,7 +348,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 999000,
unitAmount: null,
upTo: 7500000000,
upTo: 7500000,
},
{
__typename: 'BillingPriceTier',
@@ -368,7 +368,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 399000,
unitAmount: null,
upTo: 2600000000,
upTo: 2600000,
},
{
__typename: 'BillingPriceTier',
@@ -388,7 +388,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 199000,
unitAmount: null,
upTo: 1200000000,
upTo: 1200000,
},
{
__typename: 'BillingPriceTier',
@@ -408,7 +408,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 99000,
unitAmount: null,
upTo: 540000000,
upTo: 540000,
},
{
__typename: 'BillingPriceTier',
@@ -428,7 +428,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 29000,
unitAmount: null,
upTo: 130000000,
upTo: 130000,
},
{
__typename: 'BillingPriceTier',
@@ -448,7 +448,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 0,
unitAmount: null,
upTo: 50000000,
upTo: 50000,
},
{
__typename: 'BillingPriceTier',
@@ -468,7 +468,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 99900,
unitAmount: null,
upTo: 700000000,
upTo: 700000,
},
{
__typename: 'BillingPriceTier',
@@ -488,7 +488,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 39900,
unitAmount: null,
upTo: 240000000,
upTo: 240000,
},
{
__typename: 'BillingPriceTier',
@@ -508,7 +508,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 19900,
unitAmount: null,
upTo: 110000000,
upTo: 110000,
},
{
__typename: 'BillingPriceTier',
@@ -528,7 +528,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 9900,
unitAmount: null,
upTo: 50000000,
upTo: 50000,
},
{
__typename: 'BillingPriceTier',
@@ -548,7 +548,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 2900,
unitAmount: null,
upTo: 10000000,
upTo: 10000,
},
{
__typename: 'BillingPriceTier',
@@ -568,7 +568,7 @@ export const mockBillingPlans = {
__typename: 'BillingPriceTier',
flatAmount: 0,
unitAmount: null,
upTo: 5000000,
upTo: 5000,
},
{
__typename: 'BillingPriceTier',
@@ -31,7 +31,11 @@ describe('formatBillingDatabaseProductToGraphqlDTO', () => {
{
interval: SubscriptionInterval.Month,
tiers: [
{ up_to: 10, flat_amount: 500, unit_amount: null },
{
up_to: 10000000,
flat_amount: 500,
unit_amount: null,
},
{ up_to: null, flat_amount: null, unit_amount: 0.001 },
],
stripePriceId: 'price_metered1',
@@ -81,7 +85,11 @@ describe('formatBillingDatabaseProductToGraphqlDTO', () => {
{
interval: SubscriptionInterval.Month,
tiers: [
{ up_to: 10, flat_amount: 500, unit_amount: null },
{
up_to: 10000000,
flat_amount: 500,
unit_amount: null,
},
{ up_to: null, flat_amount: null, unit_amount: 0.001 },
],
stripePriceId: 'price_metered1',
@@ -91,7 +99,7 @@ describe('formatBillingDatabaseProductToGraphqlDTO', () => {
prices: [
{
tiers: [
{ upTo: 10, flatAmount: 500, unitAmount: null },
{ upTo: 10000, flatAmount: 500, unitAmount: null },
{ upTo: null, flatAmount: null, unitAmount: 0.001 },
],
recurringInterval: SubscriptionInterval.Month,
@@ -103,4 +111,49 @@ describe('formatBillingDatabaseProductToGraphqlDTO', () => {
],
});
});
it('should convert internal credits to display credits in metered tier upTo', () => {
const mockPlan = {
planKey: BillingPlanKey.PRO,
licensedProducts: [],
meteredProducts: [
{
id: 'product-2',
name: 'Test Metered Product',
billingPrices: [
{
interval: SubscriptionInterval.Month,
tiers: [
{
up_to: 50000000,
flat_amount: 0,
unit_amount: null,
},
{
up_to: null,
flat_amount: null,
unit_amount: null,
},
],
stripePriceId: 'price_metered1',
priceUsageType: BillingUsageType.METERED,
},
],
},
],
};
const result = formatBillingDatabaseProductToGraphqlDTO(
mockPlan as unknown as BillingGetPlanResult,
);
const meteredPrices = result.meteredProducts[0].prices;
expect(meteredPrices![0].tiers[0]).toEqual(
expect.objectContaining({ upTo: 50000 }),
);
expect(meteredPrices![0].tiers[1]).toEqual(
expect.objectContaining({ upTo: null }),
);
});
});
@@ -7,6 +7,7 @@ import { type BillingPriceEntity } from 'src/engine/core-modules/billing/entitie
import { SubscriptionInterval } from 'src/engine/core-modules/billing/enums/billing-subscription-interval.enum';
import { BillingUsageType } from 'src/engine/core-modules/billing/enums/billing-usage-type.enum';
import { type BillingGetPlanResult } from 'src/engine/core-modules/billing/types/billing-get-plan-result.type';
import { toDisplayCredits } from 'src/engine/core-modules/billing/utils/to-display-credits.util';
export const formatBillingDatabaseProductToGraphqlDTO = (
plan: BillingGetPlanResult,
@@ -42,7 +43,7 @@ const formatBillingDatabasePriceToMeteredPriceDTO = (
return {
tiers:
billingPrice?.tiers?.map((tier) => ({
upTo: tier.up_to,
upTo: tier.up_to !== null ? toDisplayCredits(tier.up_to) : tier.up_to,
flatAmount: tier.flat_amount,
unitAmount: tier.unit_amount,
})) ?? [],