Gå til innhold

Låsing av perioder for timeregistrering

Låsing av perioder hindrer endringer i timeregistreringer som er datert før en valgt dato. Den brukes typisk etter at en periode er kjørt gjennom lønn — da skal ingen kunne flytte timer tilbakevirkende uten at administrator først slipper opp låsen.

Innstillingen ligger som fanen Periode under Innstillinger → Portalinnstillinger og består av to deler:

  • Låsedato for timeregistrering (ny, datobasert lås — anbefales)
  • Eldre periodestatus (legacy månedsbasert lås — beholdt for kompatibilitet)

Tilgang

Feilmelding ved fremtidsdato

Info-boks om aktiv låsedato

Dato-input for låsedato

Låsing av perioder — full skjerm

Hvem Hva trengs
Rolle Bruker med modul-tilgang til Portalinnstillinger (new === truemoduleID = 99 i userAccess)
Modul Portalinnstillinger — moduleId: 99/settings/portal-setting-ruten (setting-routing.module.ts:30)
Effekt på hvem Alle ansatte og ledere i samme tenant (klient-database)

Tilgangssjekken er todelt: ModuleGuard blokkerer hele ruten, og komponentens egen checkAdminAccess() mirrorerer samme regel for å skjule UI når brukeren ikke har tilgang (portal-setting.component.ts:197-208).

Hele "Periode"-fanen med både ny låsedato-seksjon (kort øverst) og legacy "Eldre periodestatus"-knapp under


Datobasert låsing (anbefalt)

Den nye mekanismen består av to felt og en lagre-knapp øverst i fanen.

Aktiver låsing av tidsperiode

i18n-nøkkel: PortalSetting.enableTimeLock → «Aktiver låsing av tidsperiode» Hjelpetekst: PortalSetting.timeLockDescription → «Når aktivert kan ansatte ikke registrere eller endre timer før valgt dato»

På/av-bryter (toggle) som styrer om låsedato-feltet og info-boksen vises. Når bryteren slås av, kaller komponenten disableLockDate() som umiddelbart sender lockDate: null til backend og deaktiverer låsing (portal-setting.component.ts:1249-1284).

Standardverdi: Av ({ lockDate: null, isEnabled: false } i komponent-state, portal-setting.component.ts:84). IsEnabled beregnes på backend som LockDate.HasValue (TimeLockSettingsDto.cs:21).

Låsedato

i18n-nøkkel: PortalSetting.lockDate → «Låsedato» Hjelpetekst: PortalSetting.lockDateHelp → «Alle timeregistreringer før denne datoen kan ikke endres»

HTML5 <input type="date">-felt med dynamisk min/maks:

Når en gyldig dato er valgt, vises en blå info-boks med teksten «Timeregistreringer før denne datoen er låst: \<dato>» (PortalSetting.lockDateCurrentInfo + datoen formatert som dd.MM.yyyy, portal-setting.component.html:936-940).

Lagre

Lagre-knappen øverst (<app-settings-save-bar labelKey="Common.Save">) er disabled så lenge én av disse er sant:

  • lockDateSettings.isEnabled er false, eller
  • lockDateSettings.lockDate er tom

(portal-setting.component.html:882-886)

Klikk på Lagre kjører updateLockDate(). Datoen konverteres til UTC-midnatt-ISO-streng for å unngå tidssone-shift (eks. "2026-01-04""2026-01-04T00:00:00.000Z", portal-setting.component.ts:1269-1275) og sendt til POST /api/PeriodStatus/UpdateLockDate.

Klient-side-validering: Dersom valgt dato er etter i dag, avvises lagringen lokalt med toast PortalSetting.lockDateFutureError → «Låsedato kan ikke være i fremtiden» (portal-setting.component.ts:1206-1220).

Suksess-toast: PortalSetting.lockDateUpdated → «Låsedato oppdatert».

Toggle "Aktiver låsing av tidsperiode" deaktivert (av), kun bryteren og hjelpetekst synlig

Toggle aktivert (på) — dato-felt og blå info-boks med valgt dato synlig

Datalagring

Datobasert låsing lagres direkte i tabellen wv_PeriodStatus (klient-database), ikke i wv_SystemConfiguration. Repositoryt skriver via raw SQL mot kolonnene LockDate, UpdatedDate og UpdatedBy på raden for inneværende år (p_Year = DateTime.Now.Year) — INSERT hvis ikke raden finnes, UPDATE hvis den finnes (PeriodStatusRepository.cs:183-240).

Tabellen wv_PeriodStatus har følgende relevante kolonner (dbscript.sql:7226):

  • p_Year (int, PK)
  • p_m1Activep_m12Active (bit) — brukes av legacy månedslås
  • LockDate (date NULL) — ny datobasert lås
  • UpdatedDate (datetime NULL) — settes alltid til GETDATE() ved skriv
  • UpdatedBy (int NULL) — bruker-ID fra JWT-claims

Backend leser den nyeste raden (ORDER BY p_Year DESC) når UI henter innstillingen (PeriodStatusRepository.cs:144-176).

Backend-validering og bivirkninger

