-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #192 from autentia/origin/feature/subcontracted_ac…
…tivities Origin/feature/subcontracted activities
- Loading branch information
Showing
77 changed files
with
3,066 additions
and
31 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
...features/binnacle/features/activity/application/create-subcontracted-activity-cmd.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { mock } from 'jest-mock-extended' | ||
import { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
import { NewSubcontractedActivity } from '../domain/new-subcontracted-activity' | ||
import { CreateSubcontractedActivityCmd } from './create-subcontracted-activity-cmd' | ||
|
||
describe('CreateSubcontractedActivityCmd', () => { | ||
it('should create a new subcontracted activity', async () => { | ||
const { | ||
createSubcontractedActivityCmd, | ||
subcontractedActivityRepository, | ||
newSubcontractedActivity | ||
} = setup() | ||
|
||
await createSubcontractedActivityCmd.internalExecute(newSubcontractedActivity) | ||
|
||
expect(subcontractedActivityRepository.create).toBeCalledWith(newSubcontractedActivity) | ||
}) | ||
}) | ||
|
||
function setup() { | ||
const subcontractedActivityRepository = mock<SubcontractedActivityRepository>() | ||
|
||
const newSubcontractedActivity: NewSubcontractedActivity = { | ||
description: 'any-description', | ||
projectRoleId: 1, | ||
duration: 333, | ||
month: '2024-05' | ||
} | ||
|
||
return { | ||
createSubcontractedActivityCmd: new CreateSubcontractedActivityCmd( | ||
subcontractedActivityRepository | ||
), | ||
subcontractedActivityRepository, | ||
newSubcontractedActivity | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/features/binnacle/features/activity/application/create-subcontracted-activity-cmd.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Command, UseCaseKey } from '@archimedes/arch' | ||
import { SUBCONTRACTED_ACTIVITY_REPOSITORY } from '../../../../../shared/di/container-tokens' | ||
import { inject, singleton } from 'tsyringe' | ||
import { NewSubcontractedActivity } from '../domain/new-subcontracted-activity' | ||
import type { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
|
||
@UseCaseKey('CreateSubcontractedActivityCmd') | ||
@singleton() | ||
export class CreateSubcontractedActivityCmd extends Command<NewSubcontractedActivity> { | ||
constructor( | ||
@inject(SUBCONTRACTED_ACTIVITY_REPOSITORY) | ||
private subcontractedActivityRepository: SubcontractedActivityRepository | ||
) { | ||
super() | ||
} | ||
async internalExecute(newActivity: NewSubcontractedActivity): Promise<void> { | ||
await this.subcontractedActivityRepository.create(newActivity) | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...features/binnacle/features/activity/application/delete-subcontracted-activity-cmd.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { mock } from 'jest-mock-extended' | ||
import { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
import { DeleteSubcontractedActivityCmd } from './delete-subcontracted-activity-cmd' | ||
|
||
describe('DeleteSubcontractedActivityCmd', () => { | ||
it('should delete a subcontracted activity by id', async () => { | ||
const { deleteSubcontractedActivityCmd, subcontractedActivityRepository } = setup() | ||
const id = 1 | ||
|
||
await deleteSubcontractedActivityCmd.internalExecute(id) | ||
|
||
expect(subcontractedActivityRepository.delete).toBeCalledWith(id) | ||
}) | ||
}) | ||
|
||
function setup() { | ||
const subcontractedActivityRepository = mock<SubcontractedActivityRepository>() | ||
|
||
return { | ||
deleteSubcontractedActivityCmd: new DeleteSubcontractedActivityCmd( | ||
subcontractedActivityRepository | ||
), | ||
subcontractedActivityRepository | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/features/binnacle/features/activity/application/delete-subcontracted-activity-cmd.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Command, UseCaseKey } from '@archimedes/arch' | ||
import { SUBCONTRACTED_ACTIVITY_REPOSITORY } from '../../../../../shared/di/container-tokens' | ||
import { Id } from '../../../../../shared/types/id' | ||
import { inject, singleton } from 'tsyringe' | ||
import type { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
|
||
@UseCaseKey('DeleteSubcontractedActivityCmd') | ||
@singleton() | ||
export class DeleteSubcontractedActivityCmd extends Command<Id> { | ||
constructor( | ||
@inject(SUBCONTRACTED_ACTIVITY_REPOSITORY) | ||
private subcontractedActivityRepository: SubcontractedActivityRepository | ||
) { | ||
super() | ||
} | ||
|
||
async internalExecute(id: Id): Promise<void> { | ||
await this.subcontractedActivityRepository.delete(id) | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
src/features/binnacle/features/activity/application/get-subcontracted-activities-qry.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { GetUserLoggedQry } from '../../../../shared/user/application/get-user-logged-qry' | ||
import { mock } from 'jest-mock-extended' | ||
import { chrono } from '../../../../../shared/utils/chrono' | ||
import { SubcontractedActivityMother } from '../../../../../test-utils/mothers/subcontracted-activity-mother' | ||
import { SearchMother } from '../../../../../test-utils/mothers/search-mother' | ||
import { SearchProjectRolesQry } from '../../search/application/search-project-roles-qry' | ||
import { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
import { SubcontractedActivitiesWithRoleInformation } from '../domain/services/subcontracted-activities-with-role-information' | ||
import { GetSubcontractedActivitiesQry } from './get-subcontracted-activities-qry' | ||
import { UserMother } from '../../../../../test-utils/mothers/user-mother' | ||
|
||
describe('GetSubcontractedActivitiesQry', () => { | ||
it('should return subcontracted activities sorted by the given interval', async () => { | ||
const { getSubcontractedActivitiesQry, interval, subcontractedActivities } = setup() | ||
|
||
const result = await getSubcontractedActivitiesQry.internalExecute(interval) | ||
|
||
expect(result).toEqual(subcontractedActivities) | ||
}) | ||
}) | ||
|
||
function setup() { | ||
const subcontractedActivityRepository = mock<SubcontractedActivityRepository>() | ||
const searchProjectRolesQry = mock<SearchProjectRolesQry>() | ||
const getUserLoggedQry = mock<GetUserLoggedQry>() | ||
|
||
const interval = { | ||
start: new Date('2024-05'), | ||
end: new Date('2024-07') | ||
} | ||
|
||
const user = UserMother.userWithoutRoles() | ||
getUserLoggedQry.execute.mockResolvedValue(user) | ||
|
||
const activitiesResponse = [ | ||
SubcontractedActivityMother.minutesActivityWithProjectRoleIdA(), | ||
SubcontractedActivityMother.minutesBillableActivityWithProjectRoleId() | ||
] | ||
subcontractedActivityRepository.getAll | ||
.calledWith(interval, 1) | ||
.mockResolvedValue(activitiesResponse) | ||
|
||
const projectRolesInformation = SearchMother.roles() | ||
searchProjectRolesQry.execute.mockResolvedValue(projectRolesInformation) | ||
|
||
const subcontractedActivities = SubcontractedActivityMother.subcontractedActivities() | ||
subcontractedActivities.sort((a, b) => | ||
chrono(new Date(a.month)).isAfter(new Date(b.month)) ? 1 : -1 | ||
) | ||
|
||
return { | ||
getSubcontractedActivitiesQry: new GetSubcontractedActivitiesQry( | ||
subcontractedActivityRepository, | ||
searchProjectRolesQry, | ||
new SubcontractedActivitiesWithRoleInformation(), | ||
getUserLoggedQry | ||
), | ||
subcontractedActivityRepository, | ||
searchProjectRolesQry, | ||
interval, | ||
subcontractedActivities | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
src/features/binnacle/features/activity/application/get-subcontracted-activities-qry.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Query, UseCaseKey } from '@archimedes/arch' | ||
import { GetUserLoggedQry } from '../../../../shared/user/application/get-user-logged-qry' | ||
import { SUBCONTRACTED_ACTIVITY_REPOSITORY } from '../../../../../shared/di/container-tokens' | ||
import { DateInterval } from '../../../../../shared/types/date-interval' | ||
import { chrono } from '../../../../../shared/utils/chrono' | ||
import { inject, singleton } from 'tsyringe' | ||
import { SearchProjectRolesQry } from '../../search/application/search-project-roles-qry' | ||
import { SubcontractedActivity } from '../domain/subcontracted-activity' | ||
import type { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
import { SubcontractedActivitiesWithRoleInformation } from '../domain/services/subcontracted-activities-with-role-information' | ||
|
||
@UseCaseKey('GetSubcontractedActivitiesQry') | ||
@singleton() | ||
export class GetSubcontractedActivitiesQry extends Query<SubcontractedActivity[], DateInterval> { | ||
constructor( | ||
@inject(SUBCONTRACTED_ACTIVITY_REPOSITORY) | ||
private readonly subcontractedActivityRepository: SubcontractedActivityRepository, | ||
private readonly searchProjectRolesQry: SearchProjectRolesQry, | ||
private readonly subcontractedActivitiesWithRoleInformation: SubcontractedActivitiesWithRoleInformation, | ||
private readonly getUserLoggedQry: GetUserLoggedQry | ||
) { | ||
super() | ||
} | ||
|
||
async internalExecute(dateInterval: DateInterval): Promise<SubcontractedActivity[]> { | ||
const { id } = await this.getUserLoggedQry.execute() | ||
const activitiesResponse = await this.subcontractedActivityRepository.getAll(dateInterval, id) | ||
const activitiesSorted = activitiesResponse.slice() | ||
activitiesSorted.sort((a, b) => (chrono(new Date(a.month)).isAfter(new Date(b.month)) ? 1 : -1)) | ||
|
||
const projectRoleIds = activitiesSorted.map((a) => a.projectRoleId) | ||
const uniqueProjectRoleIds = Array.from(new Set(projectRoleIds)) | ||
const { start } = dateInterval | ||
|
||
const projectRolesInformation = await this.searchProjectRolesQry.execute({ | ||
ids: uniqueProjectRoleIds, | ||
year: start.getFullYear() | ||
}) | ||
|
||
return this.subcontractedActivitiesWithRoleInformation.addRoleInformationToActivities( | ||
activitiesSorted, | ||
projectRolesInformation | ||
) | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
...features/binnacle/features/activity/application/update-subcontracted-activity-cmd.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { mock } from 'jest-mock-extended' | ||
import { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
import { UpdateSubcontractedActivity } from '../domain/update-subcontracted-activity' | ||
import { UpdateSubcontractedActivityCmd } from './update-subcontracted-activity-cmd' | ||
|
||
describe('UpdateSubcontractedActivityCmd', () => { | ||
it('should update the subcontracted activity correctly', async () => { | ||
const { | ||
updateSubcontractedActivityCmd, | ||
subcontractedActivityRepository, | ||
updateSubcontractedActivity | ||
} = setup() | ||
|
||
await updateSubcontractedActivityCmd.internalExecute(updateSubcontractedActivity) | ||
|
||
expect(subcontractedActivityRepository.update).toBeCalledWith(updateSubcontractedActivity) | ||
}) | ||
}) | ||
|
||
function setup() { | ||
const subcontractedActivityRepository = mock<SubcontractedActivityRepository>() | ||
|
||
const updateSubcontractedActivity: UpdateSubcontractedActivity = { | ||
id: 1, | ||
description: 'Minutes activity', | ||
projectRoleId: 1, | ||
duration: 555, | ||
month: '2024-05' | ||
} | ||
|
||
return { | ||
updateSubcontractedActivityCmd: new UpdateSubcontractedActivityCmd( | ||
subcontractedActivityRepository | ||
), | ||
subcontractedActivityRepository, | ||
updateSubcontractedActivity | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/features/binnacle/features/activity/application/update-subcontracted-activity-cmd.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Command, UseCaseKey } from '@archimedes/arch' | ||
import { SUBCONTRACTED_ACTIVITY_REPOSITORY } from '../../../../../shared/di/container-tokens' | ||
import { inject, singleton } from 'tsyringe' | ||
import { UpdateSubcontractedActivity } from '../domain/update-subcontracted-activity' | ||
import type { SubcontractedActivityRepository } from '../domain/subcontracted-activity-repository' | ||
|
||
@UseCaseKey('UpdateSubcontractedActivityCmd') | ||
@singleton() | ||
export class UpdateSubcontractedActivityCmd extends Command<UpdateSubcontractedActivity> { | ||
constructor( | ||
@inject(SUBCONTRACTED_ACTIVITY_REPOSITORY) | ||
private subcontractedActivityRepository: SubcontractedActivityRepository | ||
) { | ||
super() | ||
} | ||
|
||
async internalExecute(activity: UpdateSubcontractedActivity): Promise<void> { | ||
await this.subcontractedActivityRepository.update(activity) | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/features/binnacle/features/activity/domain/get-subcontracted-activities-query-params.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { Id } from '../../../../../shared/types/id' | ||
|
||
export interface GetSubcontractedActivitiesQueryParams { | ||
userId?: Id | ||
startDate: string | ||
endDate: string | ||
} |
6 changes: 6 additions & 0 deletions
6
src/features/binnacle/features/activity/domain/new-subcontracted-activity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { SubcontractedActivityWithProjectRoleId } from './subcontracted-activity-with-project-role-id' | ||
|
||
export type NewSubcontractedActivity = Pick< | ||
SubcontractedActivityWithProjectRoleId, | ||
'description' | 'projectRoleId' | 'duration' | 'month' | ||
> |
40 changes: 40 additions & 0 deletions
40
...nacle/features/activity/domain/services/subcontracted-activities-with-role-information.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { SearchProjectRolesResult } from '../../../search/domain/search-project-roles-result' | ||
import { singleton } from 'tsyringe' | ||
import { SubcontractedActivityWithProjectRoleId } from '../subcontracted-activity-with-project-role-id' | ||
import { SubcontractedActivity } from '../subcontracted-activity' | ||
|
||
@singleton() | ||
export class SubcontractedActivitiesWithRoleInformation { | ||
addRoleInformationToActivities( | ||
subcontractedActivitiesWithProjectRoleId: SubcontractedActivityWithProjectRoleId[], | ||
searchProjectRolesResult: SearchProjectRolesResult | ||
): SubcontractedActivity[] { | ||
return subcontractedActivitiesWithProjectRoleId | ||
.map((subcontractedActivityProjectRoleId) => { | ||
const { projectRoleId, ...subcontractedActivityDetails } = | ||
subcontractedActivityProjectRoleId | ||
|
||
const projectRole = searchProjectRolesResult.projectRoles.find( | ||
(p) => p.id === projectRoleId | ||
) | ||
|
||
const project = searchProjectRolesResult.projects.find( | ||
(p) => p.id === projectRole?.projectId | ||
) | ||
|
||
const organization = searchProjectRolesResult.organizations.find( | ||
(o) => o.id === project?.organizationId | ||
) | ||
|
||
if (!organization) return | ||
|
||
return { | ||
...subcontractedActivityDetails, | ||
organization, | ||
project, | ||
projectRole | ||
} as SubcontractedActivity | ||
}) | ||
.filter((x) => x !== undefined) as SubcontractedActivity[] | ||
} | ||
} |
Oops, something went wrong.