Fix timesheet sync overwriting future rota dates with empty data
Pull timesheets was writing source='timesheet' rows for future dates (no clock-in data yet), giving ? / 0.00h and preventing rota sync from ever healing those dates. Three-part fix: - Backend runTimesheetSync: cap end date to today before looping - Frontend syncTimesheetData: cap end date to today before calling API - Backend runRotaSync: only skip timesheet-sourced dates that are <= today, so any future dates accidentally marked as timesheet are self-healed next time rota sync runs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ffefa1914f
commit
c19460ecb3
5 changed files with 34 additions and 26 deletions
|
|
@ -56,10 +56,13 @@ export async function runRotaSync(from, to) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip dates already replaced with timesheet actuals
|
// Skip past dates already replaced with timesheet actuals (future timesheet rows are allowed through)
|
||||||
|
const today = fmtDate(new Date())
|
||||||
const existing = await getWorkforceShifts(from, to)
|
const existing = await getWorkforceShifts(from, to)
|
||||||
const timesheetDates = new Set(
|
const timesheetDates = new Set(
|
||||||
Object.entries(existing).filter(([, d]) => d.source === 'timesheet').map(([date]) => date)
|
Object.entries(existing)
|
||||||
|
.filter(([date, d]) => d.source === 'timesheet' && date <= today)
|
||||||
|
.map(([date]) => date)
|
||||||
)
|
)
|
||||||
|
|
||||||
const dailyRows = []
|
const dailyRows = []
|
||||||
|
|
@ -81,6 +84,10 @@ export async function runTimesheetSync(from, to) {
|
||||||
|
|
||||||
if (!from || !to) ({ from, to } = defaultTimesheetWindow())
|
if (!from || !to) ({ from, to } = defaultTimesheetWindow())
|
||||||
|
|
||||||
|
// Never write timesheet rows for future dates — actual hours don't exist yet
|
||||||
|
const today = fmtDate(new Date())
|
||||||
|
if (to > today) to = today
|
||||||
|
|
||||||
const fromDate = new Date(from + 'T00:00:00')
|
const fromDate = new Date(from + 'T00:00:00')
|
||||||
const toDate = new Date(to + 'T00:00:00')
|
const toDate = new Date(to + 'T00:00:00')
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
2
frontend/dist/index.html
vendored
2
frontend/dist/index.html
vendored
|
|
@ -5,7 +5,7 @@
|
||||||
<meta name="viewport" content="width=1280" />
|
<meta name="viewport" content="width=1280" />
|
||||||
<meta name="theme-color" content="#2d6a4f" />
|
<meta name="theme-color" content="#2d6a4f" />
|
||||||
<title>HK Planner</title>
|
<title>HK Planner</title>
|
||||||
<script type="module" crossorigin src="/hk-planner/assets/index-Bp4tnr99.js"></script>
|
<script type="module" crossorigin src="/hk-planner/assets/index-CY2exSVU.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/hk-planner/assets/index-B7_UXJgZ.css">
|
<link rel="stylesheet" crossorigin href="/hk-planner/assets/index-B7_UXJgZ.css">
|
||||||
<link rel="manifest" href="/hk-planner/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/hk-planner/registerSW.js"></script></head>
|
<link rel="manifest" href="/hk-planner/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/hk-planner/registerSW.js"></script></head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
2
frontend/dist/sw.js
vendored
2
frontend/dist/sw.js
vendored
|
|
@ -1 +1 @@
|
||||||
if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,r)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(i[t])return;let o={};const c=e=>n(e,t),d={module:{uri:t},exports:o,require:c};i[t]=Promise.all(s.map(e=>d[e]||c(e))).then(e=>(r(...e),o))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"a2c395d8c225f1b3ea12388f15189bce"},{url:"index.html",revision:"455924148ee524d7363268ccabc71ea8"},{url:"icons/icon-512.png",revision:"c32202b9deed67ef38331f63dec9d1c8"},{url:"icons/icon-192.png",revision:"46ece317d50d10b8f5e225e073b3221d"},{url:"assets/index-Bp4tnr99.js",revision:null},{url:"assets/index-B7_UXJgZ.css",revision:null},{url:"manifest.webmanifest",revision:"c2510de876adb84db0c4b300b71216fa"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/hk-planner/index.html"),{denylist:[/\/api\//]}))});
|
if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(i[o])return;let t={};const d=e=>n(e,o),c={module:{uri:o},exports:t,require:d};i[o]=Promise.all(s.map(e=>c[e]||d(e))).then(e=>(r(...e),t))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"a2c395d8c225f1b3ea12388f15189bce"},{url:"index.html",revision:"57d49ab8e52eda5d76e17210e69e20a4"},{url:"icons/icon-512.png",revision:"c32202b9deed67ef38331f63dec9d1c8"},{url:"icons/icon-192.png",revision:"46ece317d50d10b8f5e225e073b3221d"},{url:"assets/index-CY2exSVU.js",revision:null},{url:"assets/index-B7_UXJgZ.css",revision:null},{url:"manifest.webmanifest",revision:"c2510de876adb84db0c4b300b71216fa"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/hk-planner/index.html"),{denylist:[/\/api\//]}))});
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,8 @@ export function Planner() {
|
||||||
async function syncTimesheetData() {
|
async function syncTimesheetData() {
|
||||||
if (!bookings) return
|
if (!bookings) return
|
||||||
const start = bookings.dates[0]
|
const start = bookings.dates[0]
|
||||||
const end = bookings.dates[bookings.dates.length - 1]
|
const today = todayStr()
|
||||||
|
const end = bookings.dates[bookings.dates.length - 1] > today ? today : bookings.dates[bookings.dates.length - 1]
|
||||||
setTimesheetSyncing(true)
|
setTimesheetSyncing(true)
|
||||||
try {
|
try {
|
||||||
await syncTimesheets(start, end)
|
await syncTimesheets(start, end)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue