Initial scaffold: wages app
Full wage cost reporting app — weekly/monthly views, rolling 12-week/12-month history, budget management, Workforce API sync with SSE backfill, net sales via forecasting public API, department filter, CSV export. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
2e0592eb90
37 changed files with 3078 additions and 0 deletions
45
backend/src/routes/net-sales.js
Normal file
45
backend/src/routes/net-sales.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { requireAuth, requireCap } from '../auth.js'
|
||||
import { getConfig } from '../db.js'
|
||||
|
||||
async function fcFetch(path) {
|
||||
const apiKey = await getConfig('forecasting_api_key')
|
||||
const baseUrl = (await getConfig('forecasting_url')) || 'http://10.10.10.113:3080'
|
||||
if (!apiKey) throw new Error('Forecasting API key not configured — add it in Settings')
|
||||
const res = await fetch(`${baseUrl}/forecasting/api/public${path}`, {
|
||||
headers: { 'X-API-Key': apiKey },
|
||||
signal: AbortSignal.timeout(15000),
|
||||
})
|
||||
if (!res.ok) throw new Error(`Forecasting API ${res.status} — ${path}`)
|
||||
return res.json()
|
||||
}
|
||||
|
||||
function daysBetween(from, to) {
|
||||
const a = new Date(from + 'T00:00:00')
|
||||
const b = new Date(to + 'T00:00:00')
|
||||
return Math.max(1, Math.ceil((b - a) / 86_400_000) + 1)
|
||||
}
|
||||
|
||||
export async function netSalesRoutes(fastify) {
|
||||
fastify.addHook('preHandler', requireAuth)
|
||||
|
||||
// Returns daily net sales and prior-year net sales for a date range.
|
||||
fastify.get('/api/net-sales', { preHandler: requireCap('view') }, async (request, reply) => {
|
||||
const { from, to } = request.query
|
||||
if (!from || !to) return reply.status(400).send({ error: 'from and to required' })
|
||||
|
||||
const days = daysBetween(from, to)
|
||||
const data = await fcFetch(`/forecast/revenue?start_date=${from}&days=${days}&type=all&dow_align=true`)
|
||||
|
||||
const result = (data?.data ?? []).map(d => ({
|
||||
date: d.date,
|
||||
net_sales: parseFloat(d.total?.otb ?? 0),
|
||||
py_sales: parseFloat(d.total?.prior_final ?? 0),
|
||||
accom: parseFloat(d.accom?.otb ?? 0),
|
||||
dry: parseFloat(d.dry?.otb ?? 0),
|
||||
wet: parseFloat(d.wet?.otb ?? 0),
|
||||
is_past: d.is_past ?? true,
|
||||
}))
|
||||
|
||||
return { days: result }
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue