Service Library - payrollReporting

This document provides a complete reference of the custom code library for the payrollReporting service. It includes all library functions, edge functions with their REST endpoints, templates, and assets.

Library Functions

Library functions are reusable modules available to all business APIs and other custom code within the service via require("lib/<moduleName>").

calculateTotalHoursWorked.js

// Computes total worked hours for given user within [periodStart, periodEnd].
// Attendance records with status 'present' or 'late' are counted as working hours.
// Args: context (Business API ctx), userId, periodStart, periodEnd
const { fetchRemoteListByMQuery } = require('serviceCommon');
module.exports = async function calculateTotalHoursWorked(context, userId, periodStart, periodEnd) {
  if (!userId || !periodStart || !periodEnd) return 0;
  const records = await fetchRemoteListByMQuery(
    'attendanceRecord',
    {
      userId: userId,
      checkInTime: { $gte: periodStart },
      checkOutTime: { $lte: periodEnd },
      status: { $in: ['present', 'late', 'leftEarly'] },
      isActive: true
    },
    0,
    3000 // practical max per period
  );
  let total = 0;
  for (const rec of records) {
    if (rec.checkInTime && rec.checkOutTime) {
      const hours = (new Date(rec.checkOutTime) - new Date(rec.checkInTime)) / (60 * 60 * 1000);
      total += Math.max(hours, 0.0);
    }
  }
  return Number(total.toFixed(2));
};

calculateOvertimeHours.js

// Computes total overtime hours in attendance within [periodStart, periodEnd].
// Overtime: working over 8 hours/shift (could be customized)
const { fetchRemoteListByMQuery } = require('serviceCommon');
module.exports = async function calculateOvertimeHours(context, userId, periodStart, periodEnd) {
  if (!userId || !periodStart || !periodEnd) return 0;
  const records = await fetchRemoteListByMQuery(
    'attendanceRecord',
    {
      userId: userId,
      checkInTime: { $gte: periodStart },
      checkOutTime: { $lte: periodEnd },
      status: { $in: ['present', 'late', 'leftEarly'] },
      isActive: true
    },
    0,
    3000
  );
  let overtime = 0;
  for (const rec of records) {
    if (rec.checkInTime && rec.checkOutTime) {
      const hours = (new Date(rec.checkOutTime) - new Date(rec.checkInTime)) / (60 * 60 * 1000);
      if (hours > 8) overtime += (hours - 8);
    }
  }
  return Number(overtime.toFixed(2));
};

calculateAbsenceDays.js

// Absence = days with leaveRequest.status === 'approved' or
//           attendance status === 'absent' during period
const { fetchRemoteListByMQuery } = require('serviceCommon');
module.exports = async function calculateAbsenceDays(context, userId, periodStart, periodEnd) {
  if (!userId || !periodStart || !periodEnd) return 0;
  // Attendance absences
  const attendance = await fetchRemoteListByMQuery(
    'attendanceRecord',
    {
      userId: userId,
      checkInTime: { $gte: periodStart },
      checkOutTime: { $lte: periodEnd },
      status: 'absent',
      isActive: true
    }, 0, 3000
  );
  let absentDays = new Set();
  for (const rec of attendance) {
    if (rec.checkInTime) absentDays.add((new Date(rec.checkInTime)).toISOString().slice(0,10));
  }
  // Leave requests
  const leaveRequests = await fetchRemoteListByMQuery(
    'leaveRequest',
    {
      userId: userId,
      status: 'approved',
      startDate: { $lte: periodEnd },
      endDate: { $gte: periodStart },
      isActive: true
    }, 0, 200
  );
  for (const leave of leaveRequests) {
    let d1 = new Date(leave.startDate), d2 = new Date(leave.endDate);
    for (let d = d1; d <= d2; d.setDate(d.getDate() + 1)) {
      const day = new Date(d).toISOString().slice(0,10);
      if ( (day >= periodStart && day <= periodEnd) ) absentDays.add(day)
    }
  }
  return absentDays.size;
};

calculateSalary.js

// Salary calculated based on policy: hourlyRate from employeeProfile/position, multiply worked+overtime, add bonus, subtract deduction. No payments processed. Placeholder: baseRate = 15/hr, overtimeRate = 1.5x
const { fetchRemoteObjectByMQuery } = require('serviceCommon');
module.exports = async function calculateSalary(context, userId, periodStart, periodEnd, totalHoursWorked, overtimeHours, bonus, deduction) {
  if (!userId || !periodStart || !periodEnd) return 0;
  // Fetch employee profile to get salary/position if defined
  let baseRate = 15, overtimeRate = 1.5 * baseRate;
  const empProf = await fetchRemoteObjectByMQuery("employeeProfile", { userId });
  if (empProf && empProf.salary && empProf.salary > 0) baseRate = empProf.salary / 160; // Assume salary is monthly, 160h month
  let base = Number(totalHoursWorked || 0) * baseRate;
  let ot = Number(overtimeHours || 0) * overtimeRate;
  let gross = base + ot + Number(bonus||0) - Number(deduction||0);
  return Number(gross.toFixed(2));
};

This document was generated from the service library configuration and should be kept in sync with design changes.