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

Import company address data for a licence #1371

Merged
merged 15 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 11 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
54 changes: 54 additions & 0 deletions app/presenters/import/legacy/company-address.presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict'

/**
* Maps the legacy NALD data to a company address
* @module CompanyAddressPresenter
*/

/**
* Maps the legacy NALD data to a company address
*
* @param {ImportLegacyCompanyAddressType} address - the legacy NALD data in company address format
*
* @returns {object} the NALD company address data transformed into the WRLS format for a company address
* ready for validation and persisting
*/
function go (address) {
return {
addressId: address.external_id,
companyId: address.company_external_id,
licenceRoleId: address.licence_role_id,
startDate: address.start_date,
endDate: address.licence_role_name === 'licenceHolder' ? _endDate(address) : address.end_date
}
}

/**
* Calculate the licence holders company address end date
*
* A licence can have multiple versions, when one licence version ends the other should start.
*
* Unless a licence has not ended then the end date can be null to show it has not ended
*
* A licence can be ended, revoked, lapsed or expired. When this is the case we want to the oldest date of the options.
*
* @param {ImportLegacyCompanyAddressType} address - the legacy NALD address
*
* @returns {date | null} the end date for a licence holder
* @private
*/
function _endDate (address) {
const oldestDate = [address.end_date, address.lapsed_date, address.expired_date, address.revoked_date]
// Removes any null values
.filter((date) => { return date })
// Sorts in asc order
.sort((date1, date2) => { return date1 - date2 })
// We only want the oldest date
.slice(-1)[0]
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved

return oldestDate || null
}

module.exports = {
go
}
89 changes: 89 additions & 0 deletions app/services/import/legacy/fetch-company-address.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict'

/**
* Fetches the NALD data for a company address using the licence ref
* @module FetchCompanyAddressesService
*/

const { db } = require('../../../../db/db.js')

/**
* Fetches the NALD data for a company address using the licence ref
*
* A Company Address is a WRLS concept. It is used to link an address to a company, with a role.
*
* @param {string} regionCode - The NALD region code
* @param {string} licenceId - The NALD licence ID
*
* @returns {Promise<ImportLegacyCompanyAddressType[]>}
*/
async function go (regionCode, licenceId) {
const query = _query()

const { rows } = await db.raw(query, [regionCode, licenceId, regionCode, licenceId])

return rows
}

/**
* Fetches the NALD data for a licence version to create a company address for the licence holder
*
* @private
*/
function _query () {
return `
WITH end_date_cte AS (
SELECT
"ACON_AADD_ID",
CASE
WHEN COUNT("EFF_END_DATE") FILTER (WHERE "EFF_END_DATE" = 'null') > 0 THEN NULL
ELSE MAX(TO_DATE(NULLIF("EFF_END_DATE", 'null'), 'DD/MM/YYYY'))
END AS end_date
FROM import."NALD_ABS_LIC_VERSIONS"
WHERE "FGAC_REGION_CODE" = ?
AND "AABL_ID" = ?
GROUP BY "ACON_AADD_ID"
)
SELECT DISTINCT ON (nalv."ACON_AADD_ID")
ed.end_date,
TO_DATE(NULLIF(nalv."EFF_ST_DATE", 'null'), 'DD/MM/YYYY') AS start_date,
TO_DATE(NULLIF(nal."REV_DATE", 'null'), 'DD/MM/YYYY') AS revoked_date,
TO_DATE(NULLIF(nal."EXPIRY_DATE", 'null'), 'DD/MM/YYYY') AS expired_date,
TO_DATE(NULLIF(nal."LAPSED_DATE", 'null'), 'DD/MM/YYYY') AS lapsed_date,
(concat_ws(':', nalv."FGAC_REGION_CODE", nalv."ACON_AADD_ID")) AS external_id,
(concat_ws(':', nalv."FGAC_REGION_CODE", nalv."ACON_APAR_ID")) AS company_external_id,
nalv."ACON_APAR_ID" AS party_id,
nalv."ACON_AADD_ID" AS address_id,
lr.id as licence_role_id,
lr.name as licence_role_name
FROM import."NALD_ABS_LIC_VERSIONS" AS nalv
INNER JOIN public.licence_roles AS lr
ON lr.name = 'licenceHolder'
INNER JOIN import."NALD_ABS_LICENCES" AS nal
ON nal."ID" = nalv."AABL_ID"
AND nal."FGAC_REGION_CODE" = nalv."FGAC_REGION_CODE"
LEFT JOIN end_date_cte AS ed
ON ed."ACON_AADD_ID" = nalv."ACON_AADD_ID"
WHERE nalv."FGAC_REGION_CODE" = ?
AND nalv."AABL_ID" = ?
AND nalv."ACON_AADD_ID" IS NOT NULL;
`
}

