refactor attendance calculations to handle multiple entry/exit times and improve time tracking accuracy
This commit is contained in:
@@ -54,15 +54,25 @@ export const useAttendanceStore = defineStore('attendanceStore', () => {
|
|||||||
|
|
||||||
for (let d = 1; d <= daysInMonth.value; d++) {
|
for (let d = 1; d <= daysInMonth.value; d++) {
|
||||||
const dateStr = `${year.value}-${month.value.toString().padStart(2, '0')}-${d.toString().padStart(2, '0')}`
|
const dateStr = `${year.value}-${month.value.toString().padStart(2, '0')}-${d.toString().padStart(2, '0')}`
|
||||||
const dayData = response.days.find((day) => day.check_in.startsWith(dateStr)) || {}
|
const dayEntries = response.days.filter((day) => day.check_in && day.check_in.startsWith(dateStr))
|
||||||
|
|
||||||
|
// Sort entries by check-in time to ensure correct order
|
||||||
|
dayEntries.sort((a, b) => new Date(a.check_in) - new Date(b.check_in))
|
||||||
|
|
||||||
|
const entryTimes = dayEntries.map((entry) => utils.extractTimeFromDateString(entry.check_in))
|
||||||
|
const exitTimes = dayEntries.map((entry) => utils.extractTimeFromDateString(entry.check_out))
|
||||||
|
const attendance_reason_ids = dayEntries.flatMap(
|
||||||
|
(entry) => entry.attendance_reason_ids || [],
|
||||||
|
)
|
||||||
|
|
||||||
const isPublicHoliday = response.public_holidays.some((h) => h.date === dateStr)
|
const isPublicHoliday = response.public_holidays.some((h) => h.date === dateStr)
|
||||||
const alert = dayData.attendance_reason_ids?.length > 0
|
const alert = attendance_reason_ids.length > 0
|
||||||
|
|
||||||
const day = {
|
const day = {
|
||||||
dayOfMonth: d,
|
dayOfMonth: d,
|
||||||
dayOfWeek: utils.getDayOfWeek(dateStr),
|
dayOfWeek: utils.getDayOfWeek(dateStr),
|
||||||
entryTime: utils.extractTimeFromDateString(dayData.check_in),
|
entryTime: entryTimes,
|
||||||
exitTime: utils.extractTimeFromDateString(dayData.check_out),
|
exitTime: exitTimes,
|
||||||
isSickLeave: false,
|
isSickLeave: false,
|
||||||
isHolidayLeave: false,
|
isHolidayLeave: false,
|
||||||
isPublicHoliday: isPublicHoliday,
|
isPublicHoliday: isPublicHoliday,
|
||||||
|
|||||||
@@ -95,21 +95,34 @@ function isValidWorkDay(day) {
|
|||||||
return day.dayOfWeek !== 'So' && day.dayOfWeek !== 'Nd' && !day.isPublicHoliday
|
return day.dayOfWeek !== 'So' && day.dayOfWeek !== 'Nd' && !day.isPublicHoliday
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateWorkedTime(entryTime, exitTime) {
|
function calculateWorkedTime(entryTimes, exitTimes) {
|
||||||
if (!entryTime || !exitTime || typeof entryTime !== 'string' || typeof exitTime !== 'string') {
|
if (!entryTimes || !exitTimes || !Array.isArray(entryTimes) || !Array.isArray(exitTimes) || entryTimes.length === 0) {
|
||||||
return 0
|
|
||||||
}
|
|
||||||
const [eh, em] = entryTime.split(':').map(Number)
|
|
||||||
const [xh, xm] = exitTime.split(':').map(Number)
|
|
||||||
|
|
||||||
if ([eh, em, xh, xm].some((v) => typeof v !== 'number' || isNaN(v))) {
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = eh * 60 + em
|
let totalWorkedMinutes = 0
|
||||||
const end = xh * 60 + xm
|
for (let i = 0; i < entryTimes.length; i++) {
|
||||||
|
const entryTime = entryTimes[i]
|
||||||
|
// Use the corresponding exit time, or skip if it doesn't exist
|
||||||
|
const exitTime = exitTimes[i]
|
||||||
|
|
||||||
let worked = (end - start) / 60
|
if (!entryTime || !exitTime || typeof entryTime !== 'string' || typeof exitTime !== 'string') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const [eh, em] = entryTime.split(':').map(Number)
|
||||||
|
const [xh, xm] = exitTime.split(':').map(Number)
|
||||||
|
|
||||||
|
if ([eh, em, xh, xm].some((v) => typeof v !== 'number' || isNaN(v))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = eh * 60 + em
|
||||||
|
const end = xh * 60 + xm
|
||||||
|
totalWorkedMinutes += end - start
|
||||||
|
}
|
||||||
|
|
||||||
|
const worked = totalWorkedMinutes / 60
|
||||||
return parseFloat(worked.toFixed(9))
|
return parseFloat(worked.toFixed(9))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,23 +143,30 @@ function floatHoursToHHMM(decimalHours) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function calculateDay(day) {
|
function calculateDay(day) {
|
||||||
let exitTime = day.exitTime
|
// Create a mutable copy of exit times to potentially add the current time.
|
||||||
|
const exitTimes = [...day.exitTime]
|
||||||
|
|
||||||
if (!day.exitTime || typeof day.exitTime !== 'string' || day.exitTime.trim() === '') {
|
// If there's one more entry than exits, it means the user is currently clocked in.
|
||||||
// const now = new Date().toLocaleString('pl-PL', { timeZone: 'Europe/Warsaw' })
|
if (day.entryTime.length > 0 && day.entryTime.length === day.exitTime.length + 1) {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const hh = String(now.getHours()).padStart(2, '0')
|
const hh = String(now.getHours()).padStart(2, '0')
|
||||||
const mm = String(now.getMinutes()).padStart(2, '0')
|
const mm = String(now.getMinutes()).padStart(2, '0')
|
||||||
const ss = String(now.getSeconds()).padStart(2, '0')
|
const ss = String(now.getSeconds()).padStart(2, '0')
|
||||||
exitTime = `${hh}:${mm}:${ss}`
|
// Add the current time as the "exit" for the last entry.
|
||||||
|
exitTimes.push(`${hh}:${mm}:${ss}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!day.entryTime || typeof day.entryTime !== 'string' || day.entryTime.trim() === '') {
|
if (day.entryTime.length === 0) {
|
||||||
day.workedHours = 0
|
day.workedHours = 0
|
||||||
} else {
|
} else {
|
||||||
day.workedHours = calculateWorkedTime(day.entryTime, exitTime) - 0.25
|
// The -0.25 for a 15-minute break seems to be a business rule.
|
||||||
day.workedHours = parseFloat(day.workedHours.toFixed(9))
|
// It should probably only be subtracted once per day, not per interval.
|
||||||
day.exitTime = exitTime
|
// Let's subtract it from the total.
|
||||||
|
let totalHours = calculateWorkedTime(day.entryTime, exitTimes)
|
||||||
|
if (totalHours > 0) {
|
||||||
|
totalHours -= 0.25
|
||||||
|
}
|
||||||
|
day.workedHours = parseFloat(totalHours.toFixed(9))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValidWorkDay(day) && !day.isHolidayLeave && !day.isSickLeave) {
|
if (isValidWorkDay(day) && !day.isHolidayLeave && !day.isSickLeave) {
|
||||||
|
|||||||
Reference in New Issue
Block a user