Add client bonus access flag
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "User" ADD COLUMN "bonusProgramEnabled" BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
|
||||||
@@ -60,6 +60,7 @@ model User {
|
|||||||
email String @unique
|
email String @unique
|
||||||
fullName String
|
fullName String
|
||||||
role UserRole
|
role UserRole
|
||||||
|
bonusProgramEnabled Boolean @default(false)
|
||||||
companyId String?
|
companyId String?
|
||||||
company Company? @relation(fields: [companyId], references: [id])
|
company Company? @relation(fields: [companyId], references: [id])
|
||||||
counterpartyProfile CounterpartyProfile?
|
counterpartyProfile CounterpartyProfile?
|
||||||
|
|||||||
@@ -161,12 +161,14 @@ async function upsertClient(index) {
|
|||||||
update: {
|
update: {
|
||||||
fullName: fullNameForIndex(index),
|
fullName: fullNameForIndex(index),
|
||||||
role: 'CLIENT',
|
role: 'CLIENT',
|
||||||
|
bonusProgramEnabled: index % 2 === 1,
|
||||||
companyId: company.id,
|
companyId: company.id,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
email: buildClientEmail(index),
|
email: buildClientEmail(index),
|
||||||
fullName: fullNameForIndex(index),
|
fullName: fullNameForIndex(index),
|
||||||
role: 'CLIENT',
|
role: 'CLIENT',
|
||||||
|
bonusProgramEnabled: index % 2 === 1,
|
||||||
companyId: company.id,
|
companyId: company.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -104,11 +104,16 @@ const manager = await prisma.user.upsert({
|
|||||||
|
|
||||||
await prisma.user.upsert({
|
await prisma.user.upsert({
|
||||||
where: { email: clientEmail },
|
where: { email: clientEmail },
|
||||||
update: { fullName: 'Demo Client', companyId: company.id },
|
update: {
|
||||||
|
fullName: 'Demo Client',
|
||||||
|
companyId: company.id,
|
||||||
|
bonusProgramEnabled: true,
|
||||||
|
},
|
||||||
create: {
|
create: {
|
||||||
email: clientEmail,
|
email: clientEmail,
|
||||||
fullName: 'Demo Client',
|
fullName: 'Demo Client',
|
||||||
role: 'CLIENT',
|
role: 'CLIENT',
|
||||||
|
bonusProgramEnabled: true,
|
||||||
companyId: company.id,
|
companyId: company.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
177
src/resolvers.js
177
src/resolvers.js
@@ -148,10 +148,61 @@ function mapManagerReferralLink(link) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const managerUserInclude = {
|
||||||
|
messengerConnections: {
|
||||||
|
where: {
|
||||||
|
type: 'TELEGRAM',
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
take: 1,
|
||||||
|
},
|
||||||
|
counterpartyProfile: {
|
||||||
|
select: {
|
||||||
|
companyName: true,
|
||||||
|
inn: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
clientOrders: {
|
||||||
|
select: {
|
||||||
|
createdAt: true,
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
take: 1,
|
||||||
|
},
|
||||||
|
_count: {
|
||||||
|
select: {
|
||||||
|
clientOrders: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function mapManagerUser(user) {
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email,
|
||||||
|
fullName: user.fullName,
|
||||||
|
role: user.role,
|
||||||
|
bonusProgramEnabled: user.bonusProgramEnabled,
|
||||||
|
companyName: user.counterpartyProfile?.companyName ?? null,
|
||||||
|
inn: user.counterpartyProfile?.inn ?? null,
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
orderCount: user._count.clientOrders,
|
||||||
|
lastOrderAt: user.clientOrders[0]?.createdAt ?? null,
|
||||||
|
telegramConnection: user.messengerConnections[0] ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function createReferralBonusTransaction(prisma, order) {
|
async function createReferralBonusTransaction(prisma, order) {
|
||||||
const referralLink = await prisma.referralLink.findFirst({
|
const referralLink = await prisma.referralLink.findFirst({
|
||||||
where: {
|
where: {
|
||||||
refereeId: order.customerId,
|
refereeId: order.customerId,
|
||||||
|
referrer: {
|
||||||
|
bonusProgramEnabled: true,
|
||||||
|
},
|
||||||
|
referee: {
|
||||||
|
bonusProgramEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
referrer: {
|
referrer: {
|
||||||
@@ -1099,49 +1150,11 @@ export const resolvers = {
|
|||||||
const managedUsersWhere = await getManagedClientUserWhere(context.prisma, manager);
|
const managedUsersWhere = await getManagedClientUserWhere(context.prisma, manager);
|
||||||
const users = await context.prisma.user.findMany({
|
const users = await context.prisma.user.findMany({
|
||||||
where: managedUsersWhere,
|
where: managedUsersWhere,
|
||||||
include: {
|
include: managerUserInclude,
|
||||||
messengerConnections: {
|
|
||||||
where: {
|
|
||||||
type: 'TELEGRAM',
|
|
||||||
isActive: true,
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
take: 1,
|
|
||||||
},
|
|
||||||
counterpartyProfile: {
|
|
||||||
select: {
|
|
||||||
companyName: true,
|
|
||||||
inn: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
clientOrders: {
|
|
||||||
select: {
|
|
||||||
createdAt: true,
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
take: 1,
|
|
||||||
},
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
clientOrders: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
});
|
});
|
||||||
|
|
||||||
return users.map((user) => ({
|
return users.map(mapManagerUser);
|
||||||
id: user.id,
|
|
||||||
email: user.email,
|
|
||||||
fullName: user.fullName,
|
|
||||||
role: user.role,
|
|
||||||
companyName: user.counterpartyProfile?.companyName ?? null,
|
|
||||||
inn: user.counterpartyProfile?.inn ?? null,
|
|
||||||
createdAt: user.createdAt,
|
|
||||||
orderCount: user._count.clientOrders,
|
|
||||||
lastOrderAt: user.clientOrders[0]?.createdAt ?? null,
|
|
||||||
telegramConnection: user.messengerConnections[0] ?? null,
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
managerOrders: async (_, { status, customerId }, context) => {
|
managerOrders: async (_, { status, customerId }, context) => {
|
||||||
@@ -1197,7 +1210,11 @@ export const resolvers = {
|
|||||||
|
|
||||||
const [users, transactionsAgg, pendingWithdrawalsAgg] = await Promise.all([
|
const [users, transactionsAgg, pendingWithdrawalsAgg] = await Promise.all([
|
||||||
context.prisma.user.findMany({
|
context.prisma.user.findMany({
|
||||||
where: managedUsersWhere,
|
where: {
|
||||||
|
...managedUsersWhere,
|
||||||
|
role: 'CLIENT',
|
||||||
|
bonusProgramEnabled: true,
|
||||||
|
},
|
||||||
include: {
|
include: {
|
||||||
counterpartyProfile: {
|
counterpartyProfile: {
|
||||||
select: {
|
select: {
|
||||||
@@ -1241,6 +1258,7 @@ export const resolvers = {
|
|||||||
email: user.email,
|
email: user.email,
|
||||||
fullName: user.fullName,
|
fullName: user.fullName,
|
||||||
companyName: user.counterpartyProfile?.companyName ?? null,
|
companyName: user.counterpartyProfile?.companyName ?? null,
|
||||||
|
bonusProgramEnabled: user.bonusProgramEnabled,
|
||||||
balance: (tx?.balance ?? 0) - pendingWithdrawalAmount,
|
balance: (tx?.balance ?? 0) - pendingWithdrawalAmount,
|
||||||
pendingWithdrawalAmount,
|
pendingWithdrawalAmount,
|
||||||
transactionsCount: tx?.transactionsCount ?? 0,
|
transactionsCount: tx?.transactionsCount ?? 0,
|
||||||
@@ -1328,7 +1346,11 @@ export const resolvers = {
|
|||||||
const managedUsersWhere = await getManagedClientUserWhere(context.prisma, manager);
|
const managedUsersWhere = await getManagedClientUserWhere(context.prisma, manager);
|
||||||
|
|
||||||
const users = await context.prisma.user.findMany({
|
const users = await context.prisma.user.findMany({
|
||||||
where: managedUsersWhere,
|
where: {
|
||||||
|
...managedUsersWhere,
|
||||||
|
role: 'CLIENT',
|
||||||
|
bonusProgramEnabled: true,
|
||||||
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
email: true,
|
email: true,
|
||||||
@@ -2224,6 +2246,7 @@ export const resolvers = {
|
|||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
role: true,
|
role: true,
|
||||||
|
bonusProgramEnabled: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2235,6 +2258,10 @@ export const resolvers = {
|
|||||||
throw new Error('Referral links can only be created between client accounts.');
|
throw new Error('Referral links can only be created between client accounts.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (users.some((user) => !user.bonusProgramEnabled)) {
|
||||||
|
throw new Error('Bonus program must be enabled for both selected clients.');
|
||||||
|
}
|
||||||
|
|
||||||
const existingRefereeLink = await context.prisma.referralLink.findFirst({
|
const existingRefereeLink = await context.prisma.referralLink.findFirst({
|
||||||
where: {
|
where: {
|
||||||
refereeId: refereeUserId,
|
refereeId: refereeUserId,
|
||||||
@@ -2259,10 +2286,56 @@ export const resolvers = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setClientBonusProgramEnabled: async (_, { userId, enabled }, context) => {
|
||||||
|
const manager = requireManagerAccess(context);
|
||||||
|
await assertManagerCanAccessUser(context.prisma, manager, userId);
|
||||||
|
|
||||||
|
const existingUser = await context.prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new Error('User was not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingUser.role !== 'CLIENT') {
|
||||||
|
throw new Error('Bonus program can only be configured for client accounts.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await context.prisma.user.update({
|
||||||
|
where: { id: userId },
|
||||||
|
data: {
|
||||||
|
bonusProgramEnabled: enabled,
|
||||||
|
},
|
||||||
|
include: managerUserInclude,
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapManagerUser(user);
|
||||||
|
},
|
||||||
|
|
||||||
createBonusProgramLink: async (_, { userId }, context) => {
|
createBonusProgramLink: async (_, { userId }, context) => {
|
||||||
const manager = requireManagerAccess(context);
|
const manager = requireManagerAccess(context);
|
||||||
await assertManagerCanAccessUser(context.prisma, manager, userId);
|
await assertManagerCanAccessUser(context.prisma, manager, userId);
|
||||||
|
|
||||||
|
const user = await context.prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
bonusProgramEnabled: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('User was not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.role !== 'CLIENT' || !user.bonusProgramEnabled) {
|
||||||
|
throw new Error('Bonus program is not enabled for this client.');
|
||||||
|
}
|
||||||
|
|
||||||
const issued = issueBonusProgramLinkToken({ userId });
|
const issued = issueBonusProgramLinkToken({ userId });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -2276,6 +2349,22 @@ export const resolvers = {
|
|||||||
addBonusTransaction: async (_, { input }, context) => {
|
addBonusTransaction: async (_, { input }, context) => {
|
||||||
const manager = requireManagerAccess(context);
|
const manager = requireManagerAccess(context);
|
||||||
await assertManagerCanAccessUser(context.prisma, manager, input.userId);
|
await assertManagerCanAccessUser(context.prisma, manager, input.userId);
|
||||||
|
const bonusUser = await context.prisma.user.findUnique({
|
||||||
|
where: { id: input.userId },
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
bonusProgramEnabled: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!bonusUser) {
|
||||||
|
throw new Error('User was not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bonusUser.role !== 'CLIENT' || !bonusUser.bonusProgramEnabled) {
|
||||||
|
throw new Error('Bonus program is not enabled for this client.');
|
||||||
|
}
|
||||||
|
|
||||||
const transaction = await context.prisma.bonusTransaction.create({
|
const transaction = await context.prisma.bonusTransaction.create({
|
||||||
data: {
|
data: {
|
||||||
userId: input.userId,
|
userId: input.userId,
|
||||||
@@ -2319,6 +2408,10 @@ export const resolvers = {
|
|||||||
|
|
||||||
requestRewardWithdrawal: (_, { input }, context) => {
|
requestRewardWithdrawal: (_, { input }, context) => {
|
||||||
const client = requireUser(context);
|
const client = requireUser(context);
|
||||||
|
if (!client.bonusProgramEnabled) {
|
||||||
|
throw new Error('Bonus program is not enabled for this client.');
|
||||||
|
}
|
||||||
|
|
||||||
if (input.amount < 100) {
|
if (input.amount < 100) {
|
||||||
throw new Error('Minimum withdrawal amount is 100.');
|
throw new Error('Minimum withdrawal amount is 100.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ type User {
|
|||||||
email: String!
|
email: String!
|
||||||
fullName: String!
|
fullName: String!
|
||||||
role: UserRole!
|
role: UserRole!
|
||||||
|
bonusProgramEnabled: Boolean!
|
||||||
company: Company
|
company: Company
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +143,7 @@ type ManagerUser {
|
|||||||
email: String!
|
email: String!
|
||||||
fullName: String!
|
fullName: String!
|
||||||
role: UserRole!
|
role: UserRole!
|
||||||
|
bonusProgramEnabled: Boolean!
|
||||||
companyName: String
|
companyName: String
|
||||||
inn: String
|
inn: String
|
||||||
createdAt: DateTime!
|
createdAt: DateTime!
|
||||||
@@ -382,6 +384,7 @@ type ManagerBonusBalance {
|
|||||||
email: String!
|
email: String!
|
||||||
fullName: String!
|
fullName: String!
|
||||||
companyName: String
|
companyName: String
|
||||||
|
bonusProgramEnabled: Boolean!
|
||||||
balance: Float!
|
balance: Float!
|
||||||
pendingWithdrawalAmount: Float!
|
pendingWithdrawalAmount: Float!
|
||||||
transactionsCount: Int!
|
transactionsCount: Int!
|
||||||
@@ -607,6 +610,7 @@ type Mutation {
|
|||||||
clientReviewOrder(orderId: ID!, decision: Decision!): Order!
|
clientReviewOrder(orderId: ID!, decision: Decision!): Order!
|
||||||
|
|
||||||
createReferral(input: CreateReferralInput!): ReferralLink!
|
createReferral(input: CreateReferralInput!): ReferralLink!
|
||||||
|
setClientBonusProgramEnabled(userId: ID!, enabled: Boolean!): ManagerUser!
|
||||||
createBonusProgramLink(userId: ID!): BonusProgramLink!
|
createBonusProgramLink(userId: ID!): BonusProgramLink!
|
||||||
addBonusTransaction(input: AddBonusTransactionInput!): BonusTransaction!
|
addBonusTransaction(input: AddBonusTransactionInput!): BonusTransaction!
|
||||||
requestRewardWithdrawal(input: RequestRewardWithdrawalInput!): RewardWithdrawalRequest!
|
requestRewardWithdrawal(input: RequestRewardWithdrawalInput!): RewardWithdrawalRequest!
|
||||||
|
|||||||
Reference in New Issue
Block a user