Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 그룹 포트폴리오 활성화 구현 #48

Merged
merged 2 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class EnablePortfolioCommand {
constructor(
readonly groupId: string,
readonly requesterUserId: string,
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Test } from '@nestjs/testing';
import { advanceTo, clear } from 'jest-date-mock';

import { EnablePortfolioCommand } from '@sight/app/application/group/command/enablePortfolio/EnablePortfolioCommand';
import { EnablePortfolioCommandHandler } from '@sight/app/application/group/command/enablePortfolio/EnablePortfolioCommandHandler';

import { Group } from '@sight/app/domain/group/model/Group';
import {
GroupRepository,
IGroupRepository,
} from '@sight/app/domain/group/IGroupRepository';

import { DomainFixture } from '@sight/__test__/fixtures';
import { Message } from '@sight/constant/message';

describe('EnablePortfolioCommandHandler', () => {
let handler: EnablePortfolioCommandHandler;
let groupRepository: jest.Mocked<IGroupRepository>;

beforeAll(async () => {
advanceTo(new Date());

const testModule = await Test.createTestingModule({
providers: [
EnablePortfolioCommandHandler,
{ provide: GroupRepository, useValue: {} },
],
}).compile();

handler = testModule.get(EnablePortfolioCommandHandler);
groupRepository = testModule.get(GroupRepository);
});

afterAll(() => {
clear();
});

describe('handle', () => {
let group: Group;

const groupId = 'groupId';
const groupAdminUserId = 'groupAdminUserId';

beforeEach(() => {
group = DomainFixture.generateGroup({
adminUserId: groupAdminUserId,
hasPortfolio: false,
});

groupRepository.findById = jest.fn().mockResolvedValue(group);

groupRepository.save = jest.fn();
});

test('그룹이 존재하지 않는다면 예외를 발생시켜야 한다', async () => {
groupRepository.findById = jest.fn().mockResolvedValue(null);

await expect(
handler.execute(new EnablePortfolioCommand(groupId, groupAdminUserId)),
).rejects.toThrowError(Message.GROUP_NOT_FOUND);
});

test('요청자가 그룹장이 아니라면 예외를 발생시켜야 한다', async () => {
const otherUserId = 'otherUserId';

await expect(
handler.execute(new EnablePortfolioCommand(groupId, otherUserId)),
).rejects.toThrowError(Message.ONLY_GROUP_ADMIN_CAN_EDIT_GROUP);
});

test('그룹이 고객센터 그룹이라면 예외를 발생시켜야 한다', async () => {
jest.spyOn(group, 'isCustomerServiceGroup').mockReturnValue(true);

await expect(
handler.execute(new EnablePortfolioCommand(groupId, groupAdminUserId)),
).rejects.toThrowError(Message.CANNOT_MODIFY_CUSTOMER_SERVICE_GROUP);
});

test('그룹의 포트폴리오를 활성화 시켜야 한다', async () => {
jest.spyOn(group, 'enablePortfolio');

await handler.execute(
new EnablePortfolioCommand(groupId, groupAdminUserId),
);

expect(group.enablePortfolio).toBeCalled();
});

test('그룹을 저장해야 한다', async () => {
await handler.execute(
new EnablePortfolioCommand(groupId, groupAdminUserId),
);

expect(groupRepository.save).toBeCalledWith(group);
expect(groupRepository.save).toBeCalledTimes(1);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import {
ForbiddenException,
Inject,
NotFoundException,
UnprocessableEntityException,
} from '@nestjs/common';

import { Transactional } from '@sight/core/persistence/transaction/Transactional';

import { EnablePortfolioCommand } from '@sight/app/application/group/command/enablePortfolio/EnablePortfolioCommand';

import {
GroupRepository,
IGroupRepository,
} from '@sight/app/domain/group/IGroupRepository';

import { Message } from '@sight/constant/message';

@CommandHandler(EnablePortfolioCommand)
export class EnablePortfolioCommandHandler
implements ICommandHandler<EnablePortfolioCommand>
{
constructor(
@Inject(GroupRepository)
private readonly groupRepository: IGroupRepository,

Check warning on line 26 in src/app/application/group/command/enablePortfolio/EnablePortfolioCommandHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
) {}

@Transactional()
async execute(command: EnablePortfolioCommand): Promise<void> {

Check warning on line 30 in src/app/application/group/command/enablePortfolio/EnablePortfolioCommandHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
const { groupId, requesterUserId } = command;

const group = await this.groupRepository.findById(groupId);
if (!group) {
throw new NotFoundException(Message.GROUP_NOT_FOUND);
}

if (group.adminUserId !== requesterUserId) {
throw new ForbiddenException(Message.ONLY_GROUP_ADMIN_CAN_EDIT_GROUP);
}

if (group.isCustomerServiceGroup()) {
throw new UnprocessableEntityException(
Message.CANNOT_MODIFY_CUSTOMER_SERVICE_GROUP,
);
}

group.enablePortfolio();
await this.groupRepository.save(group);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ describe('ModifyGroupCommandHandler', () => {

beforeEach(() => {
command = new ModifyGroupCommand(groupId, requesterUserId, params);
group = DomainFixture.generateGroup({ adminUserId: requesterUserId });
group = DomainFixture.generateGroup({
category: GroupCategory.STUDY,
adminUserId: requesterUserId,
});
const groupMember = DomainFixture.generateGroupMember();
const interests = params.interestIds.map((interestId) =>
DomainFixture.generateInterest({ id: interestId }),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// TODO: 핸들러의 구현 방향성이 잡히고, 리팩토링이 완료되면 테스트 작성 예정
describe('GroupPortfolioEnabledHandler', () => {
test.todo('그룹이 존재하지 않으면 아무것도 하지 않아야 한다');

test.todo('모든 그룹 멤버들에게 포인트를 지급해야 한다');

test.todo('그룹 로그를 생성해야 한다');

test.todo('메시지를 전송해야 한다');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Inject } from '@nestjs/common';
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';

import { Transactional } from '@sight/core/persistence/transaction/Transactional';

import { GroupPortfolioEnabled } from '@sight/app/domain/group/event/GroupPortfolioEnabled';
import { GroupLogFactory } from '@sight/app/domain/group/GroupLogFactory';
import { SlackMessageCategory } from '@sight/app/domain/message/model/constant';
import {
ISlackSender,
SlackSender,
} from '@sight/app/domain/adapter/ISlackSender';
import {
GroupLogRepository,
IGroupLogRepository,
} from '@sight/app/domain/group/IGroupLogRepository';
import {
GroupMemberRepository,
IGroupMemberRepository,
} from '@sight/app/domain/group/IGroupMemberRepository';
import {
GroupRepository,
IGroupRepository,
} from '@sight/app/domain/group/IGroupRepository';
import {
IUserRepository,
UserRepository,
} from '@sight/app/domain/user/IUserRepository';

import { Point } from '@sight/constant/point';

@EventsHandler(GroupPortfolioEnabled)
export class GroupPortfolioEnabledHandler

Check warning on line 33 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
implements IEventHandler<GroupPortfolioEnabled>
{
constructor(

Check warning on line 36 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
@Inject(GroupRepository)
private readonly groupRepository: IGroupRepository,

Check warning on line 38 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 38 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 38 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
@Inject(GroupMemberRepository)
private readonly groupMemberRepository: IGroupMemberRepository,

Check warning on line 40 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 40 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 40 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
@Inject(GroupLogFactory)
private readonly groupLogFactory: GroupLogFactory,

Check warning on line 42 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 42 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 42 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
@Inject(GroupLogRepository)
private readonly groupLogRepository: IGroupLogRepository,

Check warning on line 44 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 44 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 44 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
@Inject(UserRepository)
private readonly userRepository: IUserRepository,

Check warning on line 46 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 46 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 46 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
@Inject(SlackSender)
private readonly slackSender: ISlackSender,

Check warning on line 48 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 48 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 48 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
) {}

@Transactional()

Check warning on line 51 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
async handle(event: GroupPortfolioEnabled): Promise<void> {

Check warning on line 52 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 52 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
const { groupId } = event;

Check warning on line 53 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

const group = await this.groupRepository.findById(groupId);

Check warning on line 55 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
if (!group) {
return;

Check warning on line 57 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 58 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 58 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

const groupMembers =
await this.groupMemberRepository.findByGroupId(groupId);

Check warning on line 61 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
const userIds = groupMembers.map((groupMember) => groupMember.userId);

Check warning on line 62 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 62 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 62 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
const users = await this.userRepository.findByIds(userIds);

Check warning on line 63 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

users.forEach((user) =>

Check warning on line 65 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
user.grantPoint(
Point.GROUP_ENABLED_PORTFOLIO,
`<u>${group.title}</u> 그룹의 포트폴리오가 발행되었습니다.`,

Check warning on line 68 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 68 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
),
);
await this.userRepository.save(...users);

Check warning on line 71 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

// TODO: 그룹 로거를 만들어서 사용하도록 수정
const groupLog = this.groupLogFactory.create({
id: this.groupLogRepository.nextId(),
groupId,
userId: '', // TODO: 요청자 정보에 접근할 수 있을 때 수정
message: '포트폴리오가 발행 중입니다.',
});

Check warning on line 79 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
await this.groupLogRepository.save(groupLog);

Check warning on line 80 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

this.slackSender.send({
category: SlackMessageCategory.GROUP_ACTIVITY,
message: `<a href="/group/'${groupId}'"><u>${group.title}</u></a> 그룹의 <a href="/folio/'${groupId}'" target="_blank">포트폴리오</a>가 발행 중입니다.`,
sourceUserId: null,
targetUserId: '', // TODO: 요청자 정보에 접근할 수 있을 때 수정
});

Check warning on line 87 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}
}

Check warning on line 89 in src/app/application/group/eventHandler/GroupPortfolioEnabledHandler.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
3 changes: 3 additions & 0 deletions src/app/domain/group/event/GroupPortfolioEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class GroupPortfolioEnabled {
constructor(readonly groupId: string) {}
}
20 changes: 20 additions & 0 deletions src/app/domain/group/model/Group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ describe('Group', () => {
});
});

describe('enablePortfolio', () => {
test('포트폴리오를 활성화해야 한다', () => {
const group = DomainFixture.generateGroup({
hasPortfolio: false,
});

group.enablePortfolio();

expect(group.hasPortfolio).toEqual(true);
});

test('이미 포트폴리오가 활성화되어 있다면 예외를 발생시켜야 한다', () => {
const group = DomainFixture.generateGroup({
hasPortfolio: true,
});

expect(() => group.enablePortfolio()).toThrow();
});
});

describe('isEditable', () => {
test('그룹이 종료된 상태라면 false를 반환해야 한다', () => {
const group = DomainFixture.generateGroup({
Expand Down
18 changes: 18 additions & 0 deletions src/app/domain/group/model/Group.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { UnprocessableEntityException } from '@nestjs/common';
import { AggregateRoot } from '@nestjs/cqrs';

import { GroupPortfolioEnabled } from '@sight/app/domain/group/event/GroupPortfolioEnabled';
import { GroupStateChanged } from '@sight/app/domain/group/event/GroupStateChanged';
import { GroupUpdated } from '@sight/app/domain/group/event/GroupUpdated';
import {
Expand All @@ -9,6 +11,7 @@ import {
GroupState,
PRACTICE_GROUP_ID,
} from '@sight/app/domain/group/model/constant';
import { Message } from '@sight/constant/message';

import { isDifferentStringArray } from '@sight/util/isDifferentStringArray';

Expand Down Expand Up @@ -109,6 +112,7 @@ export class Group extends AggregateRoot {
updateGrade(grade: GroupAccessGrade): void {
if (this._grade !== grade) {
this._grade = grade;
this._updatedAt = new Date();
this.wake();
this.apply(new GroupUpdated(this.id, 'grade'));
}
Expand Down Expand Up @@ -155,7 +159,21 @@ export class Group extends AggregateRoot {
wake(): void {
if (this._state === GroupState.SUSPEND) {
this._state = GroupState.PROGRESS;
this._updatedAt = new Date();
}
}

enablePortfolio(): void {
if (this._hasPortfolio) {
throw new UnprocessableEntityException(
Message.ALREADY_GROUP_ENABLED_PORTFOLIO,
);
}

this._hasPortfolio = true;
this._updatedAt = new Date();

this.apply(new GroupPortfolioEnabled(this.id));
}

isEditable(): boolean {
Expand Down
2 changes: 2 additions & 0 deletions src/constant/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ export const Message = {
UNITED_USER_CAN_ONLY_CHANGE_EMAIL: 'United user can only change email',
GROUP_NOT_EDITABLE: 'Group is not editable',
CANNOT_MODIFY_CUSTOMER_SERVICE_GROUP: 'Cannot modify customer service group',
ALREADY_GROUP_ENABLED_PORTFOLIO: 'Already group enabled portfolio',
ALREADY_GROUP_DISABLED_PORTFOLIO: 'Already group disabled portfolio',
};
1 change: 1 addition & 0 deletions src/constant/point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export const Point = {
GROUP_CREATED: 20,
GROUP_ENDED_WITH_SUCCESS: 50,
GROUP_ENDED_WITH_FAIL: 30,
GROUP_ENABLED_PORTFOLIO: 10,
};