Skip to content

Commit 6e86079

Browse files
committed
Hotfix to keep TaaS payments as OWED, not ON_HOLD_ADMIN
1 parent e7419a8 commit 6e86079

2 files changed

Lines changed: 84 additions & 11 deletions

File tree

src/api/challenges/challenges.service.spec.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('ChallengesService', () => {
6363
expect(prisma.challenge_lock.create).not.toHaveBeenCalled();
6464
});
6565

66-
it('maps task challenges with taas metadata to TAAS_PAYMENT', () => {
66+
it('maps task challenges with taas metadata to TAAS_PAYMENT and OWED status', () => {
6767
const service = new ChallengesService(
6868
{} as any,
6969
{} as any,
@@ -87,6 +87,7 @@ describe('ChallengesService', () => {
8787
expect(payments).toEqual([
8888
expect.objectContaining({
8989
type: WinningsCategory.TAAS_PAYMENT,
90+
status: PaymentStatus.OWED,
9091
}),
9192
]);
9293
});
@@ -170,7 +171,52 @@ describe('ChallengesService', () => {
170171
]);
171172
});
172173

173-
it('defaults task challenge winnings to ON_HOLD_ADMIN status', async () => {
174+
it('defaults TAAS task challenge winnings to OWED status', async () => {
175+
const service = new ChallengesService(
176+
{} as any,
177+
{} as any,
178+
{} as any,
179+
{} as any,
180+
{} as any,
181+
);
182+
183+
jest
184+
.spyOn(service, 'getChallengeResources')
185+
.mockResolvedValue({ winner: [] } as any);
186+
jest.spyOn(service, 'generatePlacementWinnersPayments').mockReturnValue([
187+
{
188+
userId: '40158994',
189+
amount: 500,
190+
type: WinningsCategory.TAAS_PAYMENT,
191+
currency: PrizeType.USD,
192+
},
193+
] as any);
194+
jest
195+
.spyOn(service, 'generateCheckpointWinnersPayments')
196+
.mockReturnValue([]);
197+
jest.spyOn(service, 'generateCopilotPayment').mockReturnValue([]);
198+
jest.spyOn(service, 'generateReviewersPayments').mockResolvedValue([]);
199+
200+
const winnings = await service.getChallengePayments({
201+
id: '11111111-1111-1111-1111-111111111111',
202+
name: 'TAAS Task Challenge',
203+
type: 'Task',
204+
task: { isTask: true },
205+
billing: { billingAccountId: '1234', markup: 0.2 },
206+
} as any);
207+
208+
expect(winnings).toEqual([
209+
expect.objectContaining({
210+
category: WinningsCategory.TAAS_PAYMENT,
211+
status: PaymentStatus.OWED,
212+
}),
213+
]);
214+
expect(winnings[0].attributes).toMatchObject({
215+
[CHALLENGE_BUDGET_SYNC_SKIP_ATTRIBUTE]: true,
216+
});
217+
});
218+
219+
it('defaults non-TAAS task challenge winnings to ON_HOLD_ADMIN status', async () => {
174220
const service = new ChallengesService(
175221
{} as any,
176222
{} as any,

src/api/challenges/challenges.service.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,31 @@ export class ChallengesService {
148148
: WinningsCategory.REVIEW_BOARD_PAYMENT;
149149
}
150150

151+
/**
152+
* Resolves the explicit payment status for task payments generated from
153+
* challenge-api-v6 challenge data.
154+
*
155+
* @param challenge Challenge details returned by challenge-api-v6.
156+
* @param category Winnings category selected for the generated payment.
157+
* @param currency Prize currency selected for the generated payment.
158+
* @returns OWED for USD TAAS task payments, ON_HOLD_ADMIN for other USD task
159+
* payments, and undefined when standard payout-readiness rules should apply.
160+
* @throws This method does not throw.
161+
*/
162+
private getTaskPaymentStatus(
163+
challenge: Challenge,
164+
category: WinningsCategory,
165+
currency?: PrizeType,
166+
): PaymentStatus | undefined {
167+
if (!challenge.task?.isTask || currency !== PrizeType.USD) {
168+
return undefined;
169+
}
170+
171+
return category === WinningsCategory.TAAS_PAYMENT
172+
? PaymentStatus.OWED
173+
: PaymentStatus.ON_HOLD_ADMIN;
174+
}
175+
151176
async getChallenge(challengeId: string) {
152177
if (!isUUID(challengeId)) {
153178
throw new BadRequestException(
@@ -232,15 +257,15 @@ export class ChallengesService {
232257
? (type ?? defaultCategory)
233258
: WinningsCategory.POINTS_AWARD;
234259

260+
const status = this.getTaskPaymentStatus(challenge, winType, currency);
261+
235262
return {
236263
handle: winner.handle,
237264
amount: prizes[winner.placement - 1].value,
238265
userId: winner.userId.toString(),
239266
type: winType,
240267
currency,
241-
...(challenge.task?.isTask && currency === PrizeType.USD
242-
? { status: PaymentStatus.ON_HOLD_ADMIN }
243-
: {}),
268+
...(status ? { status } : {}),
244269
description:
245270
challenge.type === 'Task'
246271
? challenge.name
@@ -434,6 +459,12 @@ export class ChallengesService {
434459
currency,
435460
);
436461

462+
const status = this.getTaskPaymentStatus(
463+
challenge,
464+
winType,
465+
currency,
466+
);
467+
437468
return {
438469
handle: reviewer.memberHandle,
439470
userId: reviewer.memberId.toString(),
@@ -446,9 +477,7 @@ export class ChallengesService {
446477
),
447478
type: winType,
448479
currency: placementPrizes?.[0]?.type ?? PrizeType.USD,
449-
...(challenge.task?.isTask && currency === PrizeType.USD
450-
? { status: PaymentStatus.ON_HOLD_ADMIN }
451-
: {}),
480+
...(status ? { status } : {}),
452481
description: `${challenge.name} - ${phaseReviews[0].phaseName}`,
453482
};
454483
},
@@ -515,9 +544,7 @@ export class ChallengesService {
515544
return payments.map((payment) => {
516545
const paymentStatus =
517546
payment.status ??
518-
(challenge.task?.isTask && payment.currency === PrizeType.USD
519-
? PaymentStatus.ON_HOLD_ADMIN
520-
: undefined);
547+
this.getTaskPaymentStatus(challenge, payment.type, payment.currency);
521548

522549
return {
523550
winnerId: payment.userId.toString(),

0 commit comments

Comments
 (0)