@defra-fish/dynamics-lib 1.68.0 → 1.69.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/entities/__tests__/rcr-activity.entity.spec.js +42 -0
- package/src/entities/rcr-activity.entity.js +95 -0
- package/src/index.js +2 -1
- package/src/queries/__tests__/contact.queries.spec.js +83 -90
- package/src/queries/__tests__/rcr-activity.queries.spec.js +48 -0
- package/src/queries/contact.queries.js +35 -35
- package/src/queries/rcr-activity.queries.js +27 -0
- package/src/queries/__tests__/activity.queries.spec.js +0 -133
- package/src/queries/activity.queries.js +0 -47
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra-fish/dynamics-lib",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.69.0-rc.0",
|
|
4
4
|
"description": "Framework to support integration with dynamics",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=22"
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
|
10
10
|
"rod",
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"simple-oauth2": "4.3.0",
|
|
44
44
|
"uuid": "8.3.2"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "e9eb67926528361d45ad630df84da2ad47723b97"
|
|
47
47
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Contact, RCRActivity, RCR_ACTIVITY_STATUS } from '../../index.js'
|
|
2
|
+
|
|
3
|
+
describe('rcr activity entity', () => {
|
|
4
|
+
it('maps from dynamics', async () => {
|
|
5
|
+
const rcrActivity = RCRActivity.fromResponse({
|
|
6
|
+
activityid: '1',
|
|
7
|
+
defra_activitystatus: RCR_ACTIVITY_STATUS.STARTED,
|
|
8
|
+
defra_season: 2025,
|
|
9
|
+
actualstart: '2020-03-31T22:59:00Z',
|
|
10
|
+
modifiedon: '2020-03-31T22:59:00Z',
|
|
11
|
+
actualend: '2020-03-31T22:59:00Z'
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
expect(rcrActivity.id).toBe('1')
|
|
15
|
+
expect(rcrActivity.status).toBe(RCR_ACTIVITY_STATUS.STARTED)
|
|
16
|
+
expect(rcrActivity.season).toBe(2025)
|
|
17
|
+
expect(rcrActivity.startDate).toBe('2020-03-31T22:59:00Z')
|
|
18
|
+
expect(rcrActivity.lastUpdated).toBe('2020-03-31T22:59:00Z')
|
|
19
|
+
expect(rcrActivity.submittedDate).toBe('2020-03-31T22:59:00Z')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('maps to dynamics', async () => {
|
|
23
|
+
const rcrActivity = new RCRActivity()
|
|
24
|
+
rcrActivity.status = RCR_ACTIVITY_STATUS.SUBMITTED
|
|
25
|
+
rcrActivity.season = 2025
|
|
26
|
+
rcrActivity.startDate = '2020-03-31T22:59:00Z'
|
|
27
|
+
rcrActivity.submittedDate = '2020-03-31T22:59:00Z'
|
|
28
|
+
|
|
29
|
+
const contact = new Contact()
|
|
30
|
+
rcrActivity.bindToEntity(RCRActivity.definition.relationships.licensee, contact)
|
|
31
|
+
|
|
32
|
+
const expectedFields = {
|
|
33
|
+
actualend: '2020-03-31T22:59:00Z',
|
|
34
|
+
actualstart: '2020-03-31T22:59:00Z',
|
|
35
|
+
defra_activitystatus: RCR_ACTIVITY_STATUS.SUBMITTED,
|
|
36
|
+
defra_season: 2025,
|
|
37
|
+
'regardingobjectid_contact_defra_rcractivity@odata.bind': `$${contact.uniqueContentId}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
expect(rcrActivity.toRequestBody()).toStrictEqual(expectedFields)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { BaseEntity, EntityDefinition } from './base.entity.js'
|
|
2
|
+
import { Contact } from './contact.entity.js'
|
|
3
|
+
|
|
4
|
+
export const RCR_ACTIVITY_STATUS = Object.freeze({
|
|
5
|
+
STARTED: 910400000,
|
|
6
|
+
SUBMITTED: 910400001
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* RCRActivity entity
|
|
11
|
+
* @extends BaseEntity
|
|
12
|
+
*/
|
|
13
|
+
export class RCRActivity extends BaseEntity {
|
|
14
|
+
/** @type {EntityDefinition} */
|
|
15
|
+
static _definition = new EntityDefinition(() => ({
|
|
16
|
+
localName: 'rcrActivity',
|
|
17
|
+
dynamicsCollection: 'defra_rcractivities',
|
|
18
|
+
defaultFilter: 'statecode eq 0',
|
|
19
|
+
mappings: {
|
|
20
|
+
id: { field: 'activityid', type: 'string' },
|
|
21
|
+
status: { field: 'defra_activitystatus', type: 'integer' },
|
|
22
|
+
season: { field: 'defra_season', type: 'integer' },
|
|
23
|
+
startDate: { field: 'actualstart', type: 'datetime' },
|
|
24
|
+
lastUpdated: { field: 'modifiedon', type: 'datetime' },
|
|
25
|
+
submittedDate: { field: 'actualend', type: 'datetime' }
|
|
26
|
+
},
|
|
27
|
+
relationships: {
|
|
28
|
+
licensee: { property: 'regardingobjectid_contact_defra_rcractivity', entity: Contact, parent: true }
|
|
29
|
+
}
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The {@link EntityDefinition} providing mappings between Dynamics entity and the local entity
|
|
34
|
+
* @type {EntityDefinition}
|
|
35
|
+
*/
|
|
36
|
+
static get definition () {
|
|
37
|
+
return RCRActivity._definition
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The status associated with the rcr activity
|
|
42
|
+
* @type {RCR_ACTIVITY_STATUS}
|
|
43
|
+
*/
|
|
44
|
+
get status () {
|
|
45
|
+
return super._getState('status')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
set status (status) {
|
|
49
|
+
super._setState('status', status)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The season associated with the rcr activity
|
|
54
|
+
* @type {number}
|
|
55
|
+
*/
|
|
56
|
+
get season () {
|
|
57
|
+
return super._getState('season')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
set season (season) {
|
|
61
|
+
super._setState('season', season)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The start date associated with the rcr activity
|
|
66
|
+
* @type {date}
|
|
67
|
+
*/
|
|
68
|
+
get startDate () {
|
|
69
|
+
return super._getState('startDate')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
set startDate (startDate) {
|
|
73
|
+
super._setState('startDate', startDate)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The last updated date associated with the rcr activity
|
|
78
|
+
* @type {date}
|
|
79
|
+
*/
|
|
80
|
+
get lastUpdated () {
|
|
81
|
+
return super._getState('lastUpdated')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The submitted date associated with the rcr activity
|
|
86
|
+
* @type {date}
|
|
87
|
+
*/
|
|
88
|
+
get submittedDate () {
|
|
89
|
+
return super._getState('submittedDate')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
set submittedDate (submittedDate) {
|
|
93
|
+
super._setState('submittedDate', submittedDate)
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from './entities/transaction-currency.entity.js'
|
|
|
13
13
|
export * from './entities/pocl-file.entity.js'
|
|
14
14
|
export * from './entities/pocl-staging-exception.entity.js'
|
|
15
15
|
export * from './entities/pocl-validation-error.entity.js'
|
|
16
|
+
export * from './entities/rcr-activity.entity.js'
|
|
16
17
|
export * from './entities/recurring-payment.entity.js'
|
|
17
18
|
export * from './entities/recurring-payment-instruction.entity.js'
|
|
18
19
|
export * from './entities/staging-exception.entity.js'
|
|
@@ -28,7 +29,7 @@ export * from './queries/concession-proof.queries.js'
|
|
|
28
29
|
export * from './queries/pocl-validation-error.queries.js'
|
|
29
30
|
export * from './queries/recurring-payments.queries.js'
|
|
30
31
|
export * from './queries/contact.queries.js'
|
|
31
|
-
export * from './queries/activity.queries.js'
|
|
32
|
+
export * from './queries/rcr-activity.queries.js'
|
|
32
33
|
|
|
33
34
|
// Framework functionality
|
|
34
35
|
export * from './client/util.js'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dynamicsClient } from '../../client/dynamics-client.js'
|
|
1
|
+
import { contactAndPermissionForLicensee, contactForLicenseeNoReference } from '../contact.queries.js'
|
|
3
2
|
import { Contact } from '../../entities/contact.entity.js'
|
|
3
|
+
import { Permission } from '../../entities/permission.entity.js'
|
|
4
4
|
import { PredefinedQuery } from '../predefined-query.js'
|
|
5
5
|
|
|
6
6
|
jest.mock('dynamics-web-api', () => {
|
|
@@ -12,94 +12,6 @@ jest.mock('dynamics-web-api', () => {
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
describe('Contact Queries', () => {
|
|
15
|
-
describe('contactForLicensee', () => {
|
|
16
|
-
const mockResponse = {
|
|
17
|
-
ContactId: 'f1bb733e-3b1e-ea11-a810-000d3a25c5d6',
|
|
18
|
-
FirstName: 'Fester',
|
|
19
|
-
LastName: 'Tester',
|
|
20
|
-
DateOfBirth: '9/13/1946 12:00:00 AM',
|
|
21
|
-
Premises: '47',
|
|
22
|
-
Street: null,
|
|
23
|
-
Town: 'Testerton',
|
|
24
|
-
Locality: null,
|
|
25
|
-
Postcode: 'AB12 3CD',
|
|
26
|
-
ReturnStatus: 'success',
|
|
27
|
-
SuccessMessage: 'contact found successfully',
|
|
28
|
-
ErrorMessage: null,
|
|
29
|
-
ReturnPermissionNumber: '11100420-2WT1SFT-KPMW2C',
|
|
30
|
-
oDataContext: 'https://api.com/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.defra_GetContactByLicenceAndPostcodeResponse'
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const noContactResponse = {
|
|
34
|
-
ContactId: null,
|
|
35
|
-
FirstName: null,
|
|
36
|
-
LastName: null,
|
|
37
|
-
DateOfBirth: null,
|
|
38
|
-
Premises: null,
|
|
39
|
-
Street: null,
|
|
40
|
-
Town: null,
|
|
41
|
-
Locality: null,
|
|
42
|
-
Postcode: null,
|
|
43
|
-
ReturnStatus: 'error',
|
|
44
|
-
SuccessMessage: '',
|
|
45
|
-
ErrorMessage: 'contact does not exists',
|
|
46
|
-
ReturnPermissionNumber: null,
|
|
47
|
-
oDataContext:
|
|
48
|
-
'https://api.crm4.dynamics.com/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.defra_GetContactByLicenceAndPostcodeResponse'
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
it('should call dynamicsClient with correct parameters', async () => {
|
|
52
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(mockResponse)
|
|
53
|
-
|
|
54
|
-
const permissionNumber = 'KPMW2C'
|
|
55
|
-
const postcode = 'AB12 3CD'
|
|
56
|
-
|
|
57
|
-
await contactForLicensee(permissionNumber, postcode)
|
|
58
|
-
|
|
59
|
-
expect(dynamicsClient.executeUnboundAction).toHaveBeenCalledWith('defra_GetContactByLicenceAndPostcode', {
|
|
60
|
-
PermissionNumber: permissionNumber,
|
|
61
|
-
InputPostCode: postcode
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('should return the CRM response correctly', async () => {
|
|
66
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(mockResponse)
|
|
67
|
-
|
|
68
|
-
const result = await contactForLicensee('KPMW2C', 'AB12 3CD')
|
|
69
|
-
|
|
70
|
-
expect(result).toEqual(mockResponse)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('should handle error in dynamicsClient response', async () => {
|
|
74
|
-
const error = new Error('Failed to fetch data')
|
|
75
|
-
dynamicsClient.executeUnboundAction.mockRejectedValue(error)
|
|
76
|
-
|
|
77
|
-
await expect(contactForLicensee('KPMW2C', 'AB12 3CD')).rejects.toThrow('Failed to fetch data')
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('should handle the case where contact does not exist', async () => {
|
|
81
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(noContactResponse)
|
|
82
|
-
|
|
83
|
-
const result = await contactForLicensee('654321', 'ZZ1 1ZZ')
|
|
84
|
-
|
|
85
|
-
expect(result).toMatchObject({
|
|
86
|
-
ContactId: null,
|
|
87
|
-
FirstName: null,
|
|
88
|
-
LastName: null,
|
|
89
|
-
DateOfBirth: null,
|
|
90
|
-
Premises: null,
|
|
91
|
-
Street: null,
|
|
92
|
-
Town: null,
|
|
93
|
-
Locality: null,
|
|
94
|
-
Postcode: null,
|
|
95
|
-
ReturnStatus: 'error',
|
|
96
|
-
SuccessMessage: '',
|
|
97
|
-
ErrorMessage: 'contact does not exists',
|
|
98
|
-
ReturnPermissionNumber: null
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
15
|
describe('contactForLicenseeNoReference', () => {
|
|
104
16
|
beforeEach(() => {
|
|
105
17
|
jest.resetAllMocks()
|
|
@@ -145,4 +57,85 @@ describe('Contact Queries', () => {
|
|
|
145
57
|
})
|
|
146
58
|
})
|
|
147
59
|
})
|
|
60
|
+
|
|
61
|
+
describe('contactAndPermissionForLicensee', () => {
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
jest.resetAllMocks()
|
|
64
|
+
|
|
65
|
+
jest.spyOn(Contact.definition, 'mappings', 'get').mockReturnValue({
|
|
66
|
+
id: { field: 'contactid' },
|
|
67
|
+
postcode: { field: 'defra_postcode' }
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should return a predefined query', () => {
|
|
72
|
+
const result = contactAndPermissionForLicensee('ABC123', 'AB12 3CD')
|
|
73
|
+
expect(result).toBeInstanceOf(PredefinedQuery)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('root should return Permission', () => {
|
|
77
|
+
const result = contactAndPermissionForLicensee('ABC123', 'AB12 3CD')
|
|
78
|
+
expect(result._root).toEqual(Permission)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('should build correct filter', () => {
|
|
82
|
+
const result = contactAndPermissionForLicensee('ABC123', 'AB12 3CD')
|
|
83
|
+
|
|
84
|
+
expect(result._retrieveRequest.filter).toEqual(
|
|
85
|
+
"endswith(defra_name, 'ABC123') and statecode eq 0 and defra_ContactId/defra_postcode eq 'AB12 3CD'"
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should build correct orderBy', () => {
|
|
90
|
+
const result = contactAndPermissionForLicensee('ABC123', 'AB12 3CD')
|
|
91
|
+
|
|
92
|
+
expect(result._retrieveRequest.orderBy).toEqual(['defra_issuedate desc', 'defra_ContactId/contactid asc'])
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should set expand correctly', () => {
|
|
96
|
+
const result = contactAndPermissionForLicensee('ABC123', 'AB12 3CD')
|
|
97
|
+
|
|
98
|
+
expect(result._retrieveRequest.expand).toEqual([
|
|
99
|
+
{
|
|
100
|
+
property: 'defra_ContactId',
|
|
101
|
+
select: ['contactid', 'defra_postcode']
|
|
102
|
+
}
|
|
103
|
+
])
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it.each([
|
|
107
|
+
['XYZ999', 'EF45 6GH'],
|
|
108
|
+
['123ABC', 'IJ78 9KL'],
|
|
109
|
+
['AAAAAA', 'ZZ99 9ZZ']
|
|
110
|
+
])(
|
|
111
|
+
'should return correct retrieve request when the last 6 characters of the permission is %s and postcode is %s',
|
|
112
|
+
(permissionLast6, postcode) => {
|
|
113
|
+
const result = contactAndPermissionForLicensee(permissionLast6, postcode)
|
|
114
|
+
|
|
115
|
+
expect(result._retrieveRequest).toEqual({
|
|
116
|
+
collection: 'defra_permissions',
|
|
117
|
+
filter: `endswith(defra_name, '${permissionLast6}') and statecode eq 0 and defra_ContactId/defra_postcode eq '${postcode}'`,
|
|
118
|
+
orderBy: ['defra_issuedate desc', 'defra_ContactId/contactid asc'],
|
|
119
|
+
expand: [
|
|
120
|
+
{
|
|
121
|
+
property: 'defra_ContactId',
|
|
122
|
+
select: ['contactid', 'defra_postcode']
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
select: [
|
|
126
|
+
'defra_permissionid',
|
|
127
|
+
'defra_name',
|
|
128
|
+
'defra_issuedate',
|
|
129
|
+
'defra_startdate',
|
|
130
|
+
'defra_enddate',
|
|
131
|
+
'defra_stagingid',
|
|
132
|
+
'defra_datasource',
|
|
133
|
+
'defra_renewal',
|
|
134
|
+
'defra_rcpagreement',
|
|
135
|
+
'defra_licenceforyou'
|
|
136
|
+
]
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
})
|
|
148
141
|
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RCRActivity } from '../../entities/rcr-activity.entity.js'
|
|
2
|
+
import { PredefinedQuery } from '../predefined-query.js'
|
|
3
|
+
import { rcrActivityForContact } from '../rcr-activity.queries.js'
|
|
4
|
+
|
|
5
|
+
describe('rcrActivityForContact', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
jest.resetAllMocks()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('should return a predefined query', () => {
|
|
11
|
+
const result = rcrActivityForContact('CONTACT123', 2024)
|
|
12
|
+
expect(result).toBeInstanceOf(PredefinedQuery)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('root should return RCRActivity', () => {
|
|
16
|
+
const result = rcrActivityForContact('CONTACT123', 2024)
|
|
17
|
+
expect(result._root).toEqual(RCRActivity)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should build correct filter', () => {
|
|
21
|
+
const result = rcrActivityForContact('CONTACT123', 2024)
|
|
22
|
+
|
|
23
|
+
expect(result._retrieveRequest.filter).toEqual(
|
|
24
|
+
"regardingobjectid_contact_defra_rcractivity/contactid eq 'CONTACT123' and defra_season eq 2024 and statecode eq 0"
|
|
25
|
+
)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should set expand to empty array', () => {
|
|
29
|
+
const result = rcrActivityForContact('CONTACT123', 2024)
|
|
30
|
+
|
|
31
|
+
expect(result._retrieveRequest.expand).toEqual([])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it.each([
|
|
35
|
+
['ABC123', 2023],
|
|
36
|
+
['XYZ999', 2024],
|
|
37
|
+
['AAAAAA', 2025]
|
|
38
|
+
])('should return correct retrieve request when contactId is %s and season is %s', (contactId, season) => {
|
|
39
|
+
const result = rcrActivityForContact(contactId, season)
|
|
40
|
+
|
|
41
|
+
expect(result._retrieveRequest).toEqual({
|
|
42
|
+
collection: 'defra_rcractivities',
|
|
43
|
+
filter: `regardingobjectid_contact_defra_rcractivity/contactid eq '${contactId}' and defra_season eq ${season} and statecode eq 0`,
|
|
44
|
+
expand: [],
|
|
45
|
+
select: ['activityid', 'defra_activitystatus', 'defra_season', 'actualstart', 'modifiedon', 'actualend']
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -1,42 +1,8 @@
|
|
|
1
|
-
import { dynamicsClient } from '../client/dynamics-client.js'
|
|
2
1
|
import { Contact } from '../entities/contact.entity.js'
|
|
2
|
+
import { Permission } from '../entities/permission.entity.js'
|
|
3
3
|
import { escapeODataStringValue } from '../client/util.js'
|
|
4
4
|
import { PredefinedQuery } from './predefined-query.js'
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} ContactByLicenceAndPostcode
|
|
8
|
-
* @property {string|null} ContactId - The contact's unique identifier
|
|
9
|
-
* @property {string|null} FirstName - The contact's first name
|
|
10
|
-
* @property {string|null} LastName - The contact's last name
|
|
11
|
-
* @property {string|null} DateOfBirth - The contact's date of birth
|
|
12
|
-
* @property {string|null} Street - The contact's street
|
|
13
|
-
* @property {string|null} Town - The contact's town
|
|
14
|
-
* @property {string|null} Locality - The contact's locality
|
|
15
|
-
* @property {string|null} Postcode - The contact's postcode
|
|
16
|
-
* @property {string} ReturnStatus - The status of the request (e.g., "success" or "error")
|
|
17
|
-
* @property {string|null} SuccessMessage - A success message if the contact is found
|
|
18
|
-
* @property {string|null} ErrorMessage - An error message if the contact is not found
|
|
19
|
-
* @property {string|null} ReturnPermissionNumber - The full permission number of the contact
|
|
20
|
-
* @property {string} oDataContext - The OData context URL
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Calls the defra_GetContactByLicenceAndPostcode CRM plugin to retrieve a contact by the last 6 characters if their license number and postcode
|
|
25
|
-
*
|
|
26
|
-
* @param permissionReferenceNumberLast6Characters the last 6 characters of the permission reference number
|
|
27
|
-
* @param licenseePostcode the postcode of the contact associated with the permission
|
|
28
|
-
* @returns {Promise<ContactByLicenceAndPostcode>}
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
export const contactForLicensee = (permissionReferenceNumberLast6Characters, licenseePostcode) => {
|
|
32
|
-
const request = {
|
|
33
|
-
PermissionNumber: permissionReferenceNumberLast6Characters,
|
|
34
|
-
InputPostCode: licenseePostcode
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return dynamicsClient.executeUnboundAction('defra_GetContactByLicenceAndPostcode', request)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
6
|
export const contactForLicenseeNoReference = (licenseeBirthDate, licenseePostcode) => {
|
|
41
7
|
const { postcode, birthDate } = Contact.definition.mappings
|
|
42
8
|
const filter = `${postcode.field} eq '${escapeODataStringValue(licenseePostcode)}' and ${birthDate.field} eq ${licenseeBirthDate} and ${
|
|
@@ -48,3 +14,37 @@ export const contactForLicenseeNoReference = (licenseeBirthDate, licenseePostcod
|
|
|
48
14
|
expand: []
|
|
49
15
|
})
|
|
50
16
|
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Gets the query to get a contact by the last 6 characters if their license number and postcode
|
|
20
|
+
*
|
|
21
|
+
* @param {string} permissionLast6Characters the last 6 characters of the permission reference number
|
|
22
|
+
* @param {string} licenseePostcode the postcode of the contact associated with the permission
|
|
23
|
+
* @returns {PredefinedQuery<Permission>} returns a query as an object to fetch the contact
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export const contactAndPermissionForLicensee = (permissionLast6Characters, licenseePostcode) => {
|
|
27
|
+
const { referenceNumber, issueDate } = Permission.definition.mappings
|
|
28
|
+
const { licensee } = Permission.definition.relationships
|
|
29
|
+
const { id } = Contact.definition.mappings
|
|
30
|
+
|
|
31
|
+
const filter = `endswith(${referenceNumber.field}, '${escapeODataStringValue(permissionLast6Characters)}') and ${
|
|
32
|
+
Permission.definition.defaultFilter
|
|
33
|
+
} and ${licensee.property}/${Contact.definition.mappings.postcode.field} eq '${escapeODataStringValue(licenseePostcode)}'`
|
|
34
|
+
const orderBy = [`${issueDate.field} desc`, `${licensee.property}/${id.field} asc`]
|
|
35
|
+
|
|
36
|
+
const query = new PredefinedQuery({
|
|
37
|
+
root: Permission,
|
|
38
|
+
filter,
|
|
39
|
+
orderBy
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
query._retrieveRequest.expand = [
|
|
43
|
+
{
|
|
44
|
+
property: Permission.definition.relationships.licensee.property,
|
|
45
|
+
select: [Contact.definition.mappings.id.field, Contact.definition.mappings.postcode.field]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
return query
|
|
50
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Contact } from '../entities/contact.entity.js'
|
|
2
|
+
import { RCRActivity } from '../entities/rcr-activity.entity.js'
|
|
3
|
+
import { escapeODataStringValue } from '../client/util.js'
|
|
4
|
+
import { PredefinedQuery } from './predefined-query.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a predefined OData query for retrieving {@link RCRActivity} records
|
|
8
|
+
* associated with a specific contact id and season.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} contactId The unique identifier of the {@link Contact}.
|
|
11
|
+
* @param {string|number} seasonInput The season value to filter activities by.
|
|
12
|
+
* @returns {PredefinedQuery<RCRActivity>} returns a query as an object to fetch the contact
|
|
13
|
+
*/
|
|
14
|
+
export const rcrActivityForContact = (contactId, seasonInput) => {
|
|
15
|
+
const { season } = RCRActivity.definition.mappings
|
|
16
|
+
const { licensee } = RCRActivity.definition.relationships
|
|
17
|
+
const { id } = Contact.definition.mappings
|
|
18
|
+
|
|
19
|
+
const filter = `${licensee.property}/${id.field} eq '${escapeODataStringValue(contactId)}' and ${
|
|
20
|
+
season.field
|
|
21
|
+
} eq ${escapeODataStringValue(seasonInput)} and ${RCRActivity.definition.defaultFilter}`
|
|
22
|
+
return new PredefinedQuery({
|
|
23
|
+
root: RCRActivity,
|
|
24
|
+
filter,
|
|
25
|
+
expand: []
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { createActivity, updateActivity } from '../activity.queries.js'
|
|
2
|
-
import { dynamicsClient } from '../../client/dynamics-client.js'
|
|
3
|
-
|
|
4
|
-
jest.mock('dynamics-web-api', () => {
|
|
5
|
-
return jest.fn().mockImplementation(() => {
|
|
6
|
-
return {
|
|
7
|
-
executeUnboundAction: jest.fn()
|
|
8
|
-
}
|
|
9
|
-
})
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
describe('Activity Service', () => {
|
|
13
|
-
describe('createActivity', () => {
|
|
14
|
-
const getSuccessResponse = () => ({
|
|
15
|
-
'@odata.context': 'https://dynamics.com/api/data/v9.1/defra_CreateRCRActivityResponse',
|
|
16
|
-
RCRActivityId: 'abc123',
|
|
17
|
-
ReturnStatus: 'success',
|
|
18
|
-
SuccessMessage: 'RCR Activity - created successfully',
|
|
19
|
-
ErrorMessage: null,
|
|
20
|
-
oDataContext: 'https://dynamics.com/api/data/v9.1/defra_CreateRCRActivityResponse'
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
const getErrorResponse = () => ({
|
|
24
|
-
'@odata.context': 'https://dynamics.com/api/data/v9.1/.defra_CreateRCRActivityResponse',
|
|
25
|
-
RCRActivityId: null,
|
|
26
|
-
ReturnStatus: 'error',
|
|
27
|
-
SuccessMessage: '',
|
|
28
|
-
ErrorMessage: 'Failed to create activity',
|
|
29
|
-
oDataContext: 'https://dynamics.com/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.defra_CreateRCRActivityResponse'
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('should call dynamicsClient with correct parameters', async () => {
|
|
33
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(getSuccessResponse())
|
|
34
|
-
|
|
35
|
-
await createActivity('contact-identifier-123', 2023)
|
|
36
|
-
|
|
37
|
-
expect(dynamicsClient.executeUnboundAction).toHaveBeenCalledWith('defra_CreateRCRActivity', {
|
|
38
|
-
ContactId: 'contact-identifier-123',
|
|
39
|
-
ActivityStatus: 'STARTED',
|
|
40
|
-
Season: 2023
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('should return the CRM response correctly', async () => {
|
|
45
|
-
const successResponse = getSuccessResponse()
|
|
46
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(successResponse)
|
|
47
|
-
|
|
48
|
-
const result = await createActivity('contact-identifier-123', 2024)
|
|
49
|
-
|
|
50
|
-
expect(result).toEqual(successResponse)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should handle error in dynamicsClient response', async () => {
|
|
54
|
-
const error = new Error('Failed to create activity')
|
|
55
|
-
dynamicsClient.executeUnboundAction.mockRejectedValue(error)
|
|
56
|
-
|
|
57
|
-
await expect(createActivity('contact-identifier-123', 2024)).rejects.toThrow('Failed to create activity')
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should handle the case where activity creation fails', async () => {
|
|
61
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(getErrorResponse())
|
|
62
|
-
|
|
63
|
-
const result = await createActivity('invalid-contact-id', 2024)
|
|
64
|
-
|
|
65
|
-
expect(result).toMatchObject({
|
|
66
|
-
RCRActivityId: null,
|
|
67
|
-
ReturnStatus: 'error',
|
|
68
|
-
SuccessMessage: '',
|
|
69
|
-
ErrorMessage: 'Failed to create activity'
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
describe('updateActivity', () => {
|
|
75
|
-
const getSuccessResponse = () => ({
|
|
76
|
-
'@odata.context': 'https://dynamics.om/api/data/v9.1/defra_UpdateRCRActivityResponse',
|
|
77
|
-
ReturnStatus: 'success',
|
|
78
|
-
SuccessMessage: 'RCR Activity - updated successfully',
|
|
79
|
-
ErrorMessage: null,
|
|
80
|
-
oDataContext: 'https://dynamics.com/api/data/v9.1/defra_UpdateRCRActivityResponse'
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
const getErrorResponse = () => ({
|
|
84
|
-
'@odata.context': 'https://dynamics.om/api/data/v9.1/defra_UpdateRCRActivityResponse',
|
|
85
|
-
RCRActivityId: null,
|
|
86
|
-
ReturnStatus: 'error',
|
|
87
|
-
SuccessMessage: '',
|
|
88
|
-
ErrorMessage: 'Failed to update activity',
|
|
89
|
-
oDataContext: 'https://dynamics.com/api/data/v9.1/defra_UpdateRCRActivityResponse'
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('should call dynamicsClient with correct parameters', async () => {
|
|
93
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(getSuccessResponse())
|
|
94
|
-
|
|
95
|
-
await updateActivity('contact-identifier-123', 2023)
|
|
96
|
-
|
|
97
|
-
expect(dynamicsClient.executeUnboundAction).toHaveBeenCalledWith('defra_UpdateRCRActivity', {
|
|
98
|
-
ContactId: 'contact-identifier-123',
|
|
99
|
-
ActivityStatus: 'SUBMITTED',
|
|
100
|
-
Season: 2023
|
|
101
|
-
})
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('should return the CRM response correctly', async () => {
|
|
105
|
-
const successResponse = getSuccessResponse()
|
|
106
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(successResponse)
|
|
107
|
-
|
|
108
|
-
const result = await updateActivity('contact-identifier-123', 2024)
|
|
109
|
-
|
|
110
|
-
expect(result).toEqual(successResponse)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('should handle error in dynamicsClient response', async () => {
|
|
114
|
-
const error = new Error('Failed to update activity')
|
|
115
|
-
dynamicsClient.executeUnboundAction.mockRejectedValue(error)
|
|
116
|
-
|
|
117
|
-
await expect(updateActivity('contact-identifier-123', 2024)).rejects.toThrow('Failed to update activity')
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it('should handle the case where activity creation fails', async () => {
|
|
121
|
-
dynamicsClient.executeUnboundAction.mockResolvedValue(getErrorResponse())
|
|
122
|
-
|
|
123
|
-
const result = await updateActivity('invalid-contact-id', 2024)
|
|
124
|
-
|
|
125
|
-
expect(result).toMatchObject({
|
|
126
|
-
RCRActivityId: null,
|
|
127
|
-
ReturnStatus: 'error',
|
|
128
|
-
SuccessMessage: '',
|
|
129
|
-
ErrorMessage: 'Failed to update activity'
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
})
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { dynamicsClient } from '../client/dynamics-client.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Creates an RCR Activity in Microsoft Dynamics CRM.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} contactId - The ID of the contact associated with the activity.
|
|
7
|
-
* @param {number} season - The season year for which the activity is being created.
|
|
8
|
-
* @returns {Promise<Object>} - A promise that resolves to the response from Dynamics CRM.
|
|
9
|
-
* @property {string} response.@odata.context - The OData context URL of the response.
|
|
10
|
-
* @property {string} response.RCRActivityId - The unique identifier of the created RCR activity.
|
|
11
|
-
* @property {string} response.ReturnStatus - The status of the activity creation operation (e.g., 'success').
|
|
12
|
-
* @property {string} response.SuccessMessage - A message indicating successful creation of the activity.
|
|
13
|
-
* @property {string|null} response.ErrorMessage - An error message if the activity creation failed, otherwise null.
|
|
14
|
-
* @property {string} response.oDataContext - The OData context URL of the response.
|
|
15
|
-
*/
|
|
16
|
-
export const createActivity = (contactId, season) => {
|
|
17
|
-
const request = {
|
|
18
|
-
ContactId: contactId,
|
|
19
|
-
ActivityStatus: 'STARTED',
|
|
20
|
-
Season: season
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return dynamicsClient.executeUnboundAction('defra_CreateRCRActivity', request)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Updates an RCR Activity in Microsoft Dynamics CRM.
|
|
28
|
-
*
|
|
29
|
-
* @param {string} contactId - The ID of the contact associated with the activity.
|
|
30
|
-
* @param {number} season - The season year for which the activity is being created.
|
|
31
|
-
* @returns {Promise<Object>} - A promise that resolves to the response from Dynamics CRM.
|
|
32
|
-
* @property {string} response.@odata.context - The OData context URL of the response.
|
|
33
|
-
* @property {string} response.RCRActivityId - The unique identifier of the created RCR activity.
|
|
34
|
-
* @property {string} response.ReturnStatus - The status of the activity creation operation (e.g., 'success').
|
|
35
|
-
* @property {string} response.SuccessMessage - A message indicating successful creation of the activity.
|
|
36
|
-
* @property {string|null} response.ErrorMessage - An error message if the activity creation failed, otherwise null.
|
|
37
|
-
* @property {string} response.oDataContext - The OData context URL of the response.
|
|
38
|
-
*/
|
|
39
|
-
export const updateActivity = (contactId, season) => {
|
|
40
|
-
const request = {
|
|
41
|
-
ContactId: contactId,
|
|
42
|
-
ActivityStatus: 'SUBMITTED',
|
|
43
|
-
Season: season
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return dynamicsClient.executeUnboundAction('defra_UpdateRCRActivity', request)
|
|
47
|
-
}
|