Compare commits
2 commits
be1ef7f99e
...
91bdb63a90
| Author | SHA1 | Date | |
|---|---|---|---|
| 91bdb63a90 | |||
| 2543e09387 |
1 changed files with 44 additions and 5 deletions
|
|
@ -94,10 +94,9 @@ function weeklyFetchDates(from, to) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch timesheets for a single date — no location param (mirrors PBI pattern).
|
// Fetch timesheets for a single date — no location param (mirrors PBI pattern).
|
||||||
// Returns the weekly timesheet record(s) containing this date.
|
// include_oncosts=true is required to get cost_with_oncosts on nested shifts[].
|
||||||
// Each record: { id, user_id, shifts: [{ department_id, date, cost, leave_request_id, ... }] }
|
|
||||||
async function fetchTimesheetsForDate(dateStr) {
|
async function fetchTimesheetsForDate(dateStr) {
|
||||||
try { return await wfFetchPaged(`/api/v2/timesheets/on/${dateStr}?show_costs=true`) }
|
try { return await wfFetchPaged(`/api/v2/timesheets/on/${dateStr}?show_costs=true&include_oncosts=true`) }
|
||||||
catch { return [] }
|
catch { return [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -469,13 +468,42 @@ export async function compareEndpoints(from, to) {
|
||||||
// A3: Same shifts, no client-side dept filter (trust the API filter entirely)
|
// A3: Same shifts, no client-side dept filter (trust the API filter entirely)
|
||||||
|
|
||||||
// B: Per-day timesheets + shift.id dedup, filtered by locationDeptIds
|
// B: Per-day timesheets + shift.id dedup, filtered by locationDeptIds
|
||||||
const [a, a2, a3, b] = await Promise.all([
|
// C: Salaried staff — users with salary field, report_department_id in location depts
|
||||||
|
const [a, a2, a3, b, allUsers] = await Promise.all([
|
||||||
fetchShiftsAndSummarise(shiftsReportLoc, locationDeptIds),
|
fetchShiftsAndSummarise(shiftsReportLoc, locationDeptIds),
|
||||||
fetchShiftsAndSummarise(shiftsReportLoc, reportLocationDeptIds),
|
fetchShiftsAndSummarise(shiftsReportLoc, reportLocationDeptIds),
|
||||||
fetchShiftsAndSummarise(shiftsReportLoc, null), // trust API filter entirely
|
fetchShiftsAndSummarise(shiftsReportLoc, null),
|
||||||
fetchTimesheetsByDay(from, to, locationDeptIds),
|
fetchTimesheetsByDay(from, to, locationDeptIds),
|
||||||
|
wfFetchPaged('/api/v2/users?show_wages=true&show_inactive=false'),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// Find salaried staff based at this location (report_department_id in a location dept)
|
||||||
|
const salaryUsers = locationDeptIds
|
||||||
|
? allUsers.filter(u => u.salary != null && locationDeptIds.has(String(u.report_department_id)))
|
||||||
|
: allUsers.filter(u => u.salary != null)
|
||||||
|
|
||||||
|
// Prorate salary for the query period: annual / 365 * days, or monthly / 31 * days
|
||||||
|
const days = Math.max(1, Math.ceil((new Date(to) - new Date(from)) / 86_400_000) + 1)
|
||||||
|
const salaryDetails = salaryUsers.map(u => {
|
||||||
|
const sal = u.salary
|
||||||
|
// salary field structure unknown until we see it — expose the raw value
|
||||||
|
return {
|
||||||
|
user_id: u.id,
|
||||||
|
name: u.name || `${u.legal_first_name || ''} ${u.legal_last_name || ''}`.trim(),
|
||||||
|
report_department_id: u.report_department_id,
|
||||||
|
salary_raw: sal,
|
||||||
|
// Attempt prorations based on common WF salary field shapes
|
||||||
|
// If salary is a number: assume annual amount
|
||||||
|
annual_if_number: typeof sal === 'number' ? sal : null,
|
||||||
|
monthly_if_number: typeof sal === 'number' ? +(sal / 12).toFixed(2) : null,
|
||||||
|
period_prorated: typeof sal === 'number' ? +(sal / 365 * days).toFixed(2) : null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const totalSalaryAnnual = salaryDetails.reduce((s, u) => s + (u.annual_if_number ?? 0), 0)
|
||||||
|
const totalSalaryMonthly = salaryDetails.reduce((s, u) => s + (u.monthly_if_number ?? 0), 0)
|
||||||
|
const totalSalaryProrated = salaryDetails.reduce((s, u) => s + (u.period_prorated ?? 0), 0)
|
||||||
|
|
||||||
// Dept breakdown for report_location_id depts — to see which extra depts A2 picks up vs A
|
// Dept breakdown for report_location_id depts — to see which extra depts A2 picks up vs A
|
||||||
const extraDepts = reportLocationDeptIds && locationDeptIds
|
const extraDepts = reportLocationDeptIds && locationDeptIds
|
||||||
? allDepts
|
? allDepts
|
||||||
|
|
@ -493,6 +521,17 @@ export async function compareEndpoints(from, to) {
|
||||||
A2_shifts_api_filtered_report_dept_filter: { ...a2, note: 'shifts + report_location_id API filter + client dept filter (location_id OR report_location_id match)' },
|
A2_shifts_api_filtered_report_dept_filter: { ...a2, note: 'shifts + report_location_id API filter + client dept filter (location_id OR report_location_id match)' },
|
||||||
A3_shifts_api_filtered_no_client_filter: { ...a3, note: 'shifts + report_location_id API filter only — no client-side dept filter' },
|
A3_shifts_api_filtered_no_client_filter: { ...a3, note: 'shifts + report_location_id API filter only — no client-side dept filter' },
|
||||||
B_timesheets_per_day_dept_filter: b,
|
B_timesheets_per_day_dept_filter: b,
|
||||||
|
C_salaried_staff: {
|
||||||
|
count: salaryUsers.length,
|
||||||
|
period_days: days,
|
||||||
|
total_annual: +totalSalaryAnnual.toFixed(2),
|
||||||
|
total_monthly: +totalSalaryMonthly.toFixed(2),
|
||||||
|
total_prorated: +totalSalaryProrated.toFixed(2),
|
||||||
|
// Combined: A shifts oncostExLeave + prorated salary
|
||||||
|
combined_oncostExLeave_plus_salary_prorated: +(a.oncostExLeave + totalSalaryProrated).toFixed(2),
|
||||||
|
note: 'users with salary field set, report_department_id in location depts. salary_raw shows the actual API field shape.',
|
||||||
|
staff: salaryDetails,
|
||||||
|
},
|
||||||
wf_report_reference: {
|
wf_report_reference: {
|
||||||
note: 'Target: WF "Cost by Location and Team" — Timesheet exc. leave inc. allowances',
|
note: 'Target: WF "Cost by Location and Team" — Timesheet exc. leave inc. allowances',
|
||||||
timesheet_exc_leave: 51005.72,
|
timesheet_exc_leave: 51005.72,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue