diff --git a/functions/src/dooraccess/essl.ts b/functions/src/dooraccess/essl.ts
index c70f687..8be6a88 100644
--- a/functions/src/dooraccess/essl.ts
+++ b/functions/src/dooraccess/essl.ts
@@ -13,6 +13,10 @@ export interface EmployeeCodeRequest {
employeeCode: string;
}
+export interface PushLogRequest extends EmployeeCodeRequest {
+ attendanceDate: string;
+}
+
export interface UpdateEmployeeExRequest {
employeeCode: string;
employeeName: string;
@@ -190,6 +194,66 @@ function parseDeleteEmployeeResponse(soapResponse: string): string | null {
return resultText;
}
+function createGetEmployeePunchLogsRequest(username: string, password: string,
+ employeeCode: string, attendanceDate: string): string | null {
+ const soapRequest = `
+
+
+
+ cosqclient
+ 3bbb58d5
+ 1
+ 2025-05-24
+
+
+`;
+ return soapRequest;
+}
+
+function createDateFromTime(date: Date, timeString: string): Date {
+ const [hour, minute, second] = timeString.split(':').map(str => parseInt(str, 10));
+ const newDate = new Date(date.getTime());
+ newDate.setHours(hour);
+ newDate.setMinutes(minute);
+ newDate.setSeconds(second);
+ return newDate;
+}
+
+function parseGetEmployeePunchLogsResponse(soapResponse: string, attendanceDate: string): Date[] {
+ const rootDate = new Date(attendanceDate);
+ const parser = new DOMParser();
+ const xmlDoc = parser.parseFromString(soapResponse, "text/xml");
+ const currentElement = xmlDoc.documentElement.firstChild as HTMLElement;
+ const resultText = currentElement.textContent;
+ const punchLogs: Date[] = [];
+ const parts = resultText!.split(';');
+ for (const part of parts) {
+ try {
+ const logDateTime = new Date(part);
+ if (isNaN(logDateTime.getTime())) {
+ throw new Error('Invalid date format');
+ }
+ punchLogs.push(logDateTime);
+ } catch {
+ try {
+ const timeParts = part.split(',');
+ for (const timePart of timeParts) {
+ try {
+ const logDateTime = createDateFromTime(rootDate, timePart);
+ punchLogs.push(logDateTime);
+ } catch {
+ continue;
+ }
+ }
+ } catch {
+ continue;
+ }
+ }
+ }
+ const sortedLogs = punchLogs.sort((a, b) => b.getTime() - a.getTime());
+ return sortedLogs;
+}
+
async function sendSoapRequest(soapRequest: string, endpoint: string) {
try {
const headers: any = {
@@ -215,14 +279,14 @@ async function sendSoapRequest(soapRequest: string, endpoint: string) {
async function getUserDetails(username: string,
password: string,
- employeeCode: string, endpoint: string) {
+ employeeCode: string, endpoint: string): Promise {
const soapRequest = createGetEmployeeDetailsRequest(username, password, employeeCode);
const soapResponse = await sendSoapRequest(soapRequest!, endpoint);
const parsedResponse = parseGetEmployeeDetailsResponse(soapResponse);
return parsedResponse;
}
-async function updateEmployeeEx(username: string,
+async function updateUserEx(username: string,
password: string,
request: UpdateEmployeeExRequest,
endpoint: string) {
@@ -241,6 +305,16 @@ async function deleteEmplyee(username: string,
return parsedResponse;
}
+async function getEmployeePunchLogs(username: string,
+ password: string,
+ employeeCode: string,
+ attendanceDate: string, endpoint: string): Promise {
+ const soapRequest = createGetEmployeePunchLogsRequest(username, password, employeeCode, attendanceDate);
+ const soapResponse = await sendSoapRequest(soapRequest!, endpoint);
+ const parsedResponse = parseGetEmployeePunchLogsResponse(soapResponse, attendanceDate);
+ return parsedResponse;
+}
+
export const esslGetUserDetails = onRequest({
region: '#{SERVICES_RGN}#'
}, async (request: Request, response: Response) => {
@@ -339,7 +413,7 @@ export const esslUpdateUser = onRequest({
if (!updateEmployeeExRequest) {
throw new Error('Missing request params');
}
- const result = await updateEmployeeEx(username, password, updateEmployeeExRequest, endpoint);
+ const result = await updateUserEx(username, password, updateEmployeeExRequest, endpoint);
response.send(result);
} catch (error: any) {
logger.error(error);
@@ -398,3 +472,66 @@ export const esslDeleteUser = onRequest({
response.send(result);
});
});
+
+export const esslGetEmployeePunchLogs = onRequest({
+ region: '#{SERVICES_RGN}#'
+}, async (request: Request, response: Response) => {
+ return corsHandler(request, response, async () => {
+ let username: string | null = request.body.username as string;
+ let password: string | null = request.body.password as string;
+ let endpoint: string | null = request.body.endpoint as string;
+ let gymId: string | null = request.body.gymId as string;
+
+ const pushLogRequst = request.body.params as PushLogRequest;
+
+ if (!username) {
+ throw new Error('Missing username or password');
+ }
+ username = username.trim();
+ if (!password) {
+ if (!gymId) {
+ throw new Error('Missing password or gymId');
+ }
+ // todo: Get password from gym configuration by decrypting with private key
+ }
+ password = password.trim();
+ if (!pushLogRequst) {
+ throw new Error('Missing request params');
+ }
+ const employeeCode = pushLogRequst.employeeCode;
+ if (!employeeCode) {
+ throw new Error('Missing employeeCode');
+ }
+ const attendanceDate = pushLogRequst.attendanceDate;
+ if (!attendanceDate) {
+ throw new Error('Missing attendanceDate');
+ }
+ const isValidDate = /^\d{4}-\d{2}-\d{2}$/;
+ if (!attendanceDate.match(isValidDate)) {
+ throw new Error('attendanceDate is not in the valid format YYYY-MM-DD');
+ }
+ if (!endpoint) {
+ throw new Error('Missing endpoint');
+ }
+ if (!endpoint || endpoint.trim() === '') {
+ throw new Error('Missing endpoint');
+ }
+ try {
+ new URL(endpoint);
+ } catch (_) {
+ throw new Error('Endpoint is not a valid URI or URL');
+ }
+ if (!endpoint.endsWith('/webservice.asmx')) {
+ if (endpoint.endsWith('/')) {
+ endpoint = endpoint.substring(0, endpoint.length - 1);
+ }
+ endpoint += '/webservice.asmx';
+ }
+ const result = await getEmployeePunchLogs(username,
+ password,
+ employeeCode,
+ attendanceDate,
+ endpoint);
+ response.send(result);
+ });
+});
diff --git a/functions/src/dooraccess/index.ts b/functions/src/dooraccess/index.ts
index 587a1da..ad7da6e 100644
--- a/functions/src/dooraccess/index.ts
+++ b/functions/src/dooraccess/index.ts
@@ -1 +1,4 @@
-export { esslGetUserDetails, esslUpdateUser, esslDeleteUser } from './essl';
\ No newline at end of file
+export {
+ esslGetUserDetails, esslUpdateUser,
+ esslDeleteUser, esslGetEmployeePunchLogs
+} from './essl';
\ No newline at end of file
diff --git a/functions/src/index.ts b/functions/src/index.ts
index 515cc3b..1156209 100644
--- a/functions/src/index.ts
+++ b/functions/src/index.ts
@@ -17,4 +17,7 @@ export { processNotificationOnCreate } from './notifications';
export * from './payments';
export { getPlaceDetails, getPlacesAutocomplete } from './places';
export { registerClient } from './users';
-export { esslGetUserDetails, esslUpdateUser, esslDeleteUser } from './dooraccess';
+export {
+ esslGetUserDetails, esslUpdateUser,
+ esslDeleteUser, esslGetEmployeePunchLogs
+} from './dooraccess';