Skip to content

Commit

Permalink
Insert transactions endpoint (Transaction monitoring) (#2171)
Browse files Browse the repository at this point in the history
  • Loading branch information
MatanYadaev authored Mar 11, 2024
1 parent 6f00953 commit f1c8e7d
Show file tree
Hide file tree
Showing 39 changed files with 1,096 additions and 608 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "services/workflows-service/prisma/data-migrations"]
path = services/workflows-service/prisma/data-migrations
url = [email protected]:ballerine-io/wf-data-migration.git
ignore = dirty
ignore = all
11 changes: 2 additions & 9 deletions apps/backoffice-v2/src/domains/transactions/fetchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,7 @@ export const TransactionsListSchema = z.array(
transactionDirection: z.enum(TransactionDirections),
transactionBaseAmount: z.number(),
transactionBaseCurrency: z.string(),
businessId: z.string().nullable(),
business: z
.object({
companyName: z.string(),
})
.nullable(),

counterpartyOriginator: z
.object({
endUser: z
Expand All @@ -63,9 +58,8 @@ export const TransactionsListSchema = z.array(
.nullable(),
counterpartyOriginatorId: z.string().nullable(),
paymentMethod: z.enum(PaymentMethods),
}).transform(({ business, counterpartyOriginator, ...data }) => ({
}).transform(({ counterpartyOriginator, ...data }) => ({
...data,
business: business?.companyName,
counterpartyOriginatorName:
noNullish`${counterpartyOriginator?.endUser?.firstName} ${counterpartyOriginator?.endUser?.lastName}`.trim(),
transactionBaseAmountWithCurrency:
Expand All @@ -76,7 +70,6 @@ export const TransactionsListSchema = z.array(
export type TTransactionsList = z.output<typeof TransactionsListSchema>;

export const fetchTransactions = async (params: {
businessId: string;
counterpartyId: string;
page: {
number: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import { useQuery } from '@tanstack/react-query';
import { transactionsQueryKeys } from '@/domains/transactions/query-keys';

export const useTransactionsQuery = ({
businessId,
counterpartyId,
page,
pageSize,
}: {
businessId: string;
counterpartyId: string;
page: number;
pageSize: number;
Expand All @@ -17,12 +15,11 @@ export const useTransactionsQuery = ({

return useQuery({
...transactionsQueryKeys.list({
businessId,
counterpartyId,
page,
pageSize,
}),
enabled: isAuthenticated && (!!businessId || !!counterpartyId),
enabled: isAuthenticated && !!counterpartyId,
staleTime: 100_000,
});
};
1 change: 0 additions & 1 deletion apps/backoffice-v2/src/domains/transactions/query-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export const transactionsQueryKeys = createQueryKeys('transactions', {
pageSize,
...params
}: {
businessId: string;
counterpartyId: string;
page: number;
pageSize: number;
Expand Down
2 changes: 1 addition & 1 deletion services/workflows-service/prisma/data-migrations
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- Remove TransactionRecord unique correlationId
DROP INDEX "TransactionRecord_transactionCorrelationId_key";

-- Remove TransactionRecord<>EndUser/Business denormalization
ALTER TABLE "TransactionRecord" DROP CONSTRAINT "TransactionRecord_businessId_fkey";
ALTER TABLE "TransactionRecord" DROP CONSTRAINT "TransactionRecord_endUserId_fkey";
ALTER TABLE "TransactionRecord" DROP COLUMN "businessId", DROP COLUMN "endUserId";

-- Fix EndUser/Business unique correlationId
CREATE UNIQUE INDEX "Business_projectId_correlationId_key" ON "Business"("projectId", "correlationId");
CREATE UNIQUE INDEX "EndUser_projectId_correlationId_key" ON "EndUser"("projectId", "correlationId");
DROP INDEX "Business_correlationId_key";
DROP INDEX "EndUser_correlationId_key";

-- Drop unnecessary Counterparty type
ALTER TABLE "Counterparty" DROP COLUMN "type";
DROP TYPE "CounterpartyType";
34 changes: 10 additions & 24 deletions services/workflows-service/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ model EndUser {
id String @id @default(cuid())
isContactPerson Boolean @default(false)
correlationId String? @unique @db.VarChar
correlationId String? @db.VarChar
endUserType String? @default("individual") // Add userType: can be 'EndUser', 'CompanyContact', 'Stakeholder'
approvalState ApprovalState @default(NEW)
Expand All @@ -84,10 +84,10 @@ model EndUser {
projectId String
project Project @relation(fields: [projectId], references: [id])
WorkflowRuntimeDataToken WorkflowRuntimeDataToken[]
TransactionRecord TransactionRecord[]
Alert Alert[]
Counterparty Counterparty[]
@@unique([projectId, correlationId])
@@index([endUserType])
@@index([approvalState])
@@index([createdAt])
Expand All @@ -107,7 +107,7 @@ model EndUsersOnBusinesses {

model Business {
id String @id @default(cuid()) // Unique identifier for the business entity
correlationId String? @unique @db.VarChar
correlationId String? @db.VarChar
businessType String? @default("default")
createdAt DateTime @default(now()) // Timestamp for when the business entity was created
Expand Down Expand Up @@ -139,12 +139,12 @@ model Business {
endUsers EndUser[]
endUsersOnBusinesses EndUsersOnBusinesses[]
projectId String
project Project @relation(fields: [projectId], references: [id])
TransactionRecord TransactionRecord[]
Alert Alert[]
Counterparty Counterparty[]
projectId String
project Project @relation(fields: [projectId], references: [id])
Alert Alert[]
Counterparty Counterparty[]
@@unique([projectId, correlationId])
@@index([companyName])
@@index([approvalState])
@@index([correlationId])
Expand Down Expand Up @@ -522,7 +522,7 @@ enum TransactionDirection {

model TransactionRecord {
id String @id @default(cuid())
transactionCorrelationId String @unique @db.VarChar
transactionCorrelationId String @db.VarChar
transactionDate DateTime
transactionAmount Float // Amount of the transaction
transactionCurrency String // Currency of the transaction
Expand Down Expand Up @@ -597,12 +597,6 @@ model TransactionRecord {
counterpartyBeneficiaryId String?
counterpartyBeneficiary Counterparty? @relation("BenefitingCounterparty", fields: [counterpartyBeneficiaryId], references: [id])
endUser EndUser? @relation(fields: [endUserId], references: [id])
endUserId String?
business Business? @relation(fields: [businessId], references: [id])
businessId String?
@@unique([projectId, transactionCorrelationId])
@@index([transactionType])
@@index([transactionDate])
Expand Down Expand Up @@ -725,13 +719,6 @@ model Alert {
@@index([counterpartyId])
}

enum CounterpartyType {
individual
company
government
non_profit
}

enum RiskCategory {
low
medium
Expand Down Expand Up @@ -759,8 +746,7 @@ enum SanctionListMatchStatus {
model Counterparty {
id String @id @default(cuid())
correlationId String?
type CounterpartyType
correlationId String? // This column is a denormalized value of the EndUser.correlationId or Business.correlationId
additionalInfo Json? // Additional information about the counterparty
Expand Down
36 changes: 5 additions & 31 deletions services/workflows-service/scripts/alerts/generate-alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@prisma/client';
import { faker } from '@faker-js/faker';
import { AggregateType } from '../../src/data-analytics/consts';
import { InputJsonValue } from '@/types';

const tags = [
...new Set([
Expand Down Expand Up @@ -140,53 +141,26 @@ export const generateFakeAlertDefinition = async (
}: {
project: Project;
customer: Customer;
ids: Array<
| {
businessId: string;
}
| {
counterpartyOriginatorId: string;
}
| {
businessId: string;
counterpartyOriginatorId: string;
}
>;
ids: string[];
},
) => {
const generateFakeAlert = ({
defaultSeverity,
ids,
}: {
defaultSeverity: AlertSeverity;
ids: Array<
| {
businessId: string;
}
| {
counterpartyOriginatorId: string;
}
| {
businessId: string;
counterpartyOriginatorId: string;
}
>;
ids: string[];
}) => {
const businessIdCounterpartyOriginatorIdOrBoth = faker.helpers.arrayElement(
ids.map(id => ({
// @ts-expect-error
businessId: id.businessId,
// @ts-expect-error
counterpartyId: id.counterpartyOriginatorId,
})),
ids.map(id => ({ counterpartyId: id })),
);

return {
dataTimestamp: faker.date.past(),
state: faker.helpers.arrayElement(Object.values(AlertState)),
status: faker.helpers.arrayElement(Object.values(AlertStatus)),
tags: [faker.random.word(), faker.random.word(), faker.random.word()],
executionDetails: JSON.parse(faker.datatype.json()),
executionDetails: JSON.parse(faker.datatype.json()) as InputJsonValue,
severity: defaultSeverity,
...businessIdCounterpartyOriginatorIdOrBoth,
// TODO: Assign assigneeId value to a valid user id
Expand Down
66 changes: 40 additions & 26 deletions services/workflows-service/scripts/alerts/generate-transactions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
CounterpartyType,
Business,
EndUser,
PaymentAcquirer,
PaymentBrandName,
PaymentChannel,
Expand All @@ -15,29 +16,49 @@ import {
TransactionRecordType,
} from '@prisma/client';
import { faker } from '@faker-js/faker';
import { generateBusiness, generateEndUser } from '../generate-end-user';

export const generateTransactions = async (
prismaClient: PrismaClient,
{
projectId,
businessId,
}: {
projectId: string;
businessId: string;
},
) => {
// Create counterparties and collect their IDs
const counterpartyIds = await prismaClient.$transaction(async prisma => {
const ids: string[] = [];

for (let i = 0; i < 200; i++) {
for (let i = 0; i < 100; i++) {
const correlationId = faker.datatype.uuid();
const counterparty = await prisma.counterparty.create({
data: {
id: faker.datatype.uuid(),
correlationId: faker.datatype.uuid(),
type: faker.helpers.arrayElement(Object.values(CounterpartyType)),
businessId: businessId,
projectId: projectId,
correlationId: correlationId,
project: { connect: { id: projectId } },
business: {
create: generateBusiness({
correlationId,
projectId,
}),
},
},
});

ids.push(counterparty.id);
}
for (let i = 0; i < 100; i++) {
const correlationId = faker.datatype.uuid();
const counterparty = await prisma.counterparty.create({
data: {
correlationId: correlationId,
project: { connect: { id: projectId } },
endUser: {
create: generateEndUser({
correlationId,
projectId,
}),
},
},
});

Expand All @@ -47,32 +68,25 @@ export const generateTransactions = async (
return ids;
});

const ids: Array<
| {
businessId: string;
}
| {
counterpartyOriginatorId: string;
}
| {
businessId: string;
counterpartyOriginatorId: string;
}
> = [];
const ids: Array<{
counterpartyOriginatorId?: string;
counterpartyBeneficiaryId?: string;
}> = [];

// Create transactions with a random counterparty ID for each
for (let i = 0; i < 1000; i++) {
const randomCounterpartyId = faker.helpers.arrayElement(counterpartyIds);
const getRandomCounterpartyId = () => faker.helpers.arrayElement(counterpartyIds);
const businessIdCounterpartyIdOrBoth = faker.helpers.arrayElement([
{},
{
businessId,
counterpartyOriginatorId: randomCounterpartyId,
counterpartyOriginatorId: getRandomCounterpartyId(),
},
{
businessId,
counterpartyBeneficiaryId: getRandomCounterpartyId(),
},
{
counterpartyOriginatorId: randomCounterpartyId,
counterpartyOriginatorId: getRandomCounterpartyId(),
counterpartyBeneficiaryId: getRandomCounterpartyId(),
},
]);

Expand Down
Loading

0 comments on commit f1c8e7d

Please sign in to comment.