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 contact data for a licence #1317

Merged
merged 33 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ca447bd
Import company contact data for a licence
jonathangoulding Sep 9, 2024
226d12f
fix: use contacts instead of company contacts
jonathangoulding Sep 9, 2024
1398770
fix: a contact must be from the licence version and not an organisation
jonathangoulding Sep 9, 2024
20e8a10
test: persist logic for contacts
jonathangoulding Sep 9, 2024
6a37dac
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 10, 2024
e621958
chore: pr fixes
jonathangoulding Sep 10, 2024
9898b07
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 10, 2024
c626408
fix: datasource for contact
jonathangoulding Sep 10, 2024
3378985
fix: process licence test
jonathangoulding Sep 10, 2024
2969407
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 12, 2024
79e01ef
fix: sql query to get licence role party id's for a contact
jonathangoulding Sep 13, 2024
2657684
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 13, 2024
da29f3d
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 18, 2024
d1b5abb
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 18, 2024
d7dd16d
feat: add company contact
jonathangoulding Sep 19, 2024
cdb8a58
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 19, 2024
53043ed
test: update import contacts tests to use company contacts
jonathangoulding Sep 20, 2024
184d9a0
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 20, 2024
6e9460c
feat: add company contacts to persist
jonathangoulding Sep 20, 2024
e5f483e
feat: add company contacts to persist test
jonathangoulding Sep 20, 2024
6217f29
fix: failing persist company contact test
jonathangoulding Sep 23, 2024
46668cf
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 23, 2024
8a316d3
chore: pre pr checks
jonathangoulding Sep 23, 2024
2407f67
chore: pre pr checks
jonathangoulding Sep 23, 2024
2156b50
Update app/presenters/import/legacy/company-contact.presenter.js
jonathangoulding Sep 23, 2024
965c794
Update app/services/import/legacy/fetch-contacts.service.js
jonathangoulding Sep 23, 2024
da6de67
Update app/services/import/legacy/transform-contacts.service.js
jonathangoulding Sep 23, 2024
028aceb
Update app/services/import/legacy/transform-contacts.service.js
jonathangoulding Sep 23, 2024
1ee1505
Update app/services/import/legacy/transform-contacts.service.js
jonathangoulding Sep 23, 2024
04fd0ac
chore: fix pr issues
jonathangoulding Sep 24, 2024
66871d4
chore: fix pr issues
jonathangoulding Sep 24, 2024
ac80374
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 24, 2024
8943841
Merge branch 'main' into import-company-contact-for-licence
jonathangoulding Sep 24, 2024
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
30 changes: 30 additions & 0 deletions app/presenters/import/legacy/company-contact.presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict'

/**
* Creates a company contact in the WRLS format
* @module CompanyContactPresenter
*/

/**
* Creates a company contact in the WRLS format
*
* NALD does not have the concept of a 'company contact'. It is a WRLS construct only.
*
* If the NALD party is of type 'PERS', WRLS on import splits it into a 'company' and 'contact' record. The
* 'company-contact' record is the thing that links then together in WRLS.
*
* @param {ImportLegacyContactType} contact - the legacy NALD contact derived from the 'party'
*
* @returns {object} the details needed to persist the company contact in WRLS
*/
function go (contact) {
return {
externalId: contact.external_id,
startDate: contact.start_date,
licenceRoleId: contact.licence_role_id
}
}

module.exports = {
go
}
28 changes: 28 additions & 0 deletions app/presenters/import/legacy/contact.presenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict'

/**
* Maps the legacy NALD contact data to the WRLS format
* @module ContactPresenter
*/

/**
* Maps the legacy NALD contact data to the WRLS format
*
* @param {ImportLegacyContactType} contact - the legacy NALD contact
*
* @returns {object} the NALD contact data transformed into the WRLS format for a contact
* ready for validation and persisting
*/
function go (contact) {
return {
externalId: contact.external_id,
salutation: contact.salutation,
initials: contact.initials,
firstName: contact.first_name,
lastName: contact.last_name
}
}

module.exports = {
go
}
27 changes: 2 additions & 25 deletions app/services/import/legacy/fetch-company.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
const { db } = require('../../../../db/db.js')

