Skip to content

Commit 28daa79

Browse files
committed
fix(Members): enforce currency for totalAmountDonatedFromTo
1 parent 027e1b7 commit 28daa79

File tree

4 files changed

+58
-26
lines changed

4 files changed

+58
-26
lines changed

server/graphql/loaders/index.ts

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,13 +1122,17 @@ export const generateLoaders = req => {
11221122
});
11231123
}),
11241124
),
1125-
totalAmountDonatedFromTo: new DataLoader<{ CollectiveId: number; FromCollectiveId: number }, number>(keys =>
1126-
Transaction.findAll({
1125+
totalAmountDonatedFromTo: new DataLoader<
1126+
{ CollectiveId: number; FromCollectiveId: number; currency: SupportedCurrency },
1127+
number
1128+
>(async keys => {
1129+
const results = (await Transaction.findAll({
11271130
attributes: [
11281131
'FromCollectiveId',
11291132
'UsingGiftCardFromCollectiveId',
11301133
'CollectiveId',
11311134
[sequelize.fn('SUM', sequelize.col('amount')), 'totalAmount'],
1135+
'currency',
11321136
],
11331137
where: {
11341138
[Op.or]: {
@@ -1149,26 +1153,48 @@ export const generateLoaders = req => {
11491153
kind: { [Op.notIn]: ['HOST_FEE', 'HOST_FEE_SHARE', 'HOST_FEE_SHARE_DEBT', 'PLATFORM_TIP_DEBT'] },
11501154
RefundTransactionId: null,
11511155
},
1152-
group: ['FromCollectiveId', 'UsingGiftCardFromCollectiveId', 'CollectiveId'],
1153-
}).then(results => {
1154-
const resultsByKey = {};
1155-
results.forEach(({ CollectiveId, FromCollectiveId, UsingGiftCardFromCollectiveId, dataValues }) => {
1156-
// Credit collective that emitted the gift card (if any)
1157-
if (UsingGiftCardFromCollectiveId) {
1158-
const key = `${UsingGiftCardFromCollectiveId}-${CollectiveId}`;
1159-
const donated = resultsByKey[key] || 0;
1160-
resultsByKey[key] = donated + dataValues['totalAmount'];
1156+
group: ['FromCollectiveId', 'UsingGiftCardFromCollectiveId', 'CollectiveId', 'currency'],
1157+
raw: true,
1158+
mapToModel: false,
1159+
})) as unknown as Array<{
1160+
FromCollectiveId: number;
1161+
UsingGiftCardFromCollectiveId: number;
1162+
CollectiveId: number;
1163+
currency: SupportedCurrency;
1164+
totalAmount: number;
1165+
}>;
1166+
1167+
const resultsByKey: Record<`${number}-${number}`, Partial<Record<SupportedCurrency, number>>> = {};
1168+
results.forEach(({ CollectiveId, FromCollectiveId, UsingGiftCardFromCollectiveId, currency, totalAmount }) => {
1169+
// Credit collective that emitted the gift card (if any)
1170+
if (UsingGiftCardFromCollectiveId) {
1171+
const key = `${UsingGiftCardFromCollectiveId}-${CollectiveId}`;
1172+
resultsByKey[key] = resultsByKey[key] || {};
1173+
resultsByKey[key][currency] = (resultsByKey[key][currency] || 0) + totalAmount;
1174+
}
1175+
// Credit collective who actually made the transaction
1176+
const key = `${FromCollectiveId}-${CollectiveId}`;
1177+
resultsByKey[key] = resultsByKey[key] || {};
1178+
resultsByKey[key][currency] = (resultsByKey[key][currency] || 0) + totalAmount;
1179+
});
1180+
1181+
return Promise.all(
1182+
keys.map(async key => {
1183+
const results = resultsByKey[`${key.FromCollectiveId}-${key.CollectiveId}`] || {};
1184+
let total = 0;
1185+
for (const [resultCurrency, resultAmount] of Object.entries(results)) {
1186+
const fxRate = await req.loaders.CurrencyExchangeRate.fxRate.load({
1187+
fromCurrency: resultCurrency,
1188+
toCurrency: key.currency,
1189+
});
1190+
1191+
total += resultAmount * fxRate;
11611192
}
1162-
// Credit collective who actually made the transaction
1163-
const key = `${FromCollectiveId}-${CollectiveId}`;
1164-
const donated = resultsByKey[key] || 0;
1165-
resultsByKey[key] = donated + dataValues['totalAmount'];
1166-
});
1167-
return keys.map(key => {
1168-
return resultsByKey[`${key.FromCollectiveId}-${key.CollectiveId}`] || 0;
1169-
});
1170-
}),
1171-
),
1193+
1194+
return Math.round(total);
1195+
}),
1196+
);
1197+
}),
11721198
hostFeeAmountForTransaction: transactionLoaders.generateHostFeeAmountForTransactionLoader(),
11731199
paymentProcessorFeeAmountForTransaction:
11741200
transactionLoaders.generatePaymentProcessorFeeAmountForTransactionLoader(),

server/graphql/v1/types.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,14 @@ const StatsMemberType = new GraphQLObjectType({
283283
totalDonations: {
284284
type: GraphQLFloat,
285285
description: 'total amount donated by this member either directly or using a gift card it has emitted',
286-
resolve(member, args, req) {
286+
async resolve(member, args, req) {
287+
const collective = member.collective || (await req.loaders.Collective.byId.load(member.CollectiveId));
287288
return (
288289
member.totalDonations ||
289290
req.loaders.Transaction.totalAmountDonatedFromTo.load({
290291
FromCollectiveId: member.MemberCollectiveId,
291292
CollectiveId: member.CollectiveId,
293+
currency: collective.currency,
292294
})
293295
);
294296
},

server/graphql/v2/object/Member.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { checkScope } from '../../common/scope-check';
66
import { GraphQLMemberRole } from '../enum/MemberRole';
77
import { idEncode } from '../identifiers';
88
import { GraphQLAccount } from '../interface/Account';
9-
import { GraphQLAmount } from '../object/Amount';
9+
import { GraphQLAmount, GraphQLAmountFields } from '../object/Amount';
1010
import { GraphQLTier } from '../object/Tier';
1111

1212
const getMemberFields = () => ({
@@ -54,15 +54,18 @@ const getMemberFields = () => ({
5454
totalDonations: {
5555
type: new GraphQLNonNull(GraphQLAmount),
5656
description: 'Total amount donated',
57-
async resolve(member, args, req) {
57+
async resolve(member, args, req): Promise<GraphQLAmountFields> {
58+
const collective = await req.loaders.Collective.byId.load(member.CollectiveId);
5859
if (member.totalDonations) {
59-
return { value: member.totalDonations };
60+
return { value: member.totalDonations, currency: collective.currency };
6061
}
61-
const collective = await req.loaders.Collective.byId.load(member.CollectiveId);
62+
6263
const value = await req.loaders.Transaction.totalAmountDonatedFromTo.load({
6364
FromCollectiveId: member.MemberCollectiveId,
6465
CollectiveId: member.CollectiveId,
66+
currency: collective.currency,
6567
});
68+
6669
return { value, currency: collective.currency };
6770
},
6871
},

server/graphql/v2/object/Order.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ export const GraphQLOrder = new GraphQLObjectType({
205205
const value = await req.loaders.Transaction.totalAmountDonatedFromTo.load({
206206
FromCollectiveId: order.FromCollectiveId,
207207
CollectiveId: order.CollectiveId,
208+
currency: order.currency,
208209
});
209210
return { value, currency: order.currency };
210211
},

0 commit comments

Comments
 (0)