module.exports = {
go
}

/**
* @typedef {object} ImportLegacyCompanyAddressType
*
* @property {string} external_id - The address id
* @property {string} company_external_id - The company id
* @property {string} licence_role_id - The licence role id from the reference data
* @property {string} start_date - The licence version with the earliest start date
* @property {string} end_date - The licence version latest end date, unless null then always null
* @property {string} revoked_date - The licence revoked date
* @property {string} expired_date - The licence expired date
* @property {string} lapsed_date - The licence lapsed licence date
*
*/
2 changes: 2 additions & 0 deletions app/services/import/legacy/process-licence.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const LicenceStructureValidator = require('../../../validators/import/licence-st
const PersistLicenceService = require('../persist-licence.service.js')
const ProcessLicenceReturnLogsService = require('../../jobs/return-logs/process-licence-return-logs.service.js')
const TransformAddressesService = require('./transform-addresses.service.js')
const TransformCompanyAddressesService = require('./transform-company-addresses.service.js')
const TransformCompaniesService = require('./transform-companies.service.js')
const TransformContactsService = require('./transform-contacts.service.js')
const TransformLicenceService = require('./transform-licence.service.js')
Expand Down Expand Up @@ -43,6 +44,7 @@ async function go (licenceRef) {
// Pass the transformed companies through each transformation step, building the company as we go
await TransformContactsService.go(regionCode, naldLicenceId, transformedCompanies)
await TransformAddressesService.go(regionCode, naldLicenceId, transformedCompanies)
await TransformCompanyAddressesService.go(regionCode, naldLicenceId, transformedCompanies)

// Ensure the built licence has all the valid child records we require
LicenceStructureValidator.go(transformedLicence)
Expand Down
39 changes: 39 additions & 0 deletions app/services/import/legacy/transform-company-addresses.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict'

/**
* Transforms NALD data into a valid object that matches the WRLS structure for a company address
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved
* @module ImportLegacyTransformCompanyAddressesService
*/

const CompanyAddressPresenter = require('../../../presenters/import/legacy/company-address.presenter.js')
const FetchCompanyAddressesService = require('./fetch-company-address.service.js')

/**
* Transforms NALD data into a valid object that matches the WRLS structure for a company address
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved
*
* @param {string} regionCode - The NALD region code
* @param {string} licenceId - The NALD licence ID
* @param {object[]} transformedCompanies
*
*/
async function go (regionCode, licenceId, transformedCompanies) {
const naldAddresses = await FetchCompanyAddressesService.go(regionCode, licenceId)

naldAddresses.forEach((naldAddress) => {
const matchingCompany = _matchingCompany(transformedCompanies, naldAddress)

const address = CompanyAddressPresenter.go(naldAddress)

matchingCompany.companyAddresses = [...(matchingCompany?.companyAddresses || []), address]
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved
})
}

function _matchingCompany (transformedCompanies, naldAddress) {
return transformedCompanies.find((company) => {
return company.externalId === naldAddress.company_external_id
})
}

module.exports = {
go
}
33 changes: 32 additions & 1 deletion app/services/import/persist-licence.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,32 @@ async function _persistAddress (trx, updatedAt, address) {
])
}

async function _persistCompanyAddresses (trx, updatedAt, companyAddresses) {
for (const companyAddress of companyAddresses) {
await _persistCompanyAddress(trx, updatedAt, companyAddress)
}
}

async function _persistCompanyAddress (trx, updatedAt, companyAddress) {
const { companyId, startDate, endDate, licenceRoleId, addressId } = companyAddress

return db.raw(`
INSERT INTO public."company_addresses" (company_id, address_id, licence_role_id, start_date, end_date, "default", created_at, updated_at)
SELECT com.id, add.id, lr.id, ? ,?, true, NOW(), ?
FROM public.companies com
JOIN public."licence_roles" lr on lr.id = ?
JOIN public.addresses add ON add.external_id = ?
WHERE com.external_id = ?
ON CONFLICT (company_id, address_id, licence_role_id)
DO UPDATE SET
address_id=EXCLUDED.address_id,
"default" = EXCLUDED."default",
end_date = EXCLUDED.end_date,
updated_at = EXCLUDED.updated_at
`, [startDate, endDate, updatedAt, licenceRoleId, addressId, companyId])
.transacting(trx)
}

async function _persistAddresses (trx, updatedAt, addresses) {
for (const address of addresses) {
await _persistAddress(trx, updatedAt, address)
Expand Down Expand Up @@ -176,11 +202,16 @@ async function _persistCompanies (trx, updatedAt, companies) {
}

await _persistAddresses(trx, updatedAt, company.addresses)

// TODO: remove this when licence roles are done as every company has an and subsequently a company address
if (company.companyAddresses) {
await _persistCompanyAddresses(trx, updatedAt, company.companyAddresses)
}
}
}