/**
* Fetches the licence data from the import.NALD_PARTIES table for the licence ref
* Fetches the party data from the import.NALD_PARTIES table for the licence ref
*
* From this point parties will be referred to as companies and a party will be known as a company
*
Expand All @@ -29,24 +29,6 @@ function _query () {
return `
SELECT DISTINCT ON (np."ID")
(concat_ws(':', np."FGAC_REGION_CODE", np."ID")) AS external_id,
(
CASE np."SALUTATION"
WHEN 'null' THEN NULL
ELSE np."SALUTATION"
END
) AS salutation,
(
CASE np."FORENAME"
WHEN 'null' THEN NULL
ELSE np."FORENAME"
END
) AS firstName,
(
CASE np."NAME"
WHEN 'null' THEN NULL
ELSE np."NAME"
END
) AS lastName,
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved
(
CASE np."APAR_TYPE"
WHEN 'PER' THEN 'person'
Expand All @@ -72,8 +54,7 @@ function _query () {
END
)
END
)) AS name,
np."ID" as id
)) AS name
FROM import."NALD_PARTIES" np
LEFT JOIN import."NALD_ABS_LIC_VERSIONS" nalv
ON nalv."ACON_APAR_ID" = np."ID"
Expand All @@ -95,10 +76,6 @@ module.exports = {
/**
* @typedef {object} ImportLegacyCompanyType
*
* @property {string} - The party id
* @property {string|null} salutation - The salutation of the person, or null if not applicable.
* @property {string|null} firstName - The first name of the person, or null if not applicable.
* @property {string|null} lastName - The last name of the person, or null if not applicable.
* @property {string} type - Indicates whether the entry is a 'person' or an 'organisation'.
* @property {string|null} name - The full name, concatenated from salutation, forename/initials, and name.
* @property {string} external_id - The external ID, formatted as 'FGAC_REGION_CODE:ID'.
Expand Down
86 changes: 86 additions & 0 deletions app/services/import/legacy/fetch-contacts.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use strict'

/**
* Fetches the contact data from the import.NALD_PARTIES table for the licence ref
* @module FetchContactsService
*/

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

/**
* Fetches the company data from the import.NALD_PARTIES table for the licence ref
*
* The only contact currently imported and saved to WRLS is the licence holder.
*
* @param {string} regionCode - The NALD region code
* @param {string} licenceId - The NALD licence ID
*
* @returns {Promise<ImportLegacyContactType[]>}
*/
async function go (regionCode, licenceId) {
const query = _query()

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

return rows
}

function _query () {
return `
SELECT DISTINCT ON (np."ID")
to_date(nalv."EFF_ST_DATE", 'DD/MM/YYYY') as start_date,
(concat_ws(':', np."FGAC_REGION_CODE", np."ID")) AS external_id,
(
CASE np."SALUTATION"
WHEN 'null' THEN NULL
ELSE np."SALUTATION"
END
) AS salutation,
(
CASE np."INITIALS"
WHEN 'null' THEN NULL
ELSE np."INITIALS"
END
) AS initials,
(
CASE np."FORENAME"
WHEN 'null' THEN NULL
ELSE np."FORENAME"
END
) AS first_name,
(
CASE np."NAME"
WHEN 'null' THEN NULL
ELSE np."NAME"
END
) AS last_name,
lr.id as licence_role_id
FROM import."NALD_PARTIES" np
LEFT JOIN import."NALD_ABS_LIC_VERSIONS" nalv
ON nalv."ACON_APAR_ID" = np."ID"
AND nalv."FGAC_REGION_CODE" = np."FGAC_REGION_CODE"
INNER JOIN public.licence_roles lr
ON lr.name = 'licenceHolder'
WHERE np."APAR_TYPE" != 'ORG'
jonathangoulding marked this conversation as resolved.
Show resolved Hide resolved
AND nalv."FGAC_REGION_CODE" = ?
AND nalv."AABL_ID" = ?
ORDER BY np."ID", TO_DATE(nalv."EFF_ST_DATE", 'DD/MM/YYYY') ASC;
`
}

module.exports = {
go
}

