@defra-fish/dynamics-lib 1.68.0 → 1.69.0-rc.1

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@defra-fish/dynamics-lib",
3
- "version": "1.68.0",
3
+ "version": "1.69.0-rc.1",
4
4
  "description": "Framework to support integration with dynamics",
5
5
  "type": "module",
6
6
  "engines": {
7
- "node": ">=20"
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": "06c0080280b167f83b426a645e187c819390de4b"
46
+ "gitHead": "48305c1b0275771007639d2539b1571d937c2d55"
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 { contactForLicensee, contactForLicenseeNoReference } from '../contact.queries.js'
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
- }