diff --git a/.github/workflows/code_reviewer.yml b/.github/workflows/code_reviewer.yml deleted file mode 100644 index 7ffa3ec..0000000 --- a/.github/workflows/code_reviewer.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: AI PR Reviewer - -on: - pull_request: - types: - - opened - - synchronize -permissions: - pull-requests: write -jobs: - tc-ai-pr-review: - runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: actions/checkout@v3 - - - name: TC AI PR Reviewer - uses: topcoder-platform/tc-ai-pr-reviewer@prompt-update - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # The GITHUB_TOKEN is there by default so you just need to keep it like it is and not necessarily need to add it as secret as it will throw an error. [More Details](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) - LAB45_API_KEY: ${{ secrets.LAB45_API_KEY }} - exclude: '**/*.json, **/*.md, **/*.jpg, **/*.png, **/*.jpeg, **/*.bmp, **/*.webp' # Optional: exclude patterns separated by commas diff --git a/src/api/challenges/challenges.service.spec.ts b/src/api/challenges/challenges.service.spec.ts index ac8a42c..e7022e8 100644 --- a/src/api/challenges/challenges.service.spec.ts +++ b/src/api/challenges/challenges.service.spec.ts @@ -63,7 +63,7 @@ describe('ChallengesService', () => { expect(prisma.challenge_lock.create).not.toHaveBeenCalled(); }); - it('maps task challenges with taas metadata to TAAS_PAYMENT', () => { + it('maps task challenges with taas metadata to TAAS_PAYMENT and OWED status', () => { const service = new ChallengesService( {} as any, {} as any, @@ -87,6 +87,7 @@ describe('ChallengesService', () => { expect(payments).toEqual([ expect.objectContaining({ type: WinningsCategory.TAAS_PAYMENT, + status: PaymentStatus.OWED, }), ]); }); @@ -170,7 +171,52 @@ describe('ChallengesService', () => { ]); }); - it('defaults task challenge winnings to ON_HOLD_ADMIN status', async () => { + it('defaults TAAS task challenge winnings to OWED status', async () => { + const service = new ChallengesService( + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + ); + + jest + .spyOn(service, 'getChallengeResources') + .mockResolvedValue({ winner: [] } as any); + jest.spyOn(service, 'generatePlacementWinnersPayments').mockReturnValue([ + { + userId: '40158994', + amount: 500, + type: WinningsCategory.TAAS_PAYMENT, + currency: PrizeType.USD, + }, + ] as any); + jest + .spyOn(service, 'generateCheckpointWinnersPayments') + .mockReturnValue([]); + jest.spyOn(service, 'generateCopilotPayment').mockReturnValue([]); + jest.spyOn(service, 'generateReviewersPayments').mockResolvedValue([]); + + const winnings = await service.getChallengePayments({ + id: '11111111-1111-1111-1111-111111111111', + name: 'TAAS Task Challenge', + type: 'Task', + task: { isTask: true }, + billing: { billingAccountId: '1234', markup: 0.2 }, + } as any); + + expect(winnings).toEqual([ + expect.objectContaining({ + category: WinningsCategory.TAAS_PAYMENT, + status: PaymentStatus.OWED, + }), + ]); + expect(winnings[0].attributes).toMatchObject({ + [CHALLENGE_BUDGET_SYNC_SKIP_ATTRIBUTE]: true, + }); + }); + + it('defaults non-TAAS task challenge winnings to ON_HOLD_ADMIN status', async () => { const service = new ChallengesService( {} as any, {} as any, diff --git a/src/api/challenges/challenges.service.ts b/src/api/challenges/challenges.service.ts index b420ec0..80f2069 100644 --- a/src/api/challenges/challenges.service.ts +++ b/src/api/challenges/challenges.service.ts @@ -148,6 +148,31 @@ export class ChallengesService { : WinningsCategory.REVIEW_BOARD_PAYMENT; } + /** + * Resolves the explicit payment status for task payments generated from + * challenge-api-v6 challenge data. + * + * @param challenge Challenge details returned by challenge-api-v6. + * @param category Winnings category selected for the generated payment. + * @param currency Prize currency selected for the generated payment. + * @returns OWED for USD TAAS task payments, ON_HOLD_ADMIN for other USD task + * payments, and undefined when standard payout-readiness rules should apply. + * @throws This method does not throw. + */ + private getTaskPaymentStatus( + challenge: Challenge, + category: WinningsCategory, + currency?: PrizeType, + ): PaymentStatus | undefined { + if (!challenge.task?.isTask || currency !== PrizeType.USD) { + return undefined; + } + + return category === WinningsCategory.TAAS_PAYMENT + ? PaymentStatus.OWED + : PaymentStatus.ON_HOLD_ADMIN; + } + async getChallenge(challengeId: string) { if (!isUUID(challengeId)) { throw new BadRequestException( @@ -232,15 +257,15 @@ export class ChallengesService { ? (type ?? defaultCategory) : WinningsCategory.POINTS_AWARD; + const status = this.getTaskPaymentStatus(challenge, winType, currency); + return { handle: winner.handle, amount: prizes[winner.placement - 1].value, userId: winner.userId.toString(), type: winType, currency, - ...(challenge.task?.isTask && currency === PrizeType.USD - ? { status: PaymentStatus.ON_HOLD_ADMIN } - : {}), + ...(status ? { status } : {}), description: challenge.type === 'Task' ? challenge.name @@ -434,6 +459,12 @@ export class ChallengesService { currency, ); + const status = this.getTaskPaymentStatus( + challenge, + winType, + currency, + ); + return { handle: reviewer.memberHandle, userId: reviewer.memberId.toString(), @@ -446,9 +477,7 @@ export class ChallengesService { ), type: winType, currency: placementPrizes?.[0]?.type ?? PrizeType.USD, - ...(challenge.task?.isTask && currency === PrizeType.USD - ? { status: PaymentStatus.ON_HOLD_ADMIN } - : {}), + ...(status ? { status } : {}), description: `${challenge.name} - ${phaseReviews[0].phaseName}`, }; }, @@ -513,6 +542,10 @@ export class ChallengesService { ); return payments.map((payment) => { + const paymentStatus = + payment.status ?? + this.getTaskPaymentStatus(challenge, payment.type, payment.currency); + return { winnerId: payment.userId.toString(), type: