Give the analytics overview a per-parent, per-year balance series
Each parent projection now carries one entry per calendar year from birth to the fourth birthday: vacation earned, carried in, planned, left, carried out and expiring under Semesterlagen's 20-day floor and 5-year carry-over, plus parental-leave days used and left at year end with the age-4 clamp applied in the birthday year. The overview sums them into per-year totals. Frontend types, mapper and fixtures follow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBRLomdLK9jF7wU7a8AA6H
This commit is contained in:
@@ -33,11 +33,36 @@ describe('API response mappers (snake_case → camelCase)', () => {
|
||||
savable_after_b4: 96,
|
||||
expiring_at_b4: 134,
|
||||
savable_cap_at_b4: 96,
|
||||
year_balances: [
|
||||
{
|
||||
personal_data_uuid: 'pd1',
|
||||
child_id: 'child1',
|
||||
year: 2024,
|
||||
vacation: {
|
||||
year: 2024,
|
||||
earned: 25,
|
||||
is_assumed: true,
|
||||
carried_in: 0,
|
||||
planned_use: 2,
|
||||
left: 23,
|
||||
carried_out: 5,
|
||||
expiring: 0,
|
||||
},
|
||||
parental_leave: {
|
||||
year: 2024,
|
||||
sjukpenning_used: 10,
|
||||
lagsta_used: 0,
|
||||
sjukpenning_left: 380,
|
||||
lagsta_left: 90,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
combined_days_used: 100,
|
||||
combined_days_left: 380,
|
||||
combined_expiring_at_b4: 134,
|
||||
year_totals: [{ year: 2024, pl_left: 470, vacation_left: 23 }],
|
||||
}
|
||||
|
||||
fetchMock.mockResolvedValue({
|
||||
@@ -61,6 +86,19 @@ describe('API response mappers (snake_case → camelCase)', () => {
|
||||
expect(result.parentProjections[0]!.savableAfterB4).toBe(96)
|
||||
expect(result.parentProjections[0]!.expiringAtB4).toBe(134)
|
||||
expect(result.parentProjections[0]!.savableCapAtB4).toBe(96)
|
||||
|
||||
const yearBalances = result.parentProjections[0]!.yearBalances
|
||||
expect(yearBalances.length).toBe(1)
|
||||
expect(yearBalances[0]!.personalDataUuid).toBe('pd1')
|
||||
expect(yearBalances[0]!.childId).toBe('child1')
|
||||
expect(yearBalances[0]!.year).toBe(2024)
|
||||
expect(yearBalances[0]!.vacation.isAssumed).toBe(true)
|
||||
expect(yearBalances[0]!.vacation.plannedUse).toBe(2)
|
||||
expect(yearBalances[0]!.vacation.carriedOut).toBe(5)
|
||||
expect(yearBalances[0]!.parentalLeave.sjukpenningUsed).toBe(10)
|
||||
expect(yearBalances[0]!.parentalLeave.sjukpenningLeft).toBe(380)
|
||||
|
||||
expect(result.yearTotals).toEqual([{ year: 2024, plLeft: 470, vacationLeft: 23 }])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ import type {
|
||||
PersonalData,
|
||||
CalendarWindow,
|
||||
CalendarDayEntry,
|
||||
VacationYearBalance,
|
||||
ParentalLeaveYearBalance,
|
||||
ParentYearBalance,
|
||||
YearTotals,
|
||||
} from '@/types'
|
||||
|
||||
const BASE = '/api'
|
||||
@@ -106,6 +110,82 @@ function toParentalLeaveFetch(raw: RawParentalLeaveFetch): ParentalLeaveFetch {
|
||||
}
|
||||
}
|
||||
|
||||
export interface RawVacationYearBalance {
|
||||
year: number
|
||||
earned: number
|
||||
is_assumed: boolean
|
||||
carried_in: number
|
||||
planned_use: number
|
||||
left: number
|
||||
carried_out: number
|
||||
expiring: number
|
||||
}
|
||||
|
||||
function toVacationYearBalance(raw: RawVacationYearBalance): VacationYearBalance {
|
||||
return {
|
||||
year: raw.year,
|
||||
earned: raw.earned,
|
||||
isAssumed: raw.is_assumed,
|
||||
carriedIn: raw.carried_in,
|
||||
plannedUse: raw.planned_use,
|
||||
left: raw.left,
|
||||
carriedOut: raw.carried_out,
|
||||
expiring: raw.expiring,
|
||||
}
|
||||
}
|
||||
|
||||
export interface RawParentalLeaveYearBalance {
|
||||
year: number
|
||||
sjukpenning_used: number
|
||||
lagsta_used: number
|
||||
sjukpenning_left: number
|
||||
lagsta_left: number
|
||||
}
|
||||
|
||||
function toParentalLeaveYearBalance(
|
||||
raw: RawParentalLeaveYearBalance,
|
||||
): ParentalLeaveYearBalance {
|
||||
return {
|
||||
year: raw.year,
|
||||
sjukpenningUsed: raw.sjukpenning_used,
|
||||
lagstaUsed: raw.lagsta_used,
|
||||
sjukpenningLeft: raw.sjukpenning_left,
|
||||
lagstaLeft: raw.lagsta_left,
|
||||
}
|
||||
}
|
||||
|
||||
export interface RawParentYearBalance {
|
||||
personal_data_uuid: string
|
||||
child_id: string
|
||||
year: number
|
||||
vacation: RawVacationYearBalance
|
||||
parental_leave: RawParentalLeaveYearBalance
|
||||
}
|
||||
|
||||
function toParentYearBalance(raw: RawParentYearBalance): ParentYearBalance {
|
||||
return {
|
||||
personalDataUuid: raw.personal_data_uuid,
|
||||
childId: raw.child_id,
|
||||
year: raw.year,
|
||||
vacation: toVacationYearBalance(raw.vacation),
|
||||
parentalLeave: toParentalLeaveYearBalance(raw.parental_leave),
|
||||
}
|
||||
}
|
||||
|
||||
export interface RawYearTotals {
|
||||
year: number
|
||||
pl_left: number
|
||||
vacation_left: number
|
||||
}
|
||||
|
||||
function toYearTotals(raw: RawYearTotals): YearTotals {
|
||||
return {
|
||||
year: raw.year,
|
||||
plLeft: raw.pl_left,
|
||||
vacationLeft: raw.vacation_left,
|
||||
}
|
||||
}
|
||||
|
||||
export interface RawAge4Projection {
|
||||
personal_data_uuid: string
|
||||
child_id: string
|
||||
@@ -118,6 +198,7 @@ export interface RawAge4Projection {
|
||||
savable_after_b4: number
|
||||
expiring_at_b4: number
|
||||
savable_cap_at_b4: number
|
||||
year_balances: RawParentYearBalance[]
|
||||
}
|
||||
|
||||
function toAge4Projection(raw: RawAge4Projection): Age4Projection {
|
||||
@@ -133,6 +214,7 @@ function toAge4Projection(raw: RawAge4Projection): Age4Projection {
|
||||
savableAfterB4: raw.savable_after_b4,
|
||||
expiringAtB4: raw.expiring_at_b4,
|
||||
savableCapAtB4: raw.savable_cap_at_b4,
|
||||
yearBalances: raw.year_balances.map(toParentYearBalance),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +225,7 @@ export interface RawWorkspaceOverview {
|
||||
combined_days_used: number
|
||||
combined_days_left: number
|
||||
combined_expiring_at_b4: number
|
||||
year_totals: RawYearTotals[]
|
||||
}
|
||||
|
||||
function toWorkspaceOverview(raw: RawWorkspaceOverview): WorkspaceOverview {
|
||||
@@ -153,6 +236,7 @@ function toWorkspaceOverview(raw: RawWorkspaceOverview): WorkspaceOverview {
|
||||
combinedDaysUsed: raw.combined_days_used,
|
||||
combinedDaysLeft: raw.combined_days_left,
|
||||
combinedExpiringAtB4: raw.combined_expiring_at_b4,
|
||||
yearTotals: raw.year_totals.map(toYearTotals),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user