pha-hermes 1.0.5 → 1.2.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/dist/api/apiClient.d.ts +3 -2
- package/dist/api/timesheet/timesheetClient.d.ts +2 -0
- package/dist/api/workorder/workorderClient.d.ts +7 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/pha-hermes.cjs.development.js +161 -84
- package/dist/pha-hermes.cjs.development.js.map +1 -1
- package/dist/pha-hermes.cjs.production.min.js +1 -1
- package/dist/pha-hermes.cjs.production.min.js.map +1 -1
- package/dist/pha-hermes.esm.js +161 -84
- package/dist/pha-hermes.esm.js.map +1 -1
- package/package.json +9 -5
- package/src/api/apiClient.ts +11 -28
- package/src/api/auth/auth.ts +48 -48
- package/src/api/timesheet/timesheetClient.ts +37 -3
- package/src/api/workorder/workorderClient.ts +43 -0
- package/src/models/index.ts +5 -0
- package/src/models/practitioner.ts +1 -1
- package/src/models/timesheet.ts +21 -21
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0
|
|
2
|
+
"version": "1.2.0",
|
|
3
3
|
"main": "dist/index.js",
|
|
4
4
|
"typings": "dist/index.d.ts",
|
|
5
5
|
"files": [
|
|
@@ -24,10 +24,13 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"prettier": {
|
|
27
|
-
"printWidth": 80,
|
|
28
|
-
"semi": true,
|
|
29
27
|
"singleQuote": true,
|
|
30
|
-
"trailingComma": "
|
|
28
|
+
"trailingComma": "none",
|
|
29
|
+
"printWidth": 100,
|
|
30
|
+
"semi": false,
|
|
31
|
+
"arrowParens": "avoid",
|
|
32
|
+
"bracketSpacing": true,
|
|
33
|
+
"tabWidth": 4
|
|
31
34
|
},
|
|
32
35
|
"name": "pha-hermes",
|
|
33
36
|
"author": "Pierre Seguin",
|
|
@@ -52,6 +55,7 @@
|
|
|
52
55
|
"size-limit": "^11.1.6",
|
|
53
56
|
"tsdx": "^0.14.1",
|
|
54
57
|
"tslib": "^2.8.1",
|
|
55
|
-
"typescript": "^3.9.10"
|
|
58
|
+
"typescript": "^3.9.10",
|
|
59
|
+
"vitest": "^3.0.5"
|
|
56
60
|
}
|
|
57
61
|
}
|
package/src/api/apiClient.ts
CHANGED
|
@@ -2,7 +2,9 @@ import axios, { AxiosInstance } from 'axios'
|
|
|
2
2
|
import { SFAuthenticator } from './auth/auth'
|
|
3
3
|
import { SFTimesheetClient } from './timesheet/timesheetClient'
|
|
4
4
|
import { SFPractitionerClient } from './practitioner/practitionerClient'
|
|
5
|
-
import { Role
|
|
5
|
+
import { Role } from '../models'
|
|
6
|
+
import { SFWorkorderClient } from './workorder/workorderClient'
|
|
7
|
+
|
|
6
8
|
export const SF_API_VERSION: string = 'v57.0'
|
|
7
9
|
|
|
8
10
|
export class SFApiClient {
|
|
@@ -11,6 +13,7 @@ export class SFApiClient {
|
|
|
11
13
|
private authenticator: SFAuthenticator
|
|
12
14
|
private baseUrl: string
|
|
13
15
|
timesheetClient: SFTimesheetClient
|
|
16
|
+
workorderClient: SFWorkorderClient
|
|
14
17
|
practitionerClient: SFPractitionerClient
|
|
15
18
|
|
|
16
19
|
constructor(baseUrl?: string) {
|
|
@@ -19,6 +22,7 @@ export class SFApiClient {
|
|
|
19
22
|
baseUrl ?? 'https://do0000000d247eaa--phealth.sandbox.my.salesforce.com'
|
|
20
23
|
this.authenticator = new SFAuthenticator(this.axiosInstance, this.baseUrl)
|
|
21
24
|
this.timesheetClient = new SFTimesheetClient(this.axiosInstance)
|
|
25
|
+
this.workorderClient = new SFWorkorderClient(this.axiosInstance)
|
|
22
26
|
this.practitionerClient = new SFPractitionerClient(this.axiosInstance)
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -34,32 +38,6 @@ export class SFApiClient {
|
|
|
34
38
|
return this.instance
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
async fetchWorkOrders(assignedStaffId?: string): Promise<Workorder[]> {
|
|
38
|
-
try {
|
|
39
|
-
const url = `/services/data/${SF_API_VERSION}/query`
|
|
40
|
-
const query = assignedStaffId
|
|
41
|
-
? `SELECT Id, HospitalID__c, HospitalName__c, ProfessionalDesignation__c FROM WorkOrder__c WHERE Personnel__c = '${assignedStaffId}'`
|
|
42
|
-
: `SELECT Id, HospitalID__c, HospitalName__c, ProfessionalDesignation__c FROM WorkOrder__c`
|
|
43
|
-
return await this.axiosInstance
|
|
44
|
-
.get(url, {
|
|
45
|
-
params: { q: query }
|
|
46
|
-
})
|
|
47
|
-
.then(({ data: { records } }) =>
|
|
48
|
-
records.map(
|
|
49
|
-
(record): Workorder => ({
|
|
50
|
-
id: record.Id,
|
|
51
|
-
establishmentId: record.HospitalID__c,
|
|
52
|
-
establishmentName: record.HospitalName__c,
|
|
53
|
-
role: record.ProfessionalDesignation__c
|
|
54
|
-
})
|
|
55
|
-
)
|
|
56
|
-
)
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error('Error fetching work orders: ', error.message)
|
|
59
|
-
throw error
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
41
|
async fetchRoles(): Promise<Role[]> {
|
|
64
42
|
try {
|
|
65
43
|
const url = `/services/data/${SF_API_VERSION}/query`
|
|
@@ -72,7 +50,12 @@ export class SFApiClient {
|
|
|
72
50
|
params: { q: query }
|
|
73
51
|
})
|
|
74
52
|
.then(({ data: { records } }) =>
|
|
75
|
-
records.map(
|
|
53
|
+
records.map(
|
|
54
|
+
(record): Role => ({
|
|
55
|
+
label: record.Label,
|
|
56
|
+
value: record.Value
|
|
57
|
+
})
|
|
58
|
+
)
|
|
76
59
|
)
|
|
77
60
|
} catch (error) {
|
|
78
61
|
console.error('Error fetching roles: ', error.message)
|
package/src/api/auth/auth.ts
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import axios, { AxiosInstance } from 'axios'
|
|
2
|
-
import FormData from 'form-data'
|
|
3
|
-
|
|
4
|
-
export class SFAuthenticator {
|
|
5
|
-
axiosTokenInstance: AxiosInstance = axios.create()
|
|
6
|
-
authenticatedAxiosInstance: AxiosInstance
|
|
7
|
-
token: string | undefined = undefined
|
|
8
|
-
private refreshToken: string | undefined = undefined
|
|
9
|
-
|
|
10
|
-
constructor(authenticatedAxiosInstance: AxiosInstance, baseUrl?: string) {
|
|
11
|
-
this.axiosTokenInstance.defaults.baseURL =
|
|
12
|
-
baseUrl ?? 'https://do0000000d247eaa--phealth.sandbox.my.salesforce.com'
|
|
13
|
-
this.authenticatedAxiosInstance = authenticatedAxiosInstance
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async initializeAuth(clientId: string, clientSecret: string) {
|
|
17
|
-
const token = await this.postToken(clientId, clientSecret)
|
|
18
|
-
this.authenticatedAxiosInstance.defaults.headers.common['Authorization'] = `Bearer ${token}`
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async postToken(clientId?: string, clientSecret?: string): Promise<string> {
|
|
22
|
-
const tokenFormRequestBody = new FormData()
|
|
23
|
-
if (this.refreshToken) {
|
|
24
|
-
tokenFormRequestBody.append('refresh_token', this.refreshToken)
|
|
25
|
-
tokenFormRequestBody.append('grant_type', 'refresh_token')
|
|
26
|
-
} else if (clientId && clientSecret) {
|
|
27
|
-
tokenFormRequestBody.append('client_id', clientId)
|
|
28
|
-
tokenFormRequestBody.append('client_secret', clientSecret)
|
|
29
|
-
tokenFormRequestBody.append('grant_type', 'client_credentials')
|
|
30
|
-
} else {
|
|
31
|
-
throw new Error(
|
|
32
|
-
'Authentication failed, either the RefreshToken, or ClientId and ClientSecret need to be provided'
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const response = await this.axiosTokenInstance.post(
|
|
37
|
-
'/services/oauth2/token',
|
|
38
|
-
tokenFormRequestBody,
|
|
39
|
-
{
|
|
40
|
-
headers: tokenFormRequestBody.getHeaders()
|
|
41
|
-
}
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
this.token = response?.data?.['access_token']
|
|
45
|
-
if (!this.token) throw new Error('Authentication failed, could not get the access token')
|
|
46
|
-
return this.token
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
import axios, { AxiosInstance } from 'axios'
|
|
2
|
+
import FormData from 'form-data'
|
|
3
|
+
|
|
4
|
+
export class SFAuthenticator {
|
|
5
|
+
axiosTokenInstance: AxiosInstance = axios.create()
|
|
6
|
+
authenticatedAxiosInstance: AxiosInstance
|
|
7
|
+
token: string | undefined = undefined
|
|
8
|
+
private refreshToken: string | undefined = undefined
|
|
9
|
+
|
|
10
|
+
constructor(authenticatedAxiosInstance: AxiosInstance, baseUrl?: string) {
|
|
11
|
+
this.axiosTokenInstance.defaults.baseURL =
|
|
12
|
+
baseUrl ?? 'https://do0000000d247eaa--phealth.sandbox.my.salesforce.com'
|
|
13
|
+
this.authenticatedAxiosInstance = authenticatedAxiosInstance
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async initializeAuth(clientId: string, clientSecret: string) {
|
|
17
|
+
const token = await this.postToken(clientId, clientSecret)
|
|
18
|
+
this.authenticatedAxiosInstance.defaults.headers.common['Authorization'] = `Bearer ${token}`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async postToken(clientId?: string, clientSecret?: string): Promise<string> {
|
|
22
|
+
const tokenFormRequestBody = new FormData()
|
|
23
|
+
if (this.refreshToken) {
|
|
24
|
+
tokenFormRequestBody.append('refresh_token', this.refreshToken)
|
|
25
|
+
tokenFormRequestBody.append('grant_type', 'refresh_token')
|
|
26
|
+
} else if (clientId && clientSecret) {
|
|
27
|
+
tokenFormRequestBody.append('client_id', clientId)
|
|
28
|
+
tokenFormRequestBody.append('client_secret', clientSecret)
|
|
29
|
+
tokenFormRequestBody.append('grant_type', 'client_credentials')
|
|
30
|
+
} else {
|
|
31
|
+
throw new Error(
|
|
32
|
+
'Authentication failed, either the RefreshToken, or ClientId and ClientSecret need to be provided'
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const response = await this.axiosTokenInstance.post(
|
|
37
|
+
'/services/oauth2/token',
|
|
38
|
+
tokenFormRequestBody,
|
|
39
|
+
{
|
|
40
|
+
headers: tokenFormRequestBody.getHeaders()
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
this.token = response?.data?.['access_token']
|
|
45
|
+
if (!this.token) throw new Error('Authentication failed, could not get the access token')
|
|
46
|
+
return this.token
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -13,7 +13,7 @@ export class SFTimesheetClient {
|
|
|
13
13
|
try {
|
|
14
14
|
const url = `/services/data/${SF_API_VERSION}/sobjects/TimeSheetHour__c/${timesheetHourId}`
|
|
15
15
|
const response = await this.axiosInstance.patch(url, {
|
|
16
|
-
Date__c: timesheet.date,
|
|
16
|
+
Date__c: timesheet.date,
|
|
17
17
|
Callback__c: timesheet.callbackHours,
|
|
18
18
|
HoursEBAdjusted__c: timesheet.regularHours,
|
|
19
19
|
Misc__c: timesheet.miscHours,
|
|
@@ -41,7 +41,10 @@ export class SFTimesheetClient {
|
|
|
41
41
|
async getTimesheetId(workorderId: string): Promise<string> {
|
|
42
42
|
// First we find the correct timesheet id
|
|
43
43
|
const url = `/services/data/${SF_API_VERSION}/query`
|
|
44
|
-
const query = `SELECT Id
|
|
44
|
+
const query = `SELECT Id
|
|
45
|
+
FROM Timesheet__c
|
|
46
|
+
WHERE WorkOrder__c = '${workorderId}'
|
|
47
|
+
LIMIT 1`
|
|
45
48
|
const {
|
|
46
49
|
data: { records }
|
|
47
50
|
} = await this.axiosInstance.get(url, {
|
|
@@ -50,11 +53,42 @@ export class SFTimesheetClient {
|
|
|
50
53
|
return records[0].Id
|
|
51
54
|
}
|
|
52
55
|
|
|
56
|
+
async getTimesheetsForPractitioner(personnelID: string) {
|
|
57
|
+
const url = `/services/data/${SF_API_VERSION}/query`
|
|
58
|
+
|
|
59
|
+
const query = `SELECT FIELDS(STANDARD), WorkOrder__c
|
|
60
|
+
FROM TimeSheet__c
|
|
61
|
+
WHERE WorkOrder__c IN (SELECT Id
|
|
62
|
+
FROM WorkOrder__c
|
|
63
|
+
WHERE Personnel__c = '${personnelID}')`
|
|
64
|
+
|
|
65
|
+
const data = await this.axiosInstance.get(url, { params: { q: query } })
|
|
66
|
+
|
|
67
|
+
return data.data.records
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async getTimesheetsIdAndDateForPractitioner(personnelID: string) {
|
|
71
|
+
const url = `/services/data/${SF_API_VERSION}/query`
|
|
72
|
+
|
|
73
|
+
const query = `SELECT Id, LastModifiedDate
|
|
74
|
+
FROM TimeSheet__c
|
|
75
|
+
WHERE WorkOrder__c IN (SELECT Id
|
|
76
|
+
FROM WorkOrder__c
|
|
77
|
+
WHERE Personnel__c = '${personnelID}')`
|
|
78
|
+
|
|
79
|
+
const data = await this.axiosInstance.get(url, { params: { q: query } })
|
|
80
|
+
|
|
81
|
+
return data.data.records.map(({Id, LastModifiedDate}) => ({Id, LastModifiedDate}))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
53
85
|
async getTimesheetHoursIds(workorderId: string): Promise<string[]> {
|
|
54
86
|
const timesheetId = await this.getTimesheetId(workorderId)
|
|
55
87
|
// First we find the correct timesheet hours id
|
|
56
88
|
const url = `/services/data/${SF_API_VERSION}/query`
|
|
57
|
-
const query = `SELECT Id
|
|
89
|
+
const query = `SELECT Id
|
|
90
|
+
FROM TimesheetHour__c
|
|
91
|
+
WHERE Timesheet__c = '${timesheetId}'`
|
|
58
92
|
const {
|
|
59
93
|
data: { records }
|
|
60
94
|
} = await this.axiosInstance.get(url, {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios'
|
|
2
|
+
import { SF_API_VERSION } from '../apiClient'
|
|
3
|
+
import { Workorder } from '../../models'
|
|
4
|
+
|
|
5
|
+
export class SFWorkorderClient {
|
|
6
|
+
private axiosInstance: AxiosInstance
|
|
7
|
+
|
|
8
|
+
constructor(axiosInstance: AxiosInstance) {
|
|
9
|
+
this.axiosInstance = axiosInstance
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async getWorkordersForPractitioner(assignedStaffId?: string): Promise<Workorder[]> {
|
|
14
|
+
try {
|
|
15
|
+
const url = `/services/data/${SF_API_VERSION}/query`
|
|
16
|
+
const query = assignedStaffId
|
|
17
|
+
? `SELECT Id, HospitalID__c, HospitalName__c, ProfessionalDesignation__c, Personnel__c, startdate__c, EndDate__c, CreatedDate, LastModifiedDate FROM WorkOrder__c WHERE Personnel__c = '${assignedStaffId}'`
|
|
18
|
+
: `SELECT Id, HospitalID__c, HospitalName__c, ProfessionalDesignation__c, Personnel__c, startdate__c, EndDate__c, CreatedDate, LastModifiedDate FROM WorkOrder__c`
|
|
19
|
+
return await this.axiosInstance
|
|
20
|
+
.get(url, {
|
|
21
|
+
params: { q: query }
|
|
22
|
+
})
|
|
23
|
+
.then(({ data: { records } }) =>
|
|
24
|
+
records.map(
|
|
25
|
+
(record): Workorder => ({
|
|
26
|
+
id: record.Id,
|
|
27
|
+
establishmentId: record.HospitalID__c,
|
|
28
|
+
establishmentName: record.HospitalName__c,
|
|
29
|
+
role: record.ProfessionalDesignation__c,
|
|
30
|
+
assignedStaffId: record.Personnel__c,
|
|
31
|
+
startDate: new Date(record.startdate__c),
|
|
32
|
+
endDate: new Date(record.EndDate__c),
|
|
33
|
+
createdAt: new Date(record.CreatedDate),
|
|
34
|
+
updatedAt: new Date(record.LastModifiedDate)
|
|
35
|
+
})
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('Error fetching work orders: ', error.message)
|
|
40
|
+
throw error
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/models/index.ts
CHANGED
package/src/models/timesheet.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
export interface TimesheetDayEntry {
|
|
2
|
-
date?: Date
|
|
3
|
-
callbackHours?: number
|
|
4
|
-
regularHours?: number
|
|
5
|
-
miscHours?: number
|
|
6
|
-
overtime1Hours?: number
|
|
7
|
-
overtime2Hours?: number
|
|
8
|
-
onCallHours?: number
|
|
9
|
-
misc1HoursA?: number
|
|
10
|
-
misc1HoursB?: number
|
|
11
|
-
misc2Hours?: number
|
|
12
|
-
misc3Hours?: number
|
|
13
|
-
misc4Hours?: number
|
|
14
|
-
stat1Hours?: number
|
|
15
|
-
stat2Hours?: number
|
|
16
|
-
// "EmployeeTimeCardEntry__c": null, // unknown entry in salesforce
|
|
17
|
-
inChargeHours?: number
|
|
18
|
-
// "Shift_Differential__c": number, // unknown entry in salesforce
|
|
19
|
-
shiftTravelPerDiemHours?: number
|
|
20
|
-
// "Weekend_Differential__c": number // unknown entry in salesforce
|
|
21
|
-
}
|
|
1
|
+
export interface TimesheetDayEntry {
|
|
2
|
+
date?: Date
|
|
3
|
+
callbackHours?: number
|
|
4
|
+
regularHours?: number
|
|
5
|
+
miscHours?: number
|
|
6
|
+
overtime1Hours?: number
|
|
7
|
+
overtime2Hours?: number
|
|
8
|
+
onCallHours?: number
|
|
9
|
+
misc1HoursA?: number
|
|
10
|
+
misc1HoursB?: number
|
|
11
|
+
misc2Hours?: number
|
|
12
|
+
misc3Hours?: number
|
|
13
|
+
misc4Hours?: number
|
|
14
|
+
stat1Hours?: number
|
|
15
|
+
stat2Hours?: number
|
|
16
|
+
// "EmployeeTimeCardEntry__c": null, // unknown entry in salesforce
|
|
17
|
+
inChargeHours?: number
|
|
18
|
+
// "Shift_Differential__c": number, // unknown entry in salesforce
|
|
19
|
+
shiftTravelPerDiemHours?: number
|
|
20
|
+
// "Weekend_Differential__c": number // unknown entry in salesforce
|
|
21
|
+
}
|