hk-planner/backend/src/index.js
jtricerolph 9bfd14a082 Add timesheet sync: pull actual hours, daily cron, per-cell source indicators
- fetchTimesheetShifts: hits /api/v2/shifts, subtracts breaks, carries PENDING/APPROVED status
- syncJobs.js: shared runRotaSync/runTimesheetSync logic used by routes and cron
- Rota sync now skips dates already marked source='timesheet'
- POST /api/workforce/sync-timesheets: on-demand pull of last 7 days actual hours
- Daily 6am cron: rota sync then timesheet sync, each logged independently
- upsertWorkforceShifts now writes source per row (workforce or timesheet)
- Frontend: Pull timesheets button alongside Sync rota button
- Clock icon on timesheet cells — amber for PENDING, green for APPROVED
- Both buttons disabled while either sync is in progress

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 18:25:02 +00:00

46 lines
1.5 KiB
JavaScript

import Fastify from 'fastify'
import cookie from '@fastify/cookie'
import cors from '@fastify/cors'
import cron from 'node-cron'
import { initDb } from './db.js'
import { bookingRoutes } from './routes/bookings.js'
import { configRoutes } from './routes/config.js'
import { workforceRoutes } from './routes/workforce.js'
import { runRotaSync, runTimesheetSync } from './lib/syncJobs.js'
const app = Fastify({ logger: true, trustProxy: true })
const startedAt = Date.now()
await app.register(cookie)
await app.register(cors, { origin: process.env.CORS_ORIGIN || false, credentials: true })
app.get('/health', async () => ({ status: 'healthy', version: process.env.BUILD_VERSION || String(startedAt) }))
await app.register(bookingRoutes)
await app.register(configRoutes)
await app.register(workforceRoutes)
try {
await initDb()
await app.listen({ port: 3001, host: '0.0.0.0' })
} catch (err) {
app.log.error(err)
process.exit(1)
}
// Daily 6am: rota sync (today-7 → today+28) then timesheet sync (last 7 days)
cron.schedule('0 6 * * *', async () => {
app.log.info('cron: starting daily workforce sync')
try {
const r = await runRotaSync()
app.log.info({ dates_synced: r.dates_synced }, 'cron: rota sync complete')
} catch (err) {
app.log.error({ err: err.message }, 'cron: rota sync failed')
}
try {
const r = await runTimesheetSync()
app.log.info({ dates_synced: r.dates_synced }, 'cron: timesheet sync complete')
} catch (err) {
app.log.error({ err: err.message }, 'cron: timesheet sync failed')
}
})