Skip to content

Commit 9fc2826

Browse files
author
liuliquan
committed
PLAT-3221 lock/consume budget
1 parent 19d3d0d commit 9fc2826

File tree

2 files changed

+391
-166
lines changed

2 files changed

+391
-166
lines changed

src/api/BillingAccount.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import _ from "lodash";
2+
import axios from "axios";
3+
import { StatusBuilder } from "@grpc/grpc-js";
4+
import { Status } from "@grpc/grpc-js/build/src/constants";
5+
6+
import m2m from "../helpers/MachineToMachineToken";
7+
import { ChallengeStatuses, TGBillingAccounts } from "../common/Constants";
8+
9+
const { V3_BA_API_URL } = process.env;
10+
11+
async function lockAmount(billingAccountId: number, dto: LockAmountDTO) {
12+
if (_.includes(TGBillingAccounts, billingAccountId)) {
13+
return;
14+
}
15+
16+
try {
17+
const m2mToken = await m2m.getM2MToken();
18+
19+
await axios.patch(
20+
`${V3_BA_API_URL}/${billingAccountId}/lock-amount`,
21+
{
22+
param: dto,
23+
},
24+
{
25+
headers: {
26+
Authorization: `Bearer ${m2mToken}`,
27+
"Content-Type": "application/json",
28+
},
29+
}
30+
);
31+
} catch (err: any) {
32+
throw new StatusBuilder()
33+
.withCode(Status.INTERNAL)
34+
.withDetails(err.response?.data?.result?.content ?? "Failed to lock challenge amount")
35+
.build();
36+
}
37+
}
38+
39+
async function consumeAmount(billingAccountId: number, dto: ConsumeAmountDTO) {
40+
// prettier-ignore
41+
if (_.includes(TGBillingAccounts, billingAccountId)) {
42+
return;
43+
}
44+
45+
try {
46+
const m2mToken = await m2m.getM2MToken();
47+
48+
await axios.patch(
49+
`${V3_BA_API_URL}/${billingAccountId}/consume-amount`,
50+
{
51+
param: dto,
52+
},
53+
{
54+
headers: {
55+
Authorization: `Bearer ${m2mToken}`,
56+
"Content-Type": "application/json",
57+
},
58+
}
59+
);
60+
} catch (err: any) {
61+
throw new StatusBuilder()
62+
.withCode(Status.INTERNAL)
63+
.withDetails(err.response?.data?.result?.content ?? "Failed to consume challenge amount")
64+
.build();
65+
}
66+
}
67+
68+
interface LockAmountDTO {
69+
challengeId: string;
70+
lockAmount: number;
71+
}
72+
interface ConsumeAmountDTO {
73+
challengeId: string;
74+
actualSpent: number;
75+
markup?: number;
76+
}
77+
78+
// prettier-ignore
79+
export async function lockConsumeAmount(baValidation: BAValidation, rollback: boolean = false): Promise<void> {
80+
console.log("Update BA validation:", baValidation);
81+
if (!_.isNumber(baValidation.billingAccountId)) {
82+
return;
83+
}
84+
85+
if (
86+
baValidation.status === ChallengeStatuses.New ||
87+
baValidation.status === ChallengeStatuses.Draft ||
88+
baValidation.status === ChallengeStatuses.Active ||
89+
baValidation.status === ChallengeStatuses.Approved
90+
) {
91+
// Update lock amount
92+
const currAmount = baValidation.totalPrizesInCents / 100;
93+
const prevAmount = baValidation.prevTotalPrizesInCents / 100;
94+
95+
if (currAmount !== prevAmount) {
96+
await lockAmount(baValidation.billingAccountId, {
97+
challengeId: baValidation.challengeId!,
98+
lockAmount: rollback ? prevAmount : currAmount,
99+
});
100+
}
101+
} else if (baValidation.status === ChallengeStatuses.Completed) {
102+
// Note an already completed challenge could still be updated with prizes
103+
const currAmount = baValidation.totalPrizesInCents / 100;
104+
const prevAmount = baValidation.prevStatus === ChallengeStatuses.Completed ? baValidation.prevTotalPrizesInCents / 100 : 0;
105+
106+
if (currAmount !== prevAmount) {
107+
await consumeAmount(baValidation.billingAccountId, {
108+
challengeId: baValidation.challengeId!,
109+
actualSpent: rollback ? prevAmount : currAmount,
110+
markup: baValidation.markup,
111+
});
112+
}
113+
} else if (
114+
baValidation.status === ChallengeStatuses.Deleted ||
115+
baValidation.status === ChallengeStatuses.Canceled ||
116+
baValidation.status === ChallengeStatuses.CancelledFailedReview ||
117+
baValidation.status === ChallengeStatuses.CancelledFailedScreening ||
118+
baValidation.status === ChallengeStatuses.CancelledZeroSubmissions ||
119+
baValidation.status === ChallengeStatuses.CancelledWinnerUnresponsive ||
120+
baValidation.status === ChallengeStatuses.CancelledClientRequest ||
121+
baValidation.status === ChallengeStatuses.CancelledRequirementsInfeasible ||
122+
baValidation.status === ChallengeStatuses.CancelledZeroRegistrations ||
123+
baValidation.status === ChallengeStatuses.CancelledPaymentFailed
124+
) {
125+
// Challenge canceled, unlock previous locked amount
126+
const currAmount = 0;
127+
const prevAmount = baValidation.prevTotalPrizesInCents / 100;
128+
129+
if (currAmount !== prevAmount) {
130+
await lockAmount(baValidation.billingAccountId, {
131+
challengeId: baValidation.challengeId!,
132+
lockAmount: rollback ? prevAmount : 0,
133+
});
134+
}
135+
}
136+
}
137+
138+
export interface BAValidation {
139+
challengeId?: string;
140+
billingAccountId?: number;
141+
markup?: number;
142+
prevStatus?: string;
143+
status?: string;
144+
prevTotalPrizesInCents: number;
145+
totalPrizesInCents: number;
146+
}

0 commit comments

Comments
 (0)