feat(billing): refacto billing (#14243)

… prices for metered billing

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-09-19 11:25:53 +02:00
committed by GitHub
parent 163890f6c8
commit 43e0cd5d05
351 changed files with 16091 additions and 6101 deletions
@@ -1,3 +1,4 @@
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { DEFAULT_MUTATION_BATCH_SIZE } from '@/object-record/constants/DefaultMutationBatchSize';
import {
@@ -9,7 +10,6 @@ import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { ApolloError } from '@apollo/client';
import { t } from '@lingui/core/macro';
import { formatNumber } from '~/utils/format/formatNumber';
export const useBatchCreateManyRecords = <
CreatedObjectRecord extends ObjectRecord = ObjectRecord,
@@ -43,6 +43,7 @@ export const useBatchCreateManyRecords = <
});
const { enqueueWarningSnackBar } = useSnackBar();
const { formatNumber } = useNumberFormat();
const batchCreateManyRecords = async ({
recordsToCreate,
@@ -13,8 +13,11 @@ import isEmpty from 'lodash.isempty';
import { FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION } from 'twenty-shared/constants';
import { isDefined } from 'twenty-shared/utils';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import {
formatNumber as utilFormatNumber,
type FormatNumberOptions,
} from '~/utils/format/formatNumber';
import { formatToShortNumber } from '~/utils/format/formatToShortNumber';
import { formatNumber } from '~/utils/format/formatNumber';
import { formatDateString } from '~/utils/string/formatDateString';
import { formatDateTimeString } from '~/utils/string/formatDateTimeString';
@@ -27,6 +30,7 @@ export const computeAggregateValueAndLabel = ({
timeFormat,
timeZone,
localeCatalog,
formatNumberFn,
}: {
data: AggregateRecordsData;
objectMetadataItem: ObjectMetadataItem;
@@ -36,7 +40,15 @@ export const computeAggregateValueAndLabel = ({
timeFormat: TimeFormat;
timeZone: string;
localeCatalog: Locale;
formatNumberFn?: (
value: number,
options?: Omit<FormatNumberOptions, 'format'>,
) => string;
}) => {
const formatNumber =
formatNumberFn ??
((v: number, opts?: Omit<FormatNumberOptions, 'format'>) =>
utilFormatNumber(v, opts));
if (isEmpty(data)) {
return {};
}
@@ -92,8 +104,8 @@ export const computeAggregateValueAndLabel = ({
const { decimals, type } = field.settings ?? {};
value =
type === 'percentage'
? `${formatNumber(value * 100, decimals)}%`
: formatNumber(value, decimals);
? `${formatNumber(value * 100, { decimals })}%`
: formatNumber(value, { decimals });
break;
}
@@ -1,6 +1,6 @@
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { CalendarStartDay } from '@/localization/constants/CalendarStartDay';
import { detectCalendarStartDay } from '@/localization/utils/detectCalendarStartDay';
import { detectCalendarStartDay } from '@/localization/utils/detection/detectCalendarStartDay';
import {
addDays,
eachWeekOfInterval,
@@ -1,17 +1,14 @@
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { getNumberFormatFromWorkspaceNumberFormat } from '@/localization/utils/getNumberFormatFromWorkspaceNumberFormat';
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
import { useNumberFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useNumberFieldDisplay';
import { NumberDisplay } from '@/ui/field/display/components/NumberDisplay';
import { useRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { formatToShortNumber } from '~/utils/format/formatToShortNumber';
import { formatNumber } from '~/utils/format/formatNumber';
export const NumberFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useNumberFieldDisplay();
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState);
const type = fieldDefinition.metadata.settings?.type;
const decimals = fieldDefinition.metadata.settings?.decimals;
const { formatNumber } = useNumberFormat();
if (!isDefined(fieldValue)) {
return <NumberDisplay value={null} />;
@@ -21,17 +18,11 @@ export const NumberFieldDisplay = () => {
let formattedValue: string;
if (type === 'percentage') {
formattedValue = `${formatNumber(numericValue * 100, getNumberFormatFromWorkspaceNumberFormat(currentWorkspaceMember?.numberFormat!), decimals)}%`;
formattedValue = `${formatNumber(numericValue * 100, { decimals })}%`;
} else if (type === 'shortNumber') {
formattedValue = formatToShortNumber(numericValue);
} else {
formattedValue = formatNumber(
numericValue,
getNumberFormatFromWorkspaceNumberFormat(
currentWorkspaceMember?.numberFormat!,
),
decimals,
);
formattedValue = formatNumber(numericValue, { decimals });
}
return <NumberDisplay value={formattedValue} />;