I tillegg til klient-side-sjekken validerer PeriodStatusController.UpdateLockDate(...) at datoen ikke er i fremtiden og at bruker-ID kan leses fra JWT. Begge feilbaner returneres som ApiResponse { Success = false, ErrorMessage }:

  • «Lock date cannot be in the future» (engelsk, fra backend)
  • «Invalid user ID» (engelsk, fra backend)

(PeriodStatusController.cs:130-152)

Når en gyldig låsedato lagres, og JWT-claimen CCNotifyLockedPeriod er "True", trigges en ISY Jobtech-synkronisering for hele perioden fra 1. januar samme år til låsedatoen (PeriodStatusRepository.cs:233-238). URL-en hentes fra claimen CCLockedPeriodNotificationUrl. Bivirkningen kjører kun når begge claims er satt — for tenants uten ISY Jobtech-integrasjon skjer ingenting.

Hvordan låsen håndheves

Backend tilbyr endepunktet GET /api/PeriodStatus/IsDateLocked?date=<dato> som returnerer true hvis date <= LockDate. Frontend-kode må kalle dette eksplisitt — det er ingen global middleware som blokkerer POST/PUT mot timeregistreringer.

Konkrete bruksområder:

Admin-fane (Kontroll av timer) bruker fortsatt den eldre månedsbaserte sjekken — se neste seksjon.


Eldre periodestatus (legacy månedslås)

Eldre månedsbasert mekanikk vises som en sammenklappbar seksjon under datolåsen. Den åpnes med knappen «Eldre periodestatus» (PortalSetting.legacyPeriodStatusToggle) som veksler legacyPeriodCollapsed (portal-setting.component.html:947-956).

Når åpen vises:

  • En gul advarsel: «OBS: Gammel periodestatus-funksjon — Denne funksjonen er erstattet av ny låsedato-funksjon ovenfor. Bruk låsedato for enklere administrasjon. Tabellen nedenfor beholdes for kompatibilitet.» (PortalSetting.legacyPeriodStatusWarning + PortalSetting.legacyPeriodStatusInfo).
  • En smart-grid med en rad per år og 12 sjekkbokser (én per måned) i kolonnene m1Activem12Active. Klikk på blyantknappen aktiverer redigering for raden; lagring sender én oppdatering per år via POST /api/PeriodStatus/UpdatePeriodStatus (portal-setting.component.html:967-1002, portal-setting.component.ts:1130-1148).

Den eldre månedslåsen er den eneste som faktisk blokkerer admin-import av timer: CheckHoursController.Save slår opp PeriodStatus.Locked per måned og sender systemmelding «Den valgte perioden er låst: \<år> - \<måned>» til godkjennere, men lar admin lagre uansett (CheckHoursController.cs:298-322).

Både GetPeriodStatusList og UpdatePeriodStatus-endepunktene er markert [Obsolete] i koden (PeriodStatusController.cs:35, 55).

"Eldre periodestatus" utvidet, med advarsel og smart-grid med år + 12 måneds-sjekkbokser


Slik setter du en låsedato (datobasert)

  1. Gå til Innstillinger → Portalinnstillinger og velg fanen Periode (URL: /settings/portal-setting?tab=period).
  2. Slå på bryteren Aktiver låsing av tidsperiode.
  3. Velg dato i feltet Låsedato. Min-grense styres av første eksisterende timeregistrering i tenanten, maks er i dag.
  4. Verifiser at den blå info-boksen viser riktig dato: «Timeregistreringer før denne datoen er låst: \<dato>».
  5. Klikk Lagre øverst. Toast «Låsedato oppdatert» bekrefter at backend har lagret.

Slik slår du av låsing

  1. Slå av bryteren Aktiver låsing av tidsperiode.
  2. Komponenten kaller umiddelbart disableLockDate() som sender lockDate: null til backend — ingen separat Lagre-klikk er nødvendig (portal-setting.component.ts:1249-1284).

Vanlige problemer

«Låsedato kan ikke være i fremtiden»

Toast vist når valgt dato er senere enn i dag. Både klient (updateLockDate()) og backend (UpdateLockDate) sjekker dette — meldingen brukeren ser kommer fra klient-sjekken. Velg dagens dato eller en dato i fortiden.

Lagre-knappen er disabled

Knappen aktiveres kun når toggle er på og låsedato er satt. Hvis du nettopp slo på bryteren, må du også velge en dato før Lagre blir klikkbar.

Jeg ser ikke fanen «Periode»

Du har ikke tilgang til Portalinnstillinger (moduleID = 99 med new = true på din userLevel). Be administrator om å legge til tilgangen via Datatilgang / Brukerrettigheter.

En tidligere låst registrering er plutselig redigerbar

Du eller en annen administrator har sannsynligvis flyttet låsedatoen bakover, eller slått av bryteren. Endringen er reversibel — sett bryteren på igjen eller flytt datoen fremover.

Time-registrerings-skjermen viser «Perioden er låst», men admin-sider gjør ikke

Den datobaserte låsen håndheves i time-registration.component via et eksplisitt IsDateLocked-kall per valgt dato. Andre flater (admin-import via Kontroll av timer, eldre rapporter) bruker fortsatt den månedsbaserte legacy-sjekken. Hvis du vil sperre admin-import, må du også sette tilsvarende måneder som inaktive i «Eldre periodestatus»-tabellen.


Relaterte sider