From da0260f651fe096fc1f4123c3598be1edd263fe7 Mon Sep 17 00:00:00 2001 From: oscar-richardson-softwire Date: Wed, 15 May 2024 09:22:49 +0100 Subject: [PATCH] feat(DTFS2-7121): add SIC code mapping to /companies endpoint --- ...or-get-company-by-registration-number.json | 60 +++++++++++++ .../companies/companies.controller.test.ts | 2 +- src/modules/companies/companies.module.ts | 3 +- .../companies/companies.service.test.ts | 24 ++++- src/modules/companies/companies.service.ts | 41 +++++++-- .../companies/dto/get-company-response.dto.ts | 13 ++- ...or-get-company-by-registration-number.json | 52 +++++++++++ .../sector-industries.module.ts | 1 + ...company-by-registration-number.api-test.ts | 4 +- .../generator/get-company-generator.ts | 90 ++++++++++++++++--- 10 files changed, 265 insertions(+), 25 deletions(-) create mode 100644 src/helper-modules/companies-house/examples/example-response-for-get-company-by-registration-number.json create mode 100644 src/modules/companies/examples/example-response-for-get-company-by-registration-number.json diff --git a/src/helper-modules/companies-house/examples/example-response-for-get-company-by-registration-number.json b/src/helper-modules/companies-house/examples/example-response-for-get-company-by-registration-number.json new file mode 100644 index 00000000..db9a93ac --- /dev/null +++ b/src/helper-modules/companies-house/examples/example-response-for-get-company-by-registration-number.json @@ -0,0 +1,60 @@ +{ + "links": { + "filing_history": "/company/00000001/filing-history", + "self": "/company/00000001", + "persons_with_significant_control": "/company/00000001/persons-with-significant-control", + "officers": "/company/00000001/officers" + }, + "accounts": { + "last_accounts": { + "period_end_on": "2022-12-31", + "type": "micro-entity", + "made_up_to": "2022-12-31", + "period_start_on": "2022-01-01" + }, + "accounting_reference_date": { + "month": "12", + "day": "31" + }, + "overdue": false, + "next_made_up_to": "2023-12-31", + "next_due": "2024-09-30", + "next_accounts": { + "period_start_on": "2023-01-01", + "due_on": "2024-09-30", + "period_end_on": "2023-12-31", + "overdue": false + } + }, + "company_name": "TEST COMPANY LTD", + "company_number": "00000001", + "company_status": "active", + "confirmation_statement": { + "next_made_up_to": "2025-01-20", + "next_due": "2025-02-03", + "overdue": false, + "last_made_up_to": "2024-01-20" + }, + "date_of_creation": "2020-01-21", + "etag": "4f776861e3dfca11b773ff496c55999b7ec6a1cc", + "has_charges": false, + "has_insolvency_history": false, + "jurisdiction": "england-wales", + "registered_office_address": { + "address_line_1": "1 Test Street", + "locality": "Test City", + "postal_code": "A1 2BC", + "country": "United Kingdom" + }, + "registered_office_is_in_dispute": false, + "sic_codes": [ + "59112", + "62012", + "62020", + "62090" + ], + "type": "ltd", + "undeliverable_registered_office_address": false, + "has_super_secure_pscs": false, + "can_file": true +} \ No newline at end of file diff --git a/src/modules/companies/companies.controller.test.ts b/src/modules/companies/companies.controller.test.ts index b4109400..f61ba8d1 100644 --- a/src/modules/companies/companies.controller.test.ts +++ b/src/modules/companies/companies.controller.test.ts @@ -21,7 +21,7 @@ describe('CompaniesController', () => { beforeEach(() => { resetAllWhenMocks(); - const companiesService = new CompaniesService(null); + const companiesService = new CompaniesService(null, null); companiesServiceGetCompanyByRegistrationNumber = jest.fn(); companiesService.getCompanyByRegistrationNumber = companiesServiceGetCompanyByRegistrationNumber; diff --git a/src/modules/companies/companies.module.ts b/src/modules/companies/companies.module.ts index b294e2de..65955e6a 100644 --- a/src/modules/companies/companies.module.ts +++ b/src/modules/companies/companies.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; import { CompaniesHouseModule } from '@ukef/helper-modules/companies-house/companies-house.module'; +import { SectorIndustriesModule } from '../sector-industries/sector-industries.module'; import { CompaniesController } from './companies.controller'; import { CompaniesService } from './companies.service'; @Module({ - imports: [CompaniesHouseModule], + imports: [CompaniesHouseModule, SectorIndustriesModule], controllers: [CompaniesController], providers: [CompaniesService], }) diff --git a/src/modules/companies/companies.service.test.ts b/src/modules/companies/companies.service.test.ts index 3588dff7..0c7c54fd 100644 --- a/src/modules/companies/companies.service.test.ts +++ b/src/modules/companies/companies.service.test.ts @@ -4,18 +4,20 @@ import { GetCompanyGenerator } from '@ukef-test/support/generator/get-company-ge import { RandomValueGenerator } from '@ukef-test/support/generator/random-value-generator'; import { resetAllWhenMocks, when } from 'jest-when'; +import { SectorIndustriesService } from '../sector-industries/sector-industries.service'; import { CompaniesService } from './companies.service'; describe('CompaniesService', () => { let configServiceGet: jest.Mock; let companiesHouseServiceGetCompanyByRegistrationNumber: jest.Mock; + let sectorIndustriesServiceFind: jest.Mock; let service: CompaniesService; const valueGenerator = new RandomValueGenerator(); const testRegistrationNumber = '00000001'; - const { getCompanyCompaniesHouseResponse, getCompanyResponse } = new GetCompanyGenerator(valueGenerator).generate({ + const { getCompanyCompaniesHouseResponse, findSectorIndustriesResponse, getCompanyResponse } = new GetCompanyGenerator(valueGenerator).generate({ numberToGenerate: 1, registrationNumber: testRegistrationNumber, }); @@ -31,12 +33,17 @@ describe('CompaniesService', () => { const companiesHouseService = new CompaniesHouseService(null, configService); companiesHouseService.getCompanyByRegistrationNumber = companiesHouseServiceGetCompanyByRegistrationNumber; - service = new CompaniesService(companiesHouseService); + sectorIndustriesServiceFind = jest.fn(); + const sectorIndustriesService = new SectorIndustriesService(null, null); + sectorIndustriesService.find = sectorIndustriesServiceFind; + + service = new CompaniesService(companiesHouseService, sectorIndustriesService); }); describe('getCompanyByRegistrationNumber', () => { it('calls getCompanyByRegistrationNumber on the CompaniesHouseService with the registration number', async () => { when(companiesHouseServiceGetCompanyByRegistrationNumber).calledWith(testRegistrationNumber).mockReturnValueOnce(getCompanyCompaniesHouseResponse); + when(sectorIndustriesServiceFind).calledWith(null, null).mockReturnValueOnce(findSectorIndustriesResponse); await service.getCompanyByRegistrationNumber(testRegistrationNumber); @@ -44,8 +51,19 @@ describe('CompaniesService', () => { expect(companiesHouseServiceGetCompanyByRegistrationNumber).toHaveBeenCalledWith(testRegistrationNumber); }); - it('returns a reduced form of the company returned by the CompaniesHouseService, with fewer fields', async () => { + it(`calls find on the SectorIndustriesService with both arguments as 'null'`, async () => { + when(companiesHouseServiceGetCompanyByRegistrationNumber).calledWith(testRegistrationNumber).mockReturnValueOnce(getCompanyCompaniesHouseResponse); + when(sectorIndustriesServiceFind).calledWith(null, null).mockReturnValueOnce(findSectorIndustriesResponse); + + await service.getCompanyByRegistrationNumber(testRegistrationNumber); + + expect(sectorIndustriesServiceFind).toHaveBeenCalledTimes(1); + expect(sectorIndustriesServiceFind).toHaveBeenCalledWith(null, null); + }); + + it('returns a mapped form of the company returned by the CompaniesHouseService', async () => { when(companiesHouseServiceGetCompanyByRegistrationNumber).calledWith(testRegistrationNumber).mockReturnValueOnce(getCompanyCompaniesHouseResponse); + when(sectorIndustriesServiceFind).calledWith(null, null).mockReturnValueOnce(findSectorIndustriesResponse); const response = await service.getCompanyByRegistrationNumber(testRegistrationNumber); diff --git a/src/modules/companies/companies.service.ts b/src/modules/companies/companies.service.ts index 965632d4..693b31e2 100644 --- a/src/modules/companies/companies.service.ts +++ b/src/modules/companies/companies.service.ts @@ -2,21 +2,27 @@ import { Injectable } from '@nestjs/common'; import { CompaniesHouseService } from '@ukef/helper-modules/companies-house/companies-house.service'; import { GetCompanyCompaniesHouseResponse } from '@ukef/helper-modules/companies-house/dto/get-company-companies-house-response.dto'; -import { GetCompanyResponse } from './dto/get-company-response.dto'; +import { SectorIndustryEntity } from '../sector-industries/entities/sector-industry.entity'; +import { SectorIndustriesService } from '../sector-industries/sector-industries.service'; +import { GetCompanyResponse, Industry } from './dto/get-company-response.dto'; @Injectable() export class CompaniesService { - constructor(private readonly companiesHouseService: CompaniesHouseService) {} + constructor( + private readonly companiesHouseService: CompaniesHouseService, + private readonly sectorIndustriesService: SectorIndustriesService, + ) {} async getCompanyByRegistrationNumber(registrationNumber: string): Promise { const company: GetCompanyCompaniesHouseResponse = await this.companiesHouseService.getCompanyByRegistrationNumber(registrationNumber); + const industryClasses: SectorIndustryEntity[] = await this.sectorIndustriesService.find(null, null); - const reducedCompany = this.reduceCompany(company); + const mappedCompany = this.mapCompany(company, industryClasses); - return reducedCompany; + return mappedCompany; } - private reduceCompany(company: GetCompanyCompaniesHouseResponse): GetCompanyResponse { + private mapCompany(company: GetCompanyCompaniesHouseResponse, industryClasses: SectorIndustryEntity[]): GetCompanyResponse { const address = company.registered_office_address; return { @@ -31,7 +37,30 @@ export class CompaniesService { postalCode: address.postal_code, country: address.country, }, - sicCodes: company.sic_codes, + industries: this.mapSicCodes(company.sic_codes, industryClasses), }; } + + private mapSicCodes(sicCodes: string[], industryClasses: SectorIndustryEntity[]): Industry[] { + const industries = []; + + sicCodes.forEach((sicCode) => { + industryClasses.forEach((industryClass) => { + if (sicCode === industryClass.ukefIndustryId) { + industries.push({ + sector: { + code: industryClass.ukefSectorId.toString(), + name: industryClass.ukefSectorName, + }, + class: { + code: industryClass.ukefIndustryId, + name: industryClass.ukefIndustryName, + }, + }); + } + }); + }); + + return industries; + } } diff --git a/src/modules/companies/dto/get-company-response.dto.ts b/src/modules/companies/dto/get-company-response.dto.ts index fc90fa44..38c9c238 100644 --- a/src/modules/companies/dto/get-company-response.dto.ts +++ b/src/modules/companies/dto/get-company-response.dto.ts @@ -10,5 +10,16 @@ export class GetCompanyResponse { postalCode: string; country: string; }; - sicCodes: string[]; + industries: Industry[]; +} + +export class Industry { + sector: { + code: string; + name: string; + }; + class: { + code: string; + name: string; + }; } diff --git a/src/modules/companies/examples/example-response-for-get-company-by-registration-number.json b/src/modules/companies/examples/example-response-for-get-company-by-registration-number.json new file mode 100644 index 00000000..195f64ca --- /dev/null +++ b/src/modules/companies/examples/example-response-for-get-company-by-registration-number.json @@ -0,0 +1,52 @@ +{ + "companiesHouseRegistrationNumber": "00000001", + "companyName": "TEST COMPANY LTD", + "registeredAddress": { + "addressLine1": "1 Test Street", + "locality": "Test City", + "postalCode": "A1 2BC", + "country": "United Kingdom" + }, + "industries": [ + { + "class": { + "code": "59112", + "name": "Video production activities" + }, + "sector": { + "code": "1009", + "name": "Information and communication" + } + }, + { + "class": { + "code": "62012", + "name": "Business and domestic software development" + }, + "sector": { + "code": "1009", + "name": "Information and communication" + } + }, + { + "class": { + "code": "62020", + "name": "Information technology consultancy activities" + }, + "sector": { + "code": "1009", + "name": "Information and communication" + } + }, + { + "class": { + "code": "62090", + "name": "Other information technology service activities" + }, + "sector": { + "code": "1009", + "name": "Information and communication" + } + } + ] +} \ No newline at end of file diff --git a/src/modules/sector-industries/sector-industries.module.ts b/src/modules/sector-industries/sector-industries.module.ts index 00f8b6eb..6a0082ec 100644 --- a/src/modules/sector-industries/sector-industries.module.ts +++ b/src/modules/sector-industries/sector-industries.module.ts @@ -10,5 +10,6 @@ import { SectorIndustriesService } from './sector-industries.service'; imports: [TypeOrmModule.forFeature([SectorIndustryEntity], DATABASE.MDM)], controllers: [SectorIndustriesController], providers: [SectorIndustriesService], + exports: [SectorIndustriesService], }) export class SectorIndustriesModule {} diff --git a/test/companies/get-company-by-registration-number.api-test.ts b/test/companies/get-company-by-registration-number.api-test.ts index 962927ea..cd83f281 100644 --- a/test/companies/get-company-by-registration-number.api-test.ts +++ b/test/companies/get-company-by-registration-number.api-test.ts @@ -4,6 +4,8 @@ import { ENVIRONMENT_VARIABLES, TIME_EXCEEDING_COMPANIES_HOUSE_TIMEOUT } from '@ import { GetCompanyGenerator } from '@ukef-test/support/generator/get-company-generator'; import { RandomValueGenerator } from '@ukef-test/support/generator/random-value-generator'; import nock from 'nock'; +import getCompanyCompaniesHouseResponse = require('@ukef/helper-modules/companies-house/examples/example-response-for-get-company-by-registration-number.json'); +import getCompanyResponse = require('@ukef/modules/companies/examples/example-response-for-get-company-by-registration-number.json'); describe('GET /companies?registrationNumber=', () => { let api: Api; @@ -13,11 +15,9 @@ describe('GET /companies?registrationNumber=', () => { const { companiesHousePath, mdmPath, - getCompanyCompaniesHouseResponse, getCompanyCompaniesHouseMalformedAuthorizationHeaderResponse, getCompanyCompaniesHouseInvalidAuthorizationResponse, getCompanyCompaniesHouseNotFoundResponse, - getCompanyResponse, } = new GetCompanyGenerator(valueGenerator).generate({ numberToGenerate: 1, registrationNumber: '00000001', diff --git a/test/support/generator/get-company-generator.ts b/test/support/generator/get-company-generator.ts index 70bd219b..c869f277 100644 --- a/test/support/generator/get-company-generator.ts +++ b/test/support/generator/get-company-generator.ts @@ -1,7 +1,8 @@ import { GetCompanyCompaniesHouseErrorResponse } from '@ukef/helper-modules/companies-house/dto/get-company-companies-house-error-response.dto'; import { GetCompanyCompaniesHouseMultipleErrorResponse } from '@ukef/helper-modules/companies-house/dto/get-company-companies-house-multiple-error-response.dto'; import { GetCompanyCompaniesHouseResponse } from '@ukef/helper-modules/companies-house/dto/get-company-companies-house-response.dto'; -import { GetCompanyResponse } from '@ukef/modules/companies/dto/get-company-response.dto'; +import { GetCompanyResponse, Industry } from '@ukef/modules/companies/dto/get-company-response.dto'; +import { SectorIndustryEntity } from '@ukef/modules/sector-industries/entities/sector-industry.entity'; import { AbstractGenerator } from './abstract-generator'; import { RandomValueGenerator } from './random-value-generator'; @@ -21,10 +22,20 @@ export class GetCompanyGenerator extends AbstractGenerator this.valueGenerator.date().toISOString().split('T')[0]; const randomAccountingReferenceDate = this.valueGenerator.date(); + const shuffleArray = (array: any[]) => { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + }; + const getCompanyCompaniesHouseResponse: GetCompanyCompaniesHouseResponse = { links: { filing_history: `/company/${registrationNumberToUse}/filing-history`, @@ -88,13 +106,61 @@ export class GetCompanyGenerator extends AbstractGenerator ({ + id: this.valueGenerator.integer({ min: 1, max: 1000 }), + ukefSectorId: v.industrySectorCode, + ukefSectorName: v.industrySectorName, + internalNo: null, + ukefIndustryId: sicCode, + ukefIndustryName: v.industryClassNames[index], + acbsSectorId: this.valueGenerator.stringOfNumericCharacters({ length: 2 }), + acbsSectorName: this.valueGenerator.sentence({ words: 5 }), + acbsIndustryId: this.valueGenerator.stringOfNumericCharacters({ length: 2 }), + acbsIndustryName: this.valueGenerator.sentence({ words: 4 }), + created: this.valueGenerator.date(), + updated: this.valueGenerator.date(), + effectiveFrom: this.valueGenerator.date(), + effectiveTo: this.valueGenerator.date(), + })); + + const nonMatchingIndustryClass: SectorIndustryEntity = { + id: this.valueGenerator.integer({ min: 1, max: 1000 }), + ukefSectorId: this.valueGenerator.integer({ min: 1001, max: 1020 }), + ukefSectorName: this.valueGenerator.sentence({ words: 4 }), + internalNo: null, + ukefIndustryId: this.valueGenerator.stringOfNumericCharacters({ length: 5 }), + ukefIndustryName: this.valueGenerator.sentence({ words: 10 }), + acbsSectorId: this.valueGenerator.stringOfNumericCharacters({ length: 2 }), + acbsSectorName: this.valueGenerator.sentence({ words: 5 }), + acbsIndustryId: this.valueGenerator.stringOfNumericCharacters({ length: 2 }), + acbsIndustryName: this.valueGenerator.sentence({ words: 4 }), + created: this.valueGenerator.date(), + updated: this.valueGenerator.date(), + effectiveFrom: this.valueGenerator.date(), + effectiveTo: this.valueGenerator.date(), + }; + + findSectorIndustriesResponse.push(nonMatchingIndustryClass); + shuffleArray(findSectorIndustriesResponse); + + const industries: Industry[] = v.sicCodes.map((sicCode, index) => ({ + sector: { + code: v.industrySectorCode.toString(), + name: v.industrySectorName, + }, + class: { + code: sicCode, + name: v.industryClassNames[index], + }, + })); + const getCompanyResponse: GetCompanyResponse = { companiesHouseRegistrationNumber: registrationNumberToUse, companyName: v.companyName, @@ -104,7 +170,7 @@ export class GetCompanyGenerator extends AbstractGenerator