Skip to content

Commit

Permalink
convert Transactions to typescript class (#10081)
Browse files Browse the repository at this point in the history
  • Loading branch information
znarf committed May 15, 2024
1 parent 611dc51 commit a463dff
Show file tree
Hide file tree
Showing 23 changed files with 1,683 additions and 1,773 deletions.
4 changes: 2 additions & 2 deletions scripts/payment-methods/populate-expense-payment-method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import { parseToBoolean } from '../../server/lib/utils';
import { Op, sequelize } from '../../server/models';
import Expense from '../../server/models/Expense';
import PayoutMethod from '../../server/models/PayoutMethod';
import { TransactionInterface } from '../../server/models/Transaction';
import Transaction from '../../server/models/Transaction';

const IS_DRY = !process.env.DRY ? true : parseToBoolean(process.env.DRY);
const DATE_FROM = new Date(process.env.DATE_FROM || '2024-01-01');

const populatePaymentMethod = async expense => {
const isManual = last(expense.Transactions as Array<TransactionInterface>)?.data?.isManual;
const isManual = last(expense.Transactions as Array<Transaction>)?.data?.isManual;
// Automatically settlements that can be associated based on payout method and data
if (
!isManual &&
Expand Down
4 changes: 2 additions & 2 deletions scripts/paypal/cancel-all-subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import OrderStatuses from '../../server/constants/order-status';
import logger from '../../server/lib/logger';
import models, { Op } from '../../server/models';
import Order from '../../server/models/Order';
import { TransactionInterface } from '../../server/models/Transaction';
import Transaction from '../../server/models/Transaction';
import { paypalRequestV2 } from '../../server/paymentProviders/paypal/api';
import { getCaptureIdFromPaypalTransaction } from '../../server/paymentProviders/paypal/payment';
import {
Expand Down Expand Up @@ -65,7 +65,7 @@ const main = async () => {
throw new Error(`Collective ${collectiveSlug} not found`);
}

const orders = await models.Order.findAll<Order & { Transaction?: TransactionInterface }>({
const orders = await models.Order.findAll<Order & { Transaction?: Transaction }>({
order: [['createdAt', 'DESC']],
where: {
CollectiveId: collective.id,
Expand Down
4 changes: 2 additions & 2 deletions scripts/taxes/taxes-on-host-fee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { groupBy, mapValues, pick, sumBy } from 'lodash';
import logger from '../../server/lib/logger';
import { formatCurrency } from '../../server/lib/utils';
import models, { sequelize } from '../../server/models';
import { TransactionInterface } from '../../server/models/Transaction';
import Transaction from '../../server/models/Transaction';

const DRY_RUN = process.env.DRY_RUN !== 'false';

Expand Down Expand Up @@ -95,7 +95,7 @@ program
}
});

const migrateHostFeeTransaction = (transaction, newValues, dbTransaction): Promise<TransactionInterface> => {
const migrateHostFeeTransaction = (transaction, newValues, dbTransaction): Promise<Transaction> => {
return transaction.update(
{
...newValues,
Expand Down
12 changes: 4 additions & 8 deletions server/graphql/common/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { refundTransaction as refundTransactionPayment } from '../../lib/payment
import { getPolicy } from '../../lib/policies';
import twoFactorAuthLib from '../../lib/two-factor-authentication';
import models from '../../models';
import { TransactionInterface } from '../../models/Transaction';
import Transaction from '../../models/Transaction';
import { Forbidden, NotFound } from '../errors';

import { isHostAdmin } from './expenses';
Expand Down Expand Up @@ -127,7 +127,7 @@ const remoteUserMeetsOneCondition = async (req, transaction, conditions): Promis
};

/** Checks if the user can refund this transaction */
export const canRefund = async (transaction: TransactionInterface, _: void, req: express.Request): Promise<boolean> => {
export const canRefund = async (transaction: Transaction, _: void, req: express.Request): Promise<boolean> => {
if (transaction.type !== TransactionTypes.CREDIT || transaction.OrderId === null || transaction.isRefund === true) {
return false;
}
Expand Down Expand Up @@ -164,11 +164,7 @@ export const canRefund = async (transaction: TransactionInterface, _: void, req:
}
};

export const canDownloadInvoice = async (
transaction: TransactionInterface,
_: void,
req: express.Request,
): Promise<boolean> => {
export const canDownloadInvoice = async (transaction: Transaction, _: void, req: express.Request): Promise<boolean> => {
if (transaction.OrderId) {
const order = await req.loaders.Order.byId.load(transaction.OrderId);
if (order.status === orderStatus.REJECTED) {
Expand All @@ -188,7 +184,7 @@ export const canDownloadInvoice = async (
export const canReject = canRefund;

export async function refundTransaction(
passedTransaction: TransactionInterface,
passedTransaction: Transaction,
req: express.Request,
args: { message?: string } = {},
) {
Expand Down
36 changes: 15 additions & 21 deletions server/graphql/loaders/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { groupBy } from 'lodash';

import { TransactionKind } from '../../constants/transaction-kind';
import models, { Op } from '../../models';
import Transaction, { TransactionInterface } from '../../models/Transaction';
import Transaction from '../../models/Transaction';

export const generateHostFeeAmountForTransactionLoader = (): DataLoader<TransactionInterface, number> =>
export const generateHostFeeAmountForTransactionLoader = (): DataLoader<Transaction, number> =>
new DataLoader(
async (transactions: TransactionInterface[]) => {
async (transactions: Transaction[]) => {
const transactionsWithoutHostFee = transactions.filter(transaction => {
// Legacy transactions have their host fee set on `hostFeeInHostCurrency`. No need to fetch for them
// Also only contributions and added funds can have host fees
Expand All @@ -27,9 +27,8 @@ export const generateHostFeeAmountForTransactionLoader = (): DataLoader<Transact
},
});

const keyBuilder = (transaction: TransactionInterface) =>
`${transaction.TransactionGroup}-${transaction.CollectiveId}`;
const groupedTransactions: Record<string, TransactionInterface[]> = groupBy(hostFeeTransactions, keyBuilder);
const keyBuilder = (transaction: Transaction) => `${transaction.TransactionGroup}-${transaction.CollectiveId}`;
const groupedTransactions: Record<string, Transaction[]> = groupBy(hostFeeTransactions, keyBuilder);
return transactions.map(transaction => {
if (transaction.hostFeeInHostCurrency) {
return transaction.hostFeeInHostCurrency;
Expand All @@ -49,9 +48,9 @@ export const generateHostFeeAmountForTransactionLoader = (): DataLoader<Transact
},
);

export const generatePaymentProcessorFeeAmountForTransactionLoader = (): DataLoader<TransactionInterface, number> =>
export const generatePaymentProcessorFeeAmountForTransactionLoader = (): DataLoader<Transaction, number> =>
new DataLoader(
async (transactions: TransactionInterface[]) => {
async (transactions: Transaction[]) => {
const transactionsWithoutProcessorFee = transactions.filter(transaction => {
// Legacy transactions have their payment processor fee set on `paymentProcessorFeeInHostCurrency`. No need to fetch for them.
// Platform tips also had processor fees as we used to split them with the collective, but we stopped doing that on 2021-04-01.
Expand All @@ -71,12 +70,8 @@ export const generatePaymentProcessorFeeAmountForTransactionLoader = (): DataLoa
},
});

const keyBuilder = (transaction: TransactionInterface) =>
`${transaction.TransactionGroup}-${transaction.CollectiveId}`;
const groupedTransactions: Record<string, TransactionInterface[]> = groupBy(
processorFeesTransactions,
keyBuilder,
);
const keyBuilder = (transaction: Transaction) => `${transaction.TransactionGroup}-${transaction.CollectiveId}`;
const groupedTransactions: Record<string, Transaction[]> = groupBy(processorFeesTransactions, keyBuilder);
return transactions.map(transaction => {
if (transaction.paymentProcessorFeeInHostCurrency) {
return transaction.paymentProcessorFeeInHostCurrency;
Expand All @@ -96,9 +91,9 @@ export const generatePaymentProcessorFeeAmountForTransactionLoader = (): DataLoa
},
);

export const generateTaxAmountForTransactionLoader = (): DataLoader<TransactionInterface, number> =>
export const generateTaxAmountForTransactionLoader = (): DataLoader<Transaction, number> =>
new DataLoader(
async (transactions: TransactionInterface[]) => {
async (transactions: Transaction[]) => {
const transactionsThatMayHaveSeparateTaxes = transactions.filter(transaction => {
return !transaction.taxAmount && Transaction.canHaveFees(transaction);
});
Expand All @@ -116,9 +111,8 @@ export const generateTaxAmountForTransactionLoader = (): DataLoader<TransactionI
},
});

const keyBuilder = (transaction: TransactionInterface) =>
`${transaction.TransactionGroup}-${transaction.CollectiveId}`;
const groupedTransactions: Record<string, TransactionInterface[]> = groupBy(taxTransactions, keyBuilder);
const keyBuilder = (transaction: Transaction) => `${transaction.TransactionGroup}-${transaction.CollectiveId}`;
const groupedTransactions: Record<string, Transaction[]> = groupBy(taxTransactions, keyBuilder);
return transactions.map(transaction => {
if (transaction.taxAmount) {
return transaction.taxAmount;
Expand All @@ -138,9 +132,9 @@ export const generateTaxAmountForTransactionLoader = (): DataLoader<TransactionI
},
);

export const generateRelatedTransactionsLoader = (): DataLoader<TransactionInterface, TransactionInterface[]> =>
export const generateRelatedTransactionsLoader = (): DataLoader<Transaction, Transaction[]> =>
new DataLoader(
async (transactions: TransactionInterface[]) => {
async (transactions: Transaction[]) => {
const transactionGroups = transactions.map(transaction => transaction.TransactionGroup);
const relatedTransactions = await models.Transaction.findAll({ where: { TransactionGroup: transactionGroups } });
const groupedTransactions = groupBy(relatedTransactions, 'TransactionGroup');
Expand Down
4 changes: 2 additions & 2 deletions server/graphql/v2/collection/TransactionCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { GraphQLList, GraphQLNonNull, GraphQLObjectType } from 'graphql';

import { PAYMENT_METHOD_TYPE } from '../../../constants/paymentMethods';
import { TransactionKind } from '../../../constants/transaction-kind';
import { TransactionInterface } from '../../../models/Transaction';
import Transaction from '../../../models/Transaction';
import { GraphQLPaymentMethodType } from '../enum/PaymentMethodType';
import { GraphQLTransactionKind } from '../enum/TransactionKind';
import { CollectionFields, GraphQLCollection } from '../interface/Collection';
Expand Down Expand Up @@ -31,7 +31,7 @@ type AnyTransactionKind = TransactionKind | `${TransactionKind}`;
type AnyPaymentMethodType = PAYMENT_METHOD_TYPE | `${PAYMENT_METHOD_TYPE}`;

export interface GraphQLTransactionsCollectionReturnType {
nodes: TransactionInterface[] | Promise<TransactionInterface[]> | (() => Promise<TransactionInterface[]>);
nodes: Transaction[] | Promise<Transaction[]> | (() => Promise<Transaction[]>);
totalCount: number | Promise<number> | (() => Promise<number>);
limit: number;
offset: number;
Expand Down
4 changes: 2 additions & 2 deletions server/graphql/v2/input/TransactionReferenceInput.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GraphQLInputObjectType, GraphQLInt, GraphQLString } from 'graphql';

import models from '../../../models';
import { TransactionInterface } from '../../../models/Transaction';
import Transaction from '../../../models/Transaction';
import { NotFound } from '../../errors';

const GraphQLTransactionReferenceInput = new GraphQLInputObjectType({
Expand All @@ -24,7 +24,7 @@ const GraphQLTransactionReferenceInput = new GraphQLInputObjectType({
const fetchTransactionWithReference = async (
input: { id?: string; legacyId?: number },
{ loaders = null, throwIfMissing = false } = {},
): Promise<TransactionInterface> => {
): Promise<Transaction> => {
let transaction = null;
if (input.id) {
transaction = await models.Transaction.findOne({ where: { uuid: input.id } });
Expand Down
4 changes: 2 additions & 2 deletions server/graphql/v2/mutation/TransactionMutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import orderStatus from '../../../constants/order-status';
import { purgeCacheForCollective } from '../../../lib/cache';
import twoFactorAuthLib from '../../../lib/two-factor-authentication';
import models from '../../../models';
import { TransactionInterface } from '../../../models/Transaction';
import Transaction from '../../../models/Transaction';
import { checkRemoteUserCanUseTransactions } from '../../common/scope-check';
import { canReject, refundTransaction } from '../../common/transactions';
import { Forbidden, NotFound } from '../../errors';
Expand Down Expand Up @@ -43,7 +43,7 @@ const transactionMutations = {
description: 'Message to send to the contributor whose contribution has been rejected',
},
},
async resolve(_: void, args, req: express.Request): Promise<TransactionInterface> {
async resolve(_: void, args, req: express.Request): Promise<Transaction> {
checkRemoteUserCanUseTransactions(req);

// get transaction info
Expand Down
38 changes: 15 additions & 23 deletions server/lib/payments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ import Order from '../models/Order';
import PaymentMethod, { PaymentMethodModelInterface } from '../models/PaymentMethod';
import PayoutMethod, { PayoutMethodTypes } from '../models/PayoutMethod';
import Subscription from '../models/Subscription';
import Transaction, {
TransactionCreationAttributes,
TransactionData,
TransactionInterface,
} from '../models/Transaction';
import Transaction, { TransactionCreationAttributes, TransactionData } from '../models/Transaction';
import TransactionSettlement, { TransactionSettlementStatus } from '../models/TransactionSettlement';
import User from '../models/User';
import paymentProviders from '../paymentProviders';
Expand Down Expand Up @@ -112,7 +108,7 @@ export function findPaymentMethodProvider(
export async function processOrder(
order: Order,
options: { isAddedFund?: boolean; invoiceTemplate?: string } = {},
): Promise<TransactionInterface | void> {
): Promise<Transaction | void> {
const paymentMethodProvider = findPaymentMethodProvider(order.paymentMethod);
if (get(paymentMethodProvider, 'features.waitToCharge') && !get(order, 'paymentMethod.paid')) {
return;
Expand All @@ -130,11 +126,7 @@ export async function processOrder(
* associated to the refund transaction as who performed the refund.
* @param {string} message a optional message to explain why the transaction is rejected
*/
export async function refundTransaction(
transaction: TransactionInterface,
user?: User,
message?: string,
): Promise<TransactionInterface> {
export async function refundTransaction(transaction: Transaction, user?: User, message?: string): Promise<Transaction> {
// Make sure to fetch PaymentMethod
// Fetch PaymentMethod even if it's deleted
if (!transaction.PaymentMethod && transaction.PaymentMethodId) {
Expand Down Expand Up @@ -185,7 +177,7 @@ export function calcFee(amount: number, fee: number): number {
}

export const buildRefundForTransaction = (
t: TransactionInterface,
t: Transaction,
user?: User,
data?: TransactionData,
refundedPaymentProcessorFee?: number,
Expand Down Expand Up @@ -265,7 +257,7 @@ export const buildRefundForTransaction = (
};

export const refundPaymentProcessorFeeToCollective = async (
transaction: TransactionInterface,
transaction: Transaction,
refundTransactionGroup: string,
data: { hostFeeMigration?: string } = {},
createdAt: Date = null,
Expand Down Expand Up @@ -315,7 +307,7 @@ export const refundPaymentProcessorFeeToCollective = async (
};

async function refundPaymentProcessorFee(
transaction: TransactionInterface,
transaction: Transaction,
user: User,
refundedPaymentProcessorFee: number,
transactionGroup: string,
Expand Down Expand Up @@ -375,7 +367,7 @@ async function refundPaymentProcessorFee(
}

export async function refundHostFee(
transaction: TransactionInterface,
transaction: Transaction,
user: User,
refundedPaymentProcessorFee: number,
transactionGroup: string,
Expand Down Expand Up @@ -432,7 +424,7 @@ export async function refundHostFee(
}

async function refundTax(
transaction: TransactionInterface,
transaction: Transaction,
user: User,
transactionGroup: string,
clearedAt?: Date,
Expand Down Expand Up @@ -474,13 +466,13 @@ async function refundTax(
* transactions being created.
*/
export async function createRefundTransaction(
transaction: TransactionInterface,
transaction: Transaction,
refundedPaymentProcessorFee: number,
data: TransactionData,
user: User,
transactionGroupId?: string,
clearedAt?: Date,
): Promise<TransactionInterface> {
): Promise<Transaction> {
/* If the transaction passed isn't the one from the collective
* perspective, the opposite transaction is retrieved.
*
Expand Down Expand Up @@ -557,10 +549,10 @@ export async function createRefundTransaction(
}

export async function associateTransactionRefundId(
transaction: TransactionInterface,
refund: TransactionInterface,
transaction: Transaction,
refund: Transaction,
data?: TransactionData,
): Promise<TransactionInterface> {
): Promise<Transaction> {
const transactions = await Transaction.findAll({
order: ['id'],
where: {
Expand Down Expand Up @@ -615,7 +607,7 @@ export async function associateTransactionRefundId(
*
*/

export const sendEmailNotifications = (order: Order, transaction?: TransactionInterface | void): void => {
export const sendEmailNotifications = (order: Order, transaction?: Transaction | void): void => {
debug('sendEmailNotifications');
if (
transaction &&
Expand Down Expand Up @@ -773,7 +765,7 @@ const validatePayment = (payment): void => {
}
};

const sendOrderConfirmedEmail = async (order: Order, transaction: TransactionInterface): Promise<void> => {
const sendOrderConfirmedEmail = async (order: Order, transaction: Transaction): Promise<void> => {
const attachments = [];
const { collective, interval, fromCollective, paymentMethod } = order;
const user = await order.getUserForActivity();
Expand Down

0 comments on commit a463dff

Please sign in to comment.