async function _persistCompany (trx, updatedAt, company) {
const { contact, companyContact, addresses, ...propertiesToPersist } = company
const { contact, companyContact, addresses, companyAddresses, ...propertiesToPersist } = company

return CompanyModel.query(trx)
.insert({ ...propertiesToPersist, updatedAt })
Expand Down
136 changes: 136 additions & 0 deletions test/presenters/import/legacy/company-address.presenter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
'use strict'

// Test framework dependencies
const Lab = require('@hapi/lab')
const Code = require('@hapi/code')

const { describe, it, beforeEach } = exports.lab = Lab.script()
const { expect } = Code

// Test helpers
const { generateUUID } = require('../../../../app/lib/general.lib.js')

// Thing under test
const CompanyAddressPresenter = require('../../../../app/presenters/import/legacy/company-address.presenter.js')

describe('Import Legacy Company Address presenter', () => {
const licenceRoleId = generateUUID()

let legacyLicenceHolderAddress

beforeEach(() => {
legacyLicenceHolderAddress = _legacyLicenceHolderCompanyAddress(licenceRoleId)
})

it('correctly transforms the data', () => {
const result = CompanyAddressPresenter.go(legacyLicenceHolderAddress)

expect(result).to.equal({
addressId: '7:777',
companyId: '1:007',
endDate: null,
licenceRoleId,
startDate: new Date('2020-01-01')
})
})

describe('when the role is licence holder', () => {
describe('and all the dates are null', () => {
it('correctly sets end date to null', () => {
const { endDate } = CompanyAddressPresenter.go(legacyLicenceHolderAddress)

expect(endDate).to.be.null()
})
})

describe('and the end date is a date and the licence dates are null', () => {
beforeEach(() => {
const temp = _legacyLicenceHolderCompanyAddress(licenceRoleId)

legacyLicenceHolderAddress = { ...temp, end_date: new Date('2022-02-02') }
})

it('correctly sets end date to the end date value', () => {
const { endDate } = CompanyAddressPresenter.go(legacyLicenceHolderAddress)

expect(endDate).to.equal(new Date('2022-02-02'))
})
})

describe('and the revoked date is a date and the other dates are null', () => {
beforeEach(() => {
const temp = _legacyLicenceHolderCompanyAddress(licenceRoleId)

legacyLicenceHolderAddress = { ...temp, revoked_date: new Date('2023-03-03') }
})

it('correctly sets end date to the revoked date value', () => {
const { endDate } = CompanyAddressPresenter.go(legacyLicenceHolderAddress)

expect(endDate).to.equal(new Date('2023-03-03'))
})
})

describe('and the expired date is a date and the other dates are null', () => {
beforeEach(() => {
const temp = _legacyLicenceHolderCompanyAddress(licenceRoleId)

legacyLicenceHolderAddress = { ...temp, expired_date: new Date('2024-04-04') }
})

it('correctly sets end date to the expired date value', () => {
const { endDate } = CompanyAddressPresenter.go(legacyLicenceHolderAddress)

expect(endDate).to.equal(new Date('2024-04-04'))
})
})

describe('and the lapsed date is a date and the other dates are null', () => {
beforeEach(() => {
const temp = _legacyLicenceHolderCompanyAddress(licenceRoleId)

legacyLicenceHolderAddress = { ...temp, lapsed_date: new Date('2025-05-05') }
})

it('correctly sets end date to the lapsed date value', () => {
const { endDate } = CompanyAddressPresenter.go(legacyLicenceHolderAddress)

expect(endDate).to.equal(new Date('2025-05-05'))
})
})

describe('and all the dates have values', () => {
beforeEach(() => {
const temp = _legacyLicenceHolderCompanyAddress(licenceRoleId)

legacyLicenceHolderAddress = {
...temp,
end_date: new Date('2022-02-02'),
revoked_date: new Date('2023-03-03'),
expired_date: new Date('2024-04-04'),
lapsed_date: new Date('2025-05-05')
}
})

it('correctly sets end date latest date of the available dates', () => {
const { endDate } = CompanyAddressPresenter.go(legacyLicenceHolderAddress)

expect(endDate).to.equal(new Date('2025-05-05'))
})
})
})
})

function _legacyLicenceHolderCompanyAddress (licenceRoleId) {
return {
company_external_id: '1:007',
external_id: '7:777',
start_date: new Date('2020-01-01'),
end_date: null,
licence_role_id: licenceRoleId,
revoked_date: null,
expired_date: null,
lapsed_date: null,
licence_role_name: 'licenceHolder'
}
}
Loading
Loading