@@ -44,6 +44,7 @@ import { resolveChallengeMemberPaymentAmount } from 'src/shared/payments/challen
4444
4545const BUDGET_LEDGER_DECIMAL_PLACES = 4 ;
4646const PAYMENT_DECIMAL_PLACES = 2 ;
47+ const PAYMENT_MARKUP_DECIMAL_PLACES = 4 ;
4748export const CHALLENGE_BUDGET_SYNC_SKIP_ATTRIBUTE = 'skipChallengeBudgetSync' ;
4849
4950interface EngagementBillingAccountConsume {
@@ -729,11 +730,25 @@ export class WinningsService {
729730 }
730731
731732 /**
732- * Normalizes the engagement billing-account markup to the payment column
733- * scale used by `payment.challenge_markup`.
733+ * Quantizes a payment markup to the persisted challenge-markup scale.
734+ *
735+ * Finance stores `payment.challenge_markup` at the same four-decimal scale as
736+ * billing-accounts-api-v6 markup values so fee calculations do not round the
737+ * rate to cents before multiplication.
738+ *
739+ * @param markup Decimal markup to normalize.
740+ * @returns JavaScript number rounded to the challenge-markup column scale.
741+ */
742+ private toPaymentMarkup ( markup : Prisma . Decimal ) : number {
743+ return this . toScaledAmount ( markup , PAYMENT_MARKUP_DECIMAL_PLACES ) ;
744+ }
745+
746+ /**
747+ * Normalizes the engagement billing-account markup for payment persistence
748+ * while preserving billing-account precision for downstream fee math.
734749 *
735750 * @param markup billing-account markup returned by billing-accounts-api-v6.
736- * @returns Rounded markup suitable for persistence on payment rows.
751+ * @returns Four-decimal markup suitable for persistence on payment rows.
737752 * @throws InternalServerErrorException when the markup is not finite.
738753 */
739754 private calculateEngagementChallengeMarkup ( markup : number ) : number {
@@ -743,16 +758,15 @@ export class WinningsService {
743758 ) ;
744759 }
745760
746- return this . toPaymentAmount ( new Prisma . Decimal ( markup ) ) ;
761+ return this . toPaymentMarkup ( new Prisma . Decimal ( markup ) ) ;
747762 }
748763
749764 /**
750- * Computes the persisted engagement challenge fee using the rounded payment
751- * markup scale .
765+ * Computes the persisted engagement challenge fee using the normalized
766+ * billing-account markup .
752767 *
753768 * @param totalAmount payment detail total amount.
754- * @param challengeMarkup rounded billing-account markup persisted on the
755- * payment row.
769+ * @param challengeMarkup billing-account markup persisted on the payment row.
756770 * @param detailIndex zero-based detail index for error reporting.
757771 * @returns Payment-scale challenge fee.
758772 * @throws BadRequestException when the detail amount is invalid.
@@ -793,8 +807,7 @@ export class WinningsService {
793807 * Computes an engagement consume amount with decimal-safe arithmetic.
794808 *
795809 * @param totalAmount payment detail total amount.
796- * @param challengeMarkup rounded billing-account markup persisted on the
797- * payment row.
810+ * @param challengeMarkup billing-account markup persisted on the payment row.
798811 * @param detailIndex zero-based detail index for error reporting.
799812 * @returns Ledger-scale consume amount including markup.
800813 * @throws BadRequestException when the detail amount is invalid.
@@ -852,8 +865,8 @@ export class WinningsService {
852865 *
853866 * @param body incoming winning creation request.
854867 * @returns assignment id, trusted billing account id, and typed consume
855- * requests to execute for non-TopGear billing accounts, plus the rounded
856- * challenge markup persisted on the created payment rows.
868+ * requests to execute for non-TopGear billing accounts, plus the normalized
869+ * four-decimal challenge markup persisted on the created payment rows.
857870 * @throws BadRequestException when engagement payment input is invalid.
858871 * @throws InternalServerErrorException when billing-account metadata cannot
859872 * be normalized into a finite markup.
0 commit comments