Skip to content

Commit 2191b1f

Browse files
authored
Merge pull request #2097 from bcgov/feature/ALCS-2044-2501
NOI Condition Cards - Backend
2 parents 093f8a5 + b687c44 commit 2191b1f

25 files changed

+1632
-389
lines changed

services/apps/alcs/src/alcs/board/board.controller.spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { BOARD_CODES } from './board.dto';
2121
import { Board } from './board.entity';
2222
import { BoardService } from './board.service';
2323
import { ApplicationDecisionConditionCardService } from '../application-decision/application-decision-condition/application-decision-condition-card/application-decision-condition-card.service';
24+
import { NoticeOfIntentDecisionConditionCardService } from '../notice-of-intent-decision/notice-of-intent-decision-condition/notice-of-intent-decision-condition-card/notice-of-intent-decision-condition-card.service';
2425

2526
describe('BoardController', () => {
2627
let controller: BoardController;
@@ -36,6 +37,7 @@ describe('BoardController', () => {
3637
let notificationService: DeepMocked<NotificationService>;
3738
let inquiryService: DeepMocked<InquiryService>;
3839
let applicationDecisionConditionCardService: DeepMocked<ApplicationDecisionConditionCardService>;
40+
let noticeOfIntentDecisionConditionCardService: DeepMocked<NoticeOfIntentDecisionConditionCardService>;
3941
let mockBoard;
4042

4143
beforeEach(async () => {
@@ -50,6 +52,7 @@ describe('BoardController', () => {
5052
notificationService = createMock();
5153
inquiryService = createMock();
5254
applicationDecisionConditionCardService = createMock();
55+
noticeOfIntentDecisionConditionCardService = createMock();
5356

5457
mockBoard = new Board({
5558
allowedCardTypes: [],
@@ -77,6 +80,8 @@ describe('BoardController', () => {
7780
inquiryService.mapToDtos.mockResolvedValue([]);
7881
applicationDecisionConditionCardService.getByBoard.mockResolvedValue([]);
7982
applicationDecisionConditionCardService.mapToBoardDtos.mockResolvedValue([]);
83+
noticeOfIntentDecisionConditionCardService.getByBoard.mockResolvedValue([]);
84+
noticeOfIntentDecisionConditionCardService.mapToBoardDtos.mockResolvedValue([]);
8085

8186
const module: TestingModule = await Test.createTestingModule({
8287
imports: [
@@ -120,6 +125,10 @@ describe('BoardController', () => {
120125
provide: ApplicationDecisionConditionCardService,
121126
useValue: applicationDecisionConditionCardService,
122127
},
128+
{
129+
provide: NoticeOfIntentDecisionConditionCardService,
130+
useValue: noticeOfIntentDecisionConditionCardService,
131+
},
123132
{
124133
provide: ClsService,
125134
useValue: {},
@@ -163,6 +172,7 @@ describe('BoardController', () => {
163172
expect(planningReferralService.getByBoard).toHaveBeenCalledTimes(0);
164173
expect(planningReferralService.mapToDtos).toHaveBeenCalledTimes(1);
165174
expect(applicationDecisionConditionCardService.getByBoard).toHaveBeenCalledTimes(0);
175+
expect(noticeOfIntentDecisionConditionCardService.getByBoard).toHaveBeenCalledTimes(0);
166176
});
167177

168178
it('should call through to planning review service if board supports planning reviews', async () => {

services/apps/alcs/src/alcs/board/board.controller.ts

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { BoardDto, MinimalBoardDto } from './board.dto';
2222
import { Board } from './board.entity';
2323
import { BoardService } from './board.service';
2424
import { ApplicationDecisionConditionCardService } from '../application-decision/application-decision-condition/application-decision-condition-card/application-decision-condition-card.service';
25+
import { NoticeOfIntentDecisionConditionCardService } from '../notice-of-intent-decision/notice-of-intent-decision-condition/notice-of-intent-decision-condition-card/notice-of-intent-decision-condition-card.service';
2526

2627
@ApiOAuth2(config.get<string[]>('KEYCLOAK.SCOPES'))
2728
@Controller('board')
@@ -39,6 +40,7 @@ export class BoardController {
3940
private notificationService: NotificationService,
4041
private inquiryService: InquiryService,
4142
private applicationDecisionConditionCardService: ApplicationDecisionConditionCardService,
43+
private noticeOfIntentDecisionConditionCardService: NoticeOfIntentDecisionConditionCardService,
4244
@InjectMapper() private autoMapper: Mapper,
4345
) {}
4446

@@ -102,6 +104,10 @@ export class BoardController {
102104
? await this.applicationDecisionConditionCardService.getByBoard(board.uuid)
103105
: [];
104106

107+
const noticeOfIntentDecisionConditions = allowedCodes.includes(CARD_TYPE.NOI_CON)
108+
? await this.noticeOfIntentDecisionConditionCardService.getByBoard(board.uuid)
109+
: [];
110+
105111
return {
106112
board: await this.autoMapper.mapAsync(board, Board, BoardDto),
107113
applications: await this.applicationService.mapToDtos(applications),
@@ -114,6 +120,9 @@ export class BoardController {
114120
inquiries: await this.inquiryService.mapToDtos(inquiries),
115121
applicationDecisionConditions:
116122
await this.applicationDecisionConditionCardService.mapToBoardDtos(applicationDecisionConditions),
123+
noticeOfIntentDecisionConditions: await this.noticeOfIntentDecisionConditionCardService.mapToBoardDtos(
124+
noticeOfIntentDecisionConditions,
125+
),
117126
};
118127
}
119128

services/apps/alcs/src/alcs/board/board.service.ts

+10
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,14 @@ export class BoardService {
170170

171171
return board;
172172
}
173+
174+
async getNoticeOfIntentDecisionConditionBoard() {
175+
const board = await this.boardRepository.findOne({ where: { code: 'noicon' }, relations: ['statuses'] });
176+
177+
if (!board) {
178+
throw new ServiceNotFoundException('NOI Condition Board not found');
179+
}
180+
181+
return board;
182+
}
173183
}

services/apps/alcs/src/alcs/card/card-type/card-type.entity.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export enum CARD_TYPE {
1313
NOTIFICATION = 'NOTI',
1414
INQUIRY = 'INQR',
1515
APP_CON = 'APPCON',
16+
NOI_CON = 'NOICON',
1617
}
1718

1819
@Entity({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { createMock, DeepMocked } from '@golevelup/nestjs-testing';
3+
import { NoticeOfIntentDecisionConditionCardController } from './notice-of-intent-decision-condition-card.controller';
4+
import { NoticeOfIntentDecisionConditionCardService } from './notice-of-intent-decision-condition-card.service';
5+
import { NoticeOfIntentModificationService } from '../../notice-of-intent-modification/notice-of-intent-modification.service';
6+
import { NoticeOfIntentDecisionV2Service } from '../../notice-of-intent-decision-v2/notice-of-intent-decision-v2.service';
7+
import { NoticeOfIntentDecisionConditionCard } from './notice-of-intent-decision-condition-card.entity';
8+
import { classes } from 'automapper-classes';
9+
import { ClsService } from 'nestjs-cls';
10+
import { mockKeyCloakProviders } from '../../../../../test/mocks/mockTypes';
11+
import { AutomapperModule } from 'automapper-nestjs';
12+
import { NoticeOfIntentDecisionProfile } from '../../../../common/automapper/notice-of-intent-decision.automapper.profile';
13+
import {
14+
CreateNoticeOfIntentDecisionConditionCardDto,
15+
NoticeOfIntentDecisionConditionCardBoardDto,
16+
NoticeOfIntentDecisionConditionCardDto,
17+
UpdateNoticeOfIntentDecisionConditionCardDto,
18+
} from './notice-of-intent-decision-condition-card.dto';
19+
20+
describe('NoticeOfIntentDecisionConditionCardController', () => {
21+
let controller: NoticeOfIntentDecisionConditionCardController;
22+
let mockService: DeepMocked<NoticeOfIntentDecisionConditionCardService>;
23+
let mockModificationService: DeepMocked<NoticeOfIntentModificationService>;
24+
let mockDecisionService: DeepMocked<NoticeOfIntentDecisionV2Service>;
25+
26+
beforeEach(async () => {
27+
mockService = createMock();
28+
mockModificationService = createMock();
29+
mockDecisionService = createMock();
30+
31+
const module: TestingModule = await Test.createTestingModule({
32+
imports: [
33+
AutomapperModule.forRoot({
34+
strategyInitializer: classes(),
35+
}),
36+
],
37+
controllers: [NoticeOfIntentDecisionConditionCardController],
38+
providers: [
39+
{
40+
provide: NoticeOfIntentDecisionConditionCardService,
41+
useValue: mockService,
42+
},
43+
{
44+
provide: NoticeOfIntentModificationService,
45+
useValue: mockModificationService,
46+
},
47+
{
48+
provide: NoticeOfIntentDecisionV2Service,
49+
useValue: mockDecisionService,
50+
},
51+
{
52+
provide: ClsService,
53+
useValue: {},
54+
},
55+
NoticeOfIntentDecisionProfile,
56+
...mockKeyCloakProviders,
57+
],
58+
}).compile();
59+
60+
controller = module.get<NoticeOfIntentDecisionConditionCardController>(
61+
NoticeOfIntentDecisionConditionCardController,
62+
);
63+
});
64+
65+
it('should be defined', () => {
66+
expect(controller).toBeDefined();
67+
});
68+
69+
it('should return a condition card', async () => {
70+
const uuid = 'example-uuid';
71+
const conditionCard = new NoticeOfIntentDecisionConditionCard();
72+
conditionCard.decision = { uuid: 'decision-uuid' } as any;
73+
mockService.get.mockResolvedValue(conditionCard);
74+
75+
const result = await controller.get(uuid);
76+
77+
expect(mockService.get).toHaveBeenCalledWith(uuid);
78+
expect(result).toBeInstanceOf(NoticeOfIntentDecisionConditionCardDto);
79+
});
80+
81+
it('should create a new condition card', async () => {
82+
const dto: CreateNoticeOfIntentDecisionConditionCardDto = {
83+
conditionsUuids: ['condition-uuid-1', 'condition-uuid-2'],
84+
decisionUuid: 'decision-uuid',
85+
cardStatusCode: 'status-code',
86+
};
87+
const conditionCard = new NoticeOfIntentDecisionConditionCard();
88+
conditionCard.decision = { uuid: 'decision-uuid' } as any;
89+
mockService.create.mockResolvedValue(conditionCard);
90+
91+
const result = await controller.create(dto);
92+
93+
expect(mockService.create).toHaveBeenCalledWith(dto);
94+
expect(result).toBeInstanceOf(NoticeOfIntentDecisionConditionCardDto);
95+
});
96+
97+
it('should update the condition card and return updated card', async () => {
98+
const uuid = 'example-uuid';
99+
const dto: UpdateNoticeOfIntentDecisionConditionCardDto = {
100+
conditionsUuids: ['condition-uuid-1', 'condition-uuid-2'],
101+
cardStatusCode: 'updated-status-code',
102+
};
103+
const conditionCard = new NoticeOfIntentDecisionConditionCard();
104+
conditionCard.decision = { uuid: 'decision-uuid' } as any;
105+
mockService.update.mockResolvedValue(conditionCard);
106+
107+
const result = await controller.update(uuid, dto);
108+
109+
expect(mockService.update).toHaveBeenCalledWith(uuid, dto);
110+
expect(result).toBeInstanceOf(NoticeOfIntentDecisionConditionCardDto);
111+
});
112+
113+
it('should return a condition card by board card uuid', async () => {
114+
const uuid = 'example-uuid';
115+
const conditionCard = new NoticeOfIntentDecisionConditionCard();
116+
conditionCard.decision = { uuid: 'decision-uuid', noticeOfIntent: { fileNumber: 'file-number' } } as any;
117+
118+
mockService.getByBoardCard.mockResolvedValue(conditionCard);
119+
mockModificationService.getByNoticeOfIntentDecisionUuid.mockResolvedValue([]);
120+
mockDecisionService.getDecisionOrder.mockResolvedValue(1);
121+
122+
const result = await controller.getByCardUuid(uuid);
123+
124+
expect(mockService.getByBoardCard).toHaveBeenCalledWith(uuid);
125+
expect(result).toBeInstanceOf(NoticeOfIntentDecisionConditionCardBoardDto);
126+
expect(result.fileNumber).toEqual('file-number');
127+
});
128+
129+
it('should return condition cards by application file number', async () => {
130+
const fileNumber = 'example-file-number';
131+
const conditionCard = new NoticeOfIntentDecisionConditionCard();
132+
conditionCard.decision = { uuid: 'decision-uuid' } as any;
133+
mockDecisionService.getForDecisionConditionCardsByFileNumber.mockResolvedValue([conditionCard]);
134+
135+
const result = await controller.getByApplicationFileNumber(fileNumber);
136+
137+
expect(mockDecisionService.getForDecisionConditionCardsByFileNumber).toHaveBeenCalledWith(fileNumber);
138+
expect(result).toBeInstanceOf(Array);
139+
expect(result[0]).toBeInstanceOf(NoticeOfIntentDecisionConditionCardDto);
140+
});
141+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { Body, Controller, Get, Param, Patch, Post, UseGuards } from '@nestjs/common';
2+
import { ApiOAuth2 } from '@nestjs/swagger';
3+
import * as config from 'config';
4+
import { RolesGuard } from '../../../../common/authorization/roles-guard.service';
5+
import { NoticeOfIntentDecisionConditionCardService } from './notice-of-intent-decision-condition-card.service';
6+
import { InjectMapper } from 'automapper-nestjs';
7+
import { Mapper } from 'automapper-core';
8+
import { UserRoles } from '../../../../common/authorization/roles.decorator';
9+
import { ROLES_ALLOWED_APPLICATIONS } from '../../../../common/authorization/roles';
10+
import {
11+
NoticeOfIntentDecisionConditionCardBoardDto,
12+
NoticeOfIntentDecisionConditionCardDto,
13+
CreateNoticeOfIntentDecisionConditionCardDto,
14+
UpdateNoticeOfIntentDecisionConditionCardDto,
15+
} from './notice-of-intent-decision-condition-card.dto';
16+
import { NoticeOfIntentDecisionConditionCard } from './notice-of-intent-decision-condition-card.entity';
17+
import { NoticeOfIntentModificationService } from '../../notice-of-intent-modification/notice-of-intent-modification.service';
18+
import { NoticeOfIntentDecisionV2Service } from '../../notice-of-intent-decision-v2/notice-of-intent-decision-v2.service';
19+
20+
@ApiOAuth2(config.get<string[]>('KEYCLOAK.SCOPES'))
21+
@Controller('notice-of-intent-decision-condition-card')
22+
@UseGuards(RolesGuard)
23+
export class NoticeOfIntentDecisionConditionCardController {
24+
constructor(
25+
private service: NoticeOfIntentDecisionConditionCardService,
26+
private noticeOfIntentModificationService: NoticeOfIntentModificationService,
27+
private noticeOfIntentDecisionService: NoticeOfIntentDecisionV2Service,
28+
@InjectMapper() private mapper: Mapper,
29+
) {}
30+
31+
@Get('/:uuid')
32+
@UserRoles(...ROLES_ALLOWED_APPLICATIONS)
33+
async get(@Param('uuid') uuid: string): Promise<NoticeOfIntentDecisionConditionCardDto> {
34+
const result = await this.service.get(uuid);
35+
36+
return await this.mapper.map(result, NoticeOfIntentDecisionConditionCard, NoticeOfIntentDecisionConditionCardDto);
37+
}
38+
39+
@Post('')
40+
@UserRoles(...ROLES_ALLOWED_APPLICATIONS)
41+
async create(
42+
@Body() dto: CreateNoticeOfIntentDecisionConditionCardDto,
43+
): Promise<NoticeOfIntentDecisionConditionCardDto> {
44+
const result = await this.service.create(dto);
45+
46+
return await this.mapper.map(result, NoticeOfIntentDecisionConditionCard, NoticeOfIntentDecisionConditionCardDto);
47+
}
48+
49+
@Patch('/:uuid')
50+
@UserRoles(...ROLES_ALLOWED_APPLICATIONS)
51+
async update(
52+
@Param('uuid') uuid: string,
53+
@Body() dto: UpdateNoticeOfIntentDecisionConditionCardDto,
54+
): Promise<NoticeOfIntentDecisionConditionCardDto> {
55+
const result = await this.service.update(uuid, dto);
56+
57+
return await this.mapper.map(result, NoticeOfIntentDecisionConditionCard, NoticeOfIntentDecisionConditionCardDto);
58+
}
59+
60+
@Get('/board-card/:uuid')
61+
@UserRoles(...ROLES_ALLOWED_APPLICATIONS)
62+
async getByCardUuid(@Param('uuid') uuid: string): Promise<NoticeOfIntentDecisionConditionCardBoardDto> {
63+
const result = await this.service.getByBoardCard(uuid);
64+
const dto = await this.mapper.map(
65+
result,
66+
NoticeOfIntentDecisionConditionCard,
67+
NoticeOfIntentDecisionConditionCardBoardDto,
68+
);
69+
dto.fileNumber = result.decision.noticeOfIntent.fileNumber;
70+
71+
const appModifications = await this.noticeOfIntentModificationService.getByNoticeOfIntentDecisionUuid(
72+
result.decision.uuid,
73+
);
74+
75+
dto.isModification = appModifications.length > 0;
76+
77+
const decisionOrder = await this.noticeOfIntentDecisionService.getDecisionOrder(
78+
result.decision.noticeOfIntent.fileNumber,
79+
result.decision.uuid,
80+
);
81+
dto.decisionOrder = decisionOrder;
82+
83+
return dto;
84+
}
85+
86+
@Get('/application/:fileNumber')
87+
@UserRoles(...ROLES_ALLOWED_APPLICATIONS)
88+
async getByApplicationFileNumber(
89+
@Param('fileNumber') fileNumber: string,
90+
): Promise<NoticeOfIntentDecisionConditionCardDto[]> {
91+
const conditionCards =
92+
await this.noticeOfIntentDecisionService.getForDecisionConditionCardsByFileNumber(fileNumber);
93+
const dtos: NoticeOfIntentDecisionConditionCardDto[] = [];
94+
for (const card of conditionCards) {
95+
const dto = await this.mapper.map(
96+
card,
97+
NoticeOfIntentDecisionConditionCard,
98+
NoticeOfIntentDecisionConditionCardDto,
99+
);
100+
dtos.push(dto);
101+
}
102+
return dtos;
103+
}
104+
}

0 commit comments

Comments
 (0)