/**
* @typedef {object} ImportLegacyContactType
*
* @property {string|null} salutation - The salutation of the person, or null if not applicable.
* @property {string|null} initials - The initials of the person, or null if not applicable.
* @property {string|null} firstName - The first name of the person, or null if not applicable.
* @property {string|null} lastName - The last name of the person, or null if not applicable.
* @property {string} external_id - The external ID, formatted as 'FGAC_REGION_CODE:ID'.
* @property {Date} start_date - the earliest start date for the licence versions
* @property {uuid} licence_role_id - the licence role reference data id from WLRS
*
*/
4 changes: 4 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 TransformCompaniesService = require('./transform-companies.service.js')
const TransformContactsService = require('./transform-contacts.service.js')
const TransformLicenceService = require('./transform-licence.service.js')
const TransformLicenceVersionPurposeConditionsService = require('./transform-licence-version-purpose-conditions.service.js')
const TransformLicenceVersionPurposesService = require('./transform-licence-version-purposes.service.js')
Expand Down Expand Up @@ -38,6 +39,9 @@ async function go (licenceRef) {
// Transform the company data
const { transformedCompanies } = await TransformCompaniesService.go(regionCode, naldLicenceId)

// Pass the transformed companies through each transformation step, building the company as we go
await TransformContactsService.go(regionCode, naldLicenceId, transformedCompanies)

// Ensure the built licence has all the valid child records we require
LicenceStructureValidator.go(transformedLicence)

Expand Down
45 changes: 45 additions & 0 deletions app/services/import/legacy/transform-contacts.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict'

/**
* Transforms NALD contact data into a valid object that matches the WRLS structure
* @module ImportLegacyTransformContactsService
*/

const FetchContactsService = require('./fetch-contacts.service.js')
const ContactPresenter = require('../../../presenters/import/legacy/contact.presenter.js')
const CompanyContactPresenter = require('../../../presenters/import/legacy/company-contact.presenter.js')
const ImportContactValidator = require('../../../validators/import/contact.validator.js')

/**
* Transforms NALD contact data into a validated object that matches the WRLS structure
*
* @param {string} regionCode - The NALD region code
* @param {string} licenceId - The NALD licence ID
* @param {object[]} transformedCompanies
*
*/
async function go (regionCode, licenceId, transformedCompanies) {
const naldContacts = await FetchContactsService.go(regionCode, licenceId)

naldContacts.forEach((naldContact) => {
const matchingCompany = _matchingCompany(transformedCompanies, naldContact)

const transformedContact = ContactPresenter.go(naldContact)

ImportContactValidator.go(transformedContact)

matchingCompany.contact = { ...transformedContact, dataSource: 'nald' }

matchingCompany.companyContact = CompanyContactPresenter.go(naldContact)
})
}

function _matchingCompany (transformedCompanies, naldContact) {
return transformedCompanies.find((company) => {
return company.externalId === naldContact.external_id
})
}

module.exports = {
go
}
50 changes: 46 additions & 4 deletions app/services/import/persist-licence.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
* @module PersistLicenceService
*/

const { timestampForPostgres } = require('../../lib/general.lib.js')
const CompanyModel = require('../../models/company.model.js')
const ContactModel = require('../../models/contact.model.js')
const LicenceModel = require('../../models/licence.model.js')
const LicenceVersionModel = require('../../models/licence-version.model.js')
const LicenceVersionPurposeModel = require('../../models/licence-version-purpose.model.js')
const LicenceVersionPurposeConditionModel = require('../../models/licence-version-purpose-condition.model.js')
const CompanyModel = require('../../models/company.model.js')
const LicenceVersionPurposeModel = require('../../models/licence-version-purpose.model.js')
const { timestampForPostgres } = require('../../lib/general.lib.js')
const { db } = require('../../../db/db.js')

/**
* Creates or updates an imported licence and its child entities that have been transformed and validated
Expand Down Expand Up @@ -140,11 +142,19 @@ async function _persistLicenceVersionPurposeCondition (
async function _persistCompanies (trx, updatedAt, companies) {
for (const company of companies) {
await _persistCompany(trx, updatedAt, company)

if (company.contact) {
await _persistContact(trx, updatedAt, company.contact)
}

if (company.companyContact) {
await _persistsCompanyContact(trx, updatedAt, company.companyContact)
}
}
}

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

return CompanyModel.query(trx)
.insert({ ...propertiesToPersist, updatedAt })
Expand All @@ -156,6 +166,38 @@ async function _persistCompany (trx, updatedAt, company) {
])
}

async function _persistContact (trx, updatedAt, contact) {
return ContactModel.query(trx)
.insert({ ...contact, updatedAt })
.onConflict('externalId')
.merge([
'salutation',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this update the data source to nald ? what if this has changed in wrls. Might be a none issue as the test helper sets this as wrls.

'initials',
'firstName',
'lastName',
'updatedAt'
])
}

async function _persistsCompanyContact (trx, updatedAt, companyContact) {
const { externalId, startDate, licenceRoleId } = companyContact

return db.raw(`
INSERT INTO public."company_contacts" (company_id, contact_id, licence_role_id, start_date, "default", created_at, updated_at)
SELECT com.id, con.id, lr.id, ?, true, NOW(), ?
FROM public.companies com
JOIN public."licence_roles" lr on lr.id = ?
JOIN public.contacts con ON con.external_id = ?
WHERE com.external_id = ?
ON CONFLICT (company_id, contact_id, licence_role_id, start_date)
DO UPDATE SET
contact_id = EXCLUDED.contact_id,
"default" = EXCLUDED."default",
updated_at = EXCLUDED.updated_at
`, [startDate, updatedAt, licenceRoleId, externalId, externalId])
.transacting(trx)
}

module.exports = {
go
}
34 changes: 34 additions & 0 deletions app/validators/import/contact.validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict'

/**
* @module ImportContactValidator
*/

const Joi = require('joi')

/**
* Checks that the imported contact data has been transformed and is valid for persisting to WRLS
*
* @param {object} contact - The transformed contact data
*
* @throws {Joi.ValidationError} - throws a Joi validation error if the validation fails
*/
function go (contact) {
const schema = Joi.object({
salutation: Joi.string().allow(null),
initials: Joi.string().allow(null),
firstName: Joi.string().allow(null),
lastName: Joi.string().allow(null),
externalId: Joi.string().required()
})

const result = schema.validate(contact, { convert: false })

if (result.error) {
throw result.error
}
}

module.exports = {
go
}
Loading
Loading