Parallelize per-day timesheet fetch; extend compare route timeout to 120s
Sequential day-by-day caused 504 on month-long ranges. Switching to Promise.all over all dates so 30 concurrent calls complete in ~3-5s. Route timeout bumped to 120s to give the parallel calls room to land. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4cba6a2ffa
commit
265df96a63
2 changed files with 33 additions and 26 deletions
|
|
@ -287,40 +287,42 @@ async function fetchAndSummarise(path) {
|
|||
}
|
||||
}
|
||||
|
||||
// Fetch per-date timesheets for every day in [from, to] and aggregate.
|
||||
// Slow (one request per day) but works when the range endpoint returns 404.
|
||||
// Fetch per-date timesheets for every day in [from, to] in parallel and aggregate.
|
||||
// Uses /api/v2/timesheets/on/{date} when the range endpoint returns 404.
|
||||
async function fetchTimesheetsByDay(from, to, locationId) {
|
||||
const start = new Date(from + 'T00:00:00')
|
||||
const end = new Date(to + 'T00:00:00')
|
||||
let baseCost = 0, totalCost = 0, count = 0
|
||||
const byDept = {}
|
||||
const sampleFields = null
|
||||
|
||||
// Build list of all dates in range
|
||||
const dates = []
|
||||
let cur = new Date(start)
|
||||
while (cur <= end) {
|
||||
const dateStr = cur.toISOString().slice(0, 10)
|
||||
dates.push(cur.toISOString().slice(0, 10))
|
||||
cur.setDate(cur.getDate() + 1)
|
||||
}
|
||||
|
||||
// Fire all days in parallel — ~30 concurrent requests, completes in ~3-5s
|
||||
const perDay = await Promise.all(dates.map(async dateStr => {
|
||||
let path = `/api/v2/timesheets/on/${dateStr}?show_costs=true&include_oncosts=true`
|
||||
if (locationId) path += `&location_id=${locationId}`
|
||||
try {
|
||||
const items = await wfFetchPaged(path)
|
||||
if (Array.isArray(items)) {
|
||||
try { return await wfFetchPaged(path) } catch { return [] }
|
||||
}))
|
||||
|
||||
let baseCost = 0, totalCost = 0, count = 0
|
||||
const byDept = {}
|
||||
for (const items of perDay) {
|
||||
if (!Array.isArray(items)) continue
|
||||
for (const s of items) {
|
||||
const base = parseFloat(s.cost ?? 0)
|
||||
const total = parseFloat(s.cost_with_oncosts ?? s.cost ?? 0)
|
||||
baseCost += base
|
||||
totalCost += total
|
||||
count++
|
||||
baseCost += base; totalCost += total; count++
|
||||
const deptId = String(s.department_id ?? 'unknown')
|
||||
const deptName = s.department_name || deptId
|
||||
if (!byDept[deptId]) byDept[deptId] = { name: deptName, baseCost: 0, totalCost: 0, count: 0 }
|
||||
if (!byDept[deptId]) byDept[deptId] = { name: s.department_name || deptId, baseCost: 0, totalCost: 0, count: 0 }
|
||||
byDept[deptId].baseCost += base
|
||||
byDept[deptId].totalCost += total
|
||||
byDept[deptId].count++
|
||||
}
|
||||
}
|
||||
} catch { /* ignore per-day errors */ }
|
||||
cur.setDate(cur.getDate() + 1)
|
||||
}
|
||||
const topDepts = Object.entries(byDept)
|
||||
.map(([id, v]) => ({ id, name: v.name, baseCost: +v.baseCost.toFixed(2), totalCost: +v.totalCost.toFixed(2), count: v.count }))
|
||||
.sort((a, b) => b.totalCost - a.totalCost)
|
||||
|
|
|
|||
|
|
@ -69,7 +69,12 @@ export async function syncRoutes(fastify) {
|
|||
// Compare cost totals from all available Workforce endpoints for a date range.
|
||||
// Useful for diagnosing figure discrepancies between shifts vs timesheets.
|
||||
// Usage: GET /wages/api/sync/compare?from=2025-06-01&to=2025-06-30
|
||||
fastify.get('/api/sync/compare', { preHandler: requireCap('sync') }, async (req, reply) => {
|
||||
fastify.get('/api/sync/compare', {
|
||||
preHandler: requireCap('sync'),
|
||||
config: { rawBody: false },
|
||||
// 120s — allows 30 parallel per-day timesheet calls to complete
|
||||
onRequest: async (req) => { req.socket.setTimeout(120_000) },
|
||||
}, async (req, reply) => {
|
||||
const { from, to } = req.query
|
||||
if (!from || !to) return reply.status(400).send({ error: 'from and to are required (YYYY-MM-DD)' })
|
||||
try {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue