api's created for zoho people

This commit is contained in:
yashwin-foxy 2025-09-22 19:23:19 +05:30
parent 713e593b66
commit af72fa4c98
4 changed files with 302 additions and 16 deletions

View File

@ -1,5 +1,6 @@
const { success, failure } = require('../../utils/response'); const { success, failure } = require('../../utils/response');
const IntegrationService = require('../../services/integration/integrationService'); const IntegrationService = require('../../services/integration/integrationService');
const ZohoClient = require('../../integrations/zoho/client');
async function getData(req, res) { async function getData(req, res) {
try { try {
@ -903,6 +904,54 @@ async function getPerformanceData(req, res) {
} }
} }
// Get attendance report for users
const getUserReport = async (req, res) => {
try {
const { sdate, edate, dateFormat, startIndex } = req.query;
const userId = req.user.id;
const zohoClient = new ZohoClient(userId);
const attendanceReport = await zohoClient.getUserReport({
sdate,
edate,
dateFormat,
startIndex
});
res.json(success('Attendance report retrieved successfully', attendanceReport));
} catch (error) {
res.status(400).json(failure(error.message, 'INTEGRATION_ERROR'));
}
}
// Get leave tracker report
const getLeaveTrackerReport = async (req, res) => {
try {
const userId = req.user.id;
const zohoClient = new ZohoClient(userId);
const leaveTrackerReport = await zohoClient.getLeaveTrackerReport();
res.json(success('Leave tracker report retrieved successfully', leaveTrackerReport));
} catch (error) {
res.status(400).json(failure(error.message, 'INTEGRATION_ERROR'));
}
}
// Get holidays
const getHolidays = async (req, res) => {
try {
const userId = req.user.id;
const zohoClient = new ZohoClient(userId);
const holidays = await zohoClient.getHolidays();
res.json(success('Holidays retrieved successfully', holidays));
} catch (error) {
res.status(400).json(failure(error.message, 'INTEGRATION_ERROR'));
}
}
module.exports = { module.exports = {
getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks, getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks,
getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases, getSalesOrders, getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases, getSalesOrders,
@ -910,5 +959,6 @@ module.exports = {
getOrganizations, getCustomers, getVendors, getItems, getEstimates, getBills, getOrganizations, getCustomers, getVendors, getItems, getEstimates, getBills,
getExpenses, getBankAccounts, getBankTransactions, getReports, getBooksSalesOrders, getExpenses, getBankAccounts, getBankTransactions, getReports, getBooksSalesOrders,
getBooksPurchaseOrders, getContacts, getBooksContacts, getBooksInvoices, getEmployeeForms, getEmployeeById, getBooksPurchaseOrders, getContacts, getBooksContacts, getBooksInvoices, getEmployeeForms, getEmployeeById,
getAttendanceEntries, getShiftConfiguration, getLeaveData, getGoalsData, getPerformanceData getAttendanceEntries, getShiftConfiguration, getLeaveData, getGoalsData, getPerformanceData,
getUserReport, getLeaveTrackerReport, getHolidays
}; };

View File

@ -7,7 +7,8 @@ const {
getOrganizations, getCustomers, getVendors, getItems, getEstimates, getBills, getOrganizations, getCustomers, getVendors, getItems, getEstimates, getBills,
getExpenses, getBankAccounts, getBankTransactions, getReports, getBooksSalesOrders, getExpenses, getBankAccounts, getBankTransactions, getReports, getBooksSalesOrders,
getBooksPurchaseOrders, getContacts, getBooksContacts, getBooksInvoices, getEmployeeForms, getEmployeeById, getBooksPurchaseOrders, getContacts, getBooksContacts, getBooksInvoices, getEmployeeForms, getEmployeeById,
getAttendanceEntries, getShiftConfiguration, getLeaveData, getGoalsData, getPerformanceData getAttendanceEntries, getShiftConfiguration, getLeaveData, getGoalsData, getPerformanceData,
getUserReport, getLeaveTrackerReport, getHolidays
} = require('../controllers/integrationController'); } = require('../controllers/integrationController');
const auth = require('../middlewares/auth'); const auth = require('../middlewares/auth');
const ZohoHandler = require('../../integrations/zoho/handler'); const ZohoHandler = require('../../integrations/zoho/handler');
@ -245,6 +246,11 @@ router.get('/zoho/people/leave-data', auth, validate(leaveDataSchema), getLeaveD
router.get('/zoho/people/goals-data', auth, validate(goalsDataSchema), getGoalsData); router.get('/zoho/people/goals-data', auth, validate(goalsDataSchema), getGoalsData);
router.get('/zoho/people/performance-data', auth, validate(performanceDataSchema), getPerformanceData); router.get('/zoho/people/performance-data', auth, validate(performanceDataSchema), getPerformanceData);
// Zoho People Reports API routes
router.get('/zoho/people/attendance-report', auth, getUserReport);
router.get('/zoho/people/leave-tracker-report', auth, getLeaveTrackerReport);
router.get('/zoho/people/holidays', auth, getHolidays);
// Zoho Books specific routes // Zoho Books specific routes
const organizationsSchema = Joi.object({ const organizationsSchema = Joi.object({
provider: Joi.string().valid('zoho').required(), provider: Joi.string().valid('zoho').required(),

View File

@ -54,6 +54,7 @@ class ZohoClient {
return response.data; return response.data;
} catch (error) { } catch (error) {
// console.log('error in makeRequest',JSON.stringify(error))
if (error.response?.status === 401) { if (error.response?.status === 401) {
await this.refreshToken(); await this.refreshToken();
const newTokens = await this.getTokens(); const newTokens = await this.getTokens();
@ -190,9 +191,71 @@ class ZohoClient {
async getPerformanceData(formLinkName = 'performance', params = {}) { async getPerformanceData(formLinkName = 'performance', params = {}) {
const response = await this.makeRequest(`/people/api/forms/${formLinkName}/getRecords`, { params }, 'people'); const response = await this.makeRequest(`/people/api/forms/${formLinkName}/getRecords`, { params }, 'people');
console.log('performance response i got',response)
return ZohoMapper.mapApiResponse(response, 'performance_data'); return ZohoMapper.mapApiResponse(response, 'performance_data');
} }
async getUserReport(params = {}) {
// Set default to last 7 days if no dates provided
if (!params.sdate || !params.edate) {
const today = new Date();
const sevenDaysAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
params.sdate = sevenDaysAgo.toISOString().split('T')[0];
params.edate = today.toISOString().split('T')[0];
}
// Set default date format if not provided
if (!params.dateFormat) {
params.dateFormat = 'yyyy-MM-dd';
}
// Set default start index if not provided
if (!params.startIndex) {
params.startIndex = 1;
}
const response = await this.makeRequest('/people/api/attendance/getUserReport', { params }, 'people');
return ZohoMapper.mapApiResponse(response, 'attendance_report');
}
async getLeaveTrackerReport(params = {}) {
// Set default parameters if not provided
if (!params.from) {
// Current leave year from - start of current year
const currentYear = new Date().getFullYear();
const startDate = new Date(currentYear, 0, 1); // January 1st
params.from = this.formatDate(startDate);
}
if (!params.to) {
// Current date
params.to = this.formatDate(new Date());
}
if (!params.unit) {
// Default unit is Day
params.unit = 'Day';
}
const response = await this.makeRequest('/people/api/v2/leavetracker/reports/bookedAndBalance', { params }, 'people');
return ZohoMapper.mapApiResponse(response, 'leave_tracker_report');
}
// Helper method to format date as DD-MMM-YYYY
formatDate(date) {
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const day = date.getDate().toString().padStart(2, '0');
const month = months[date.getMonth()];
const year = date.getFullYear();
return `${day}-${month}-${year}`;
}
async getHolidays(params = {}) {
const response = await this.makeRequest('/people/api/leave/v2/holidays/get', { params }, 'people');
return ZohoMapper.mapApiResponse(response, 'holidays');
}
// Zoho Books methods // Zoho Books methods
async getOrganizations(params = {}) { async getOrganizations(params = {}) {
const response = await this.makeRequest('/books/v3/organizations', { params }, 'books'); const response = await this.makeRequest('/books/v3/organizations', { params }, 'books');
@ -347,7 +410,7 @@ class ZohoClient {
getAvailableResources(service) { getAvailableResources(service) {
const resources = { const resources = {
'crm': ['leads', 'contacts', 'deals', 'tasks', 'sales_orders', 'purchase_orders', 'invoices'], 'crm': ['leads', 'contacts', 'deals', 'tasks', 'sales_orders', 'purchase_orders', 'invoices'],
'people': ['employees', 'departments', 'timesheets', 'leave_requests', 'attendance', 'employee_forms', 'attendance_entries', 'shift_configuration', 'leave_data', 'goals_data', 'performance_data'], 'people': ['employees', 'departments', 'timesheets', 'leave_requests', 'attendance', 'employee_forms', 'attendance_entries', 'shift_configuration', 'leave_data', 'goals_data', 'performance_data', 'attendance_report', 'leave_tracker_report', 'holidays'],
'books': ['organizations', 'customers', 'vendors', 'items', 'invoices', 'estimates', 'bills', 'expenses', 'bank_accounts', 'bank_transactions', 'reports', 'sales_orders', 'purchase_orders'], 'books': ['organizations', 'customers', 'vendors', 'items', 'invoices', 'estimates', 'bills', 'expenses', 'bank_accounts', 'bank_transactions', 'reports', 'sales_orders', 'purchase_orders'],
'projects': ['projects', 'tasks', 'timesheets'] 'projects': ['projects', 'tasks', 'timesheets']
}; };

View File

@ -211,10 +211,37 @@ class ZohoMapper {
employeeId: zohoAttendanceEntry.empId, employeeId: zohoAttendanceEntry.empId,
emailId: zohoAttendanceEntry.emailId, emailId: zohoAttendanceEntry.emailId,
date: zohoAttendanceEntry.date, date: zohoAttendanceEntry.date,
checkIn: zohoAttendanceEntry.checkIn, // First In details
checkOut: zohoAttendanceEntry.checkOut, firstIn: zohoAttendanceEntry.firstIn,
hoursWorked: zohoAttendanceEntry.hoursWorked, firstIn_Location: zohoAttendanceEntry.firstIn_Location,
firstIn_Building: zohoAttendanceEntry.firstIn_Building,
firstIn_latitude: zohoAttendanceEntry.firstIn_latitude,
firstIn_longitude: zohoAttendanceEntry.firstIn_longitude,
// Last Out details
lastOut: zohoAttendanceEntry.lastOut,
lastOut_Location: zohoAttendanceEntry.lastOut_Location,
lastOut_Building: zohoAttendanceEntry.lastOut_Building,
lastOut_latitude: zohoAttendanceEntry.lastOut_latitude,
lastOut_longitude: zohoAttendanceEntry.lastOut_longitude,
// Summary
totalHrs: zohoAttendanceEntry.totalHrs,
allowedToCheckIn: zohoAttendanceEntry.allowedToCheckIn,
status: zohoAttendanceEntry.status, status: zohoAttendanceEntry.status,
// Detailed entries array
entries: zohoAttendanceEntry.entries ? zohoAttendanceEntry.entries.map(entry => ({
checkIn: entry.checkIn,
checkOut: entry.checkOut,
checkIn_Location: entry.checkIn_Location,
checkOut_Location: entry.checkOut_Location,
checkIn_Building: entry.checkIn_Building,
checkOut_Building: entry.checkOut_Building,
checkIn_Latitude: entry.checkIn_Latitude,
checkIn_Longitude: entry.checkIn_Longitude,
checkOut_Latitude: entry.checkOut_Latitude,
checkOut_Longitude: entry.checkOut_Longitude,
sourceOfPunchIn: entry.sourceOfPunchIn,
sourceOfPunchOut: entry.sourceOfPunchOut
})) : [],
customFields: this.mapCustomFields(zohoAttendanceEntry) customFields: this.mapCustomFields(zohoAttendanceEntry)
}; };
} }
@ -236,14 +263,20 @@ class ZohoMapper {
// Map Zoho People Leave Data to standardized format // Map Zoho People Leave Data to standardized format
static mapLeaveData(zohoLeaveData) { static mapLeaveData(zohoLeaveData) {
return { return {
id: zohoLeaveData.id, id: zohoLeaveData.Id || zohoLeaveData.id,
employeeId: zohoLeaveData.employeeId, employeeId: zohoLeaveData.empId || zohoLeaveData.employeeId,
leaveType: zohoLeaveData.leaveType, employeeName: zohoLeaveData.employeeName || zohoLeaveData.employee_name,
startDate: zohoLeaveData.startDate, leaveType: zohoLeaveData.leaveType || zohoLeaveData.leave_type,
endDate: zohoLeaveData.endDate, leaveTypeName: zohoLeaveData.leaveTypeName || zohoLeaveData.leave_type_name,
days: zohoLeaveData.days, startDate: zohoLeaveData.startDate || zohoLeaveData.start_date,
reason: zohoLeaveData.reason, endDate: zohoLeaveData.endDate || zohoLeaveData.end_date,
status: zohoLeaveData.status, days: zohoLeaveData.days || zohoLeaveData.total_days,
reason: zohoLeaveData.reason || zohoLeaveData.leave_reason,
status: zohoLeaveData.status || zohoLeaveData.leave_status,
appliedDate: zohoLeaveData.appliedDate || zohoLeaveData.applied_date,
approvedBy: zohoLeaveData.approvedBy || zohoLeaveData.approved_by,
approvedDate: zohoLeaveData.approvedDate || zohoLeaveData.approved_date,
comments: zohoLeaveData.comments || zohoLeaveData.leave_comments,
customFields: this.mapCustomFields(zohoLeaveData) customFields: this.mapCustomFields(zohoLeaveData)
}; };
} }
@ -279,6 +312,83 @@ class ZohoMapper {
}; };
} }
// Map Zoho People Attendance Report to standardized format
static mapAttendanceReport(zohoAttendanceReport) {
return {
employeeId: zohoAttendanceReport.employeeDetails?.id,
employeeName: `${zohoAttendanceReport.employeeDetails?.['first name']} ${zohoAttendanceReport.employeeDetails?.['last name']}`,
email: zohoAttendanceReport.employeeDetails?.['mail id'],
employeeNumber: zohoAttendanceReport.employeeDetails?.erecno,
attendanceDetails: zohoAttendanceReport.attendanceDetails ?
Object.entries(zohoAttendanceReport.attendanceDetails).map(([date, details]) => ({
date,
shiftStartTime: details.ShiftStartTime,
shiftEndTime: details.ShiftEndTime,
shiftName: details.ShiftName,
status: details.Status,
firstIn: details.FirstIn,
lastOut: details.LastOut,
firstInLocation: details.FirstIn_Location,
lastOutLocation: details.LastOut_Location,
totalHours: details.TotalHours,
workingHours: details.WorkingHours,
paidBreakHours: details.paidBreakHours,
unPaidBreakHours: details.unPaidBreakHours,
deviationTime: details.DeviationTime
})) : []
};
}
// Map Zoho People Leave Tracker Report to standardized format
static mapLeaveTrackerReport(zohoLeaveTrackerReport) {
return {
leaveTypes: zohoLeaveTrackerReport.leavetypes ?
Object.entries(zohoLeaveTrackerReport.leavetypes).map(([id, type]) => ({
id,
name: type.name,
code: type.code,
unit: type.unit,
type: type.type
})) : [],
employees: zohoLeaveTrackerReport.employees || [],
reports: zohoLeaveTrackerReport.report ?
Object.entries(zohoLeaveTrackerReport.report).map(([employeeId, report]) => ({
employeeId,
employeeName: report.employee?.name,
leaveBalances: Object.entries(report).filter(([key]) => key !== 'totals' && key !== 'employee').map(([leaveTypeId, balance]) => ({
leaveTypeId,
booked: balance.booked || 0,
balance: balance.balance || 0
})),
totals: {
paidBooked: report.totals?.paidBooked || 0,
paidBalance: report.totals?.paidBalance || 0,
unpaidBooked: report.totals?.unpaidBooked || 0,
unpaidBalance: report.totals?.unpaidBalance || 0,
ondutyBooked: report.totals?.ondutyBooked || 0,
ondutyBalance: report.totals?.ondutyBalance || 0
}
})) : []
};
}
// Map Zoho People Holidays to standardized format
static mapHoliday(zohoHoliday) {
return {
id: zohoHoliday.Id,
name: zohoHoliday.Name,
date: zohoHoliday.Date,
remarks: zohoHoliday.Remarks,
locationId: zohoHoliday.LocationId,
locationName: zohoHoliday.LocationName,
shiftId: zohoHoliday.ShiftId,
shiftName: zohoHoliday.ShiftName,
isHalfday: zohoHoliday.isHalfday,
isRestrictedHoliday: zohoHoliday.isRestrictedHoliday,
session: zohoHoliday.Session
};
}
// Map Zoho Books Organization to standardized format // Map Zoho Books Organization to standardized format
static mapOrganization(zohoOrganization) { static mapOrganization(zohoOrganization) {
return { return {
@ -541,6 +651,9 @@ class ZohoMapper {
'leave_data': this.mapLeaveData, 'leave_data': this.mapLeaveData,
'goals_data': this.mapGoalsData, 'goals_data': this.mapGoalsData,
'performance_data': this.mapPerformanceData, 'performance_data': this.mapPerformanceData,
'attendance_report': this.mapAttendanceReport,
'leave_tracker_report': this.mapLeaveTrackerReport,
'holidays': this.mapHoliday,
// Books // Books
'organizations': this.mapOrganization, 'organizations': this.mapOrganization,
'customers': this.mapCustomer, 'customers': this.mapCustomer,
@ -781,9 +894,7 @@ class ZohoMapper {
break; break;
case 'employee_forms': case 'employee_forms':
case 'attendance_entries':
case 'shift_configuration': case 'shift_configuration':
case 'leave_data':
case 'goals_data': case 'goals_data':
case 'performance_data': case 'performance_data':
// People Forms API response structure // People Forms API response structure
@ -795,6 +906,62 @@ class ZohoMapper {
}; };
break; break;
case 'leave_data':
// Leave data specific response structure
records = zohoResponse.response?.result || [];
pageInfo = {
count: records.length,
moreRecords: false,
page: 1,
message: zohoResponse.response?.message,
status: zohoResponse.response?.status
};
break;
case 'attendance_entries':
// Attendance entries specific response structure
records = zohoResponse.entries || [];
pageInfo = {
count: zohoResponse.info?.count || records.length,
moreRecords: zohoResponse.info?.more_records || false,
page: zohoResponse.info?.page || 1
};
break;
case 'attendance_report':
// Attendance report response structure
records = zohoResponse.result || [];
pageInfo = {
count: records.length,
moreRecords: false,
page: 1,
message: zohoResponse.message,
status: zohoResponse.status
};
break;
case 'leave_tracker_report':
// Leave tracker report response structure
records = [zohoResponse]; // Wrap in array for consistency
pageInfo = {
count: 1,
moreRecords: false,
page: 1
};
break;
case 'holidays':
// Holidays response structure
records = zohoResponse.data || [];
pageInfo = {
count: records.length,
moreRecords: false,
page: 1,
message: zohoResponse.message,
status: zohoResponse.status
};
break;
case 'employee_detail': case 'employee_detail':
// Single employee detail response // Single employee detail response
records = zohoResponse.data ? [zohoResponse.data] : []; records = zohoResponse.data ? [zohoResponse.data] : [];