Skip to content

Commit 1dcf5c8

Browse files
renemadsenclaude
andcommitted
Add design spec for GLS-A/3F Jordbrug Pay Rule Set tests
Covers standard agriculture, animal care (pasning af dyr), apprentice under-18/over-18 variants, and rule set transitions. Defines 5 Pay Rule Set fixtures, backend NUnit test cases, and E2E Playwright scenarios. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0c3015c commit 1dcf5c8

1 file changed

Lines changed: 380 additions & 0 deletions

File tree

Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
# GLS-A/3F Jordbrug Pay Rule Set Test Design
2+
3+
## Context
4+
5+
The timeplanning plugin needs tests that verify Pay Rule Sets correctly classify hours into pay codes according to the GLS-A/3F Jordbrugsoverenskomsten 2024-2026. These pay codes drive the Excel export used for payroll calculations. The system must handle:
6+
7+
- **Standard agriculture** workers (field work, crop production)
8+
- **Animal care** workers (pasning af dyr - different night/weekend supplements)
9+
- **Apprentices under 18** (strict legal hour caps: 8h/day, 40h/week)
10+
- **Apprentices over 18** (different overtime tier: 50% first 2h vs adult 30%)
11+
- **Transitions** from apprentice to standard/animal care mid-period
12+
13+
Classification only - no enforcement of hour caps. The Excel export must show correct pay code splits for a given period.
14+
15+
## Overenskomst Rules Summary
16+
17+
Source: Jordbrugsoverenskomsten 2024-2026 between GLS-A and 3F Den Gronne Gruppe.
18+
19+
### Standard Agriculture (SS 8-9, 22-23)
20+
21+
| Rule | Detail |
22+
|------|--------|
23+
| Normal hours | 37h/week, Mon-Sat 06:00-18:00 |
24+
| Daily norm | 7.4h (37h / 5 days) |
25+
| Overtime 1st+2nd hour | +30% of B-lon |
26+
| Overtime 3rd+ hour | +80% of B-lon |
27+
| Overtime Sun/Holiday | +80% of B-lon (all hours) |
28+
| Shifted: before 06:00 | Up to 2h, supplement per hour |
29+
| Shifted: after 18:00 | Up to 2h, supplement per hour |
30+
| Saturday before 12:00 | Normal rate |
31+
| Saturday after 12:00 | Supplement per day |
32+
| Sunday/Holiday | Supplement per day |
33+
| Grundlovsdag | Half day (separate from Holiday) |
34+
35+
### Animal Care - SS 15 (on top of standard)
36+
37+
| Rule | Detail |
38+
|------|--------|
39+
| Night Mon-Sat 00:00-05:00 | Supplement per hour |
40+
| Saturday after 12:00 | Supplement per occurrence |
41+
| Sunday/Holiday | Supplement per day |
42+
| Working time | Can be placed all 7 days, hele dognet |
43+
| Weekly norm | 37h/week or 296h/8 weeks |
44+
45+
### Apprentice Under-18 (Arbejdstilsynet + GLS-A)
46+
47+
| Rule | Detail |
48+
|------|--------|
49+
| Max daily | 8 hours |
50+
| Max weekly | 40 hours |
51+
| Night restriction | No work 20:00-06:00 (agriculture: start from 04:00 for stald) |
52+
| Daily rest | 12 consecutive hours |
53+
| Weekly rest | 2 consecutive days |
54+
| Break | 30 min after 4.5h |
55+
| Overtime Sun/Holiday 1st 2h | +50% |
56+
| Overtime Sun/Holiday 3rd+ | +80% |
57+
58+
### Apprentice Over-18 (GLS-A)
59+
60+
| Rule | Detail |
61+
|------|--------|
62+
| Working time | Same as adult (37h/week) |
63+
| Overtime weekday 1st+2nd h | Same as adult (+30%) |
64+
| Overtime Sun/Holiday 1st 2h | +50% (NOT 30% like adults) |
65+
| Overtime Sun/Holiday 3rd+ | +80% |
66+
67+
## Architecture
68+
69+
### Approach: A+C Hybrid
70+
71+
- **Backend (A):** Fixture-based Pay Rule Set configurations as C# helper methods, one per overenskomst variant. Unit tests feed PlanRegistration data into `PayLineGenerator` and assert correct PayCode/seconds output.
72+
- **E2E (C):** Scenario-driven Playwright tests that create rule sets through UI, register work hours for realistic weeks/months, export Excel, and verify pay code columns.
73+
74+
### Existing Infrastructure Used
75+
76+
| Component | Location | Purpose |
77+
|-----------|----------|---------|
78+
| `PayRuleSet` entity | `eform-timeplanning-base/.../Entities/PayRuleSet.cs` | Top-level rule container |
79+
| `PayDayRule` entity | `eform-timeplanning-base/.../Entities/PayDayRule.cs` | Rules per day code |
80+
| `PayTierRule` entity | `eform-timeplanning-base/.../Entities/PayTierRule.cs` | Tiered hour allocation |
81+
| `PayDayTypeRule` entity | `eform-timeplanning-base/.../Entities/PayDayTypeRule.cs` | Rules per day type enum |
82+
| `PayTimeBandRule` entity | `eform-timeplanning-base/.../Entities/PayTimeBandRule.cs` | Time-of-day bands |
83+
| `PayLineGenerator` | `eform-timeplanning-base/.../Helpers/PayLineGenerator.cs` | Core calculation engine |
84+
| `WorkingTimeRuleSet` entity | `eform-timeplanning-base/.../Entities/WorkingTimeRuleSet.cs` | Normal hours/overtime config |
85+
| `AssignedSiteRuleSetAssignments` | `eform-timeplanning-base/.../Entities/AssignedSiteRuleSetAssignments.cs` | Time-bound rule set assignment |
86+
| `PlanRegistrationPayLine` | `eform-timeplanning-base/.../Entities/PlanRegistrationPayLine.cs` | Output pay line records |
87+
88+
## Pay Rule Set Fixtures
89+
90+
### Fixture 1: GlsA_Jordbrug_Standard
91+
92+
```
93+
PayRuleSet "GLS-A Jordbrug Standard"
94+
WorkingTimeRuleSet:
95+
WeeklyNormalSeconds = 133200 (37h)
96+
DailyNormalSeconds = 26640 (7.4h)
97+
WeekStartsOn = Monday
98+
OvertimeBasis = DailyThenWeekly
99+
100+
PayDayRules:
101+
WEEKDAY:
102+
Tier 1: Order=1, UpToSeconds=26640 (7.4h), PayCode="NORMAL"
103+
Tier 2: Order=2, UpToSeconds=33840 (7.4h+2h), PayCode="OVERTIME_30"
104+
Tier 3: Order=3, UpToSeconds=null, PayCode="OVERTIME_80"
105+
106+
SATURDAY:
107+
Tier 1: Order=1, UpToSeconds=null, PayCode="SAT_WORK"
108+
(Saturday time-of-day split handled via PayDayTypeRules below)
109+
110+
SUNDAY:
111+
Tier 1: Order=1, UpToSeconds=null, PayCode="SUN_HOLIDAY"
112+
113+
HOLIDAY:
114+
Tier 1: Order=1, UpToSeconds=null, PayCode="SUN_HOLIDAY"
115+
116+
GRUNDLOVSDAG:
117+
Tier 1: Order=1, UpToSeconds=null, PayCode="GRUNDLOVSDAG"
118+
119+
PayDayTypeRules (time-of-day supplements):
120+
WEEKDAY:
121+
TimeBand 14400-21600 (04:00-06:00): PayCode="SHIFTED_MORNING"
122+
TimeBand 21600-64800 (06:00-18:00): PayCode="NORMAL"
123+
TimeBand 64800-72000 (18:00-20:00): PayCode="SHIFTED_EVENING"
124+
125+
SATURDAY:
126+
TimeBand 21600-43200 (06:00-12:00): PayCode="SAT_NORMAL"
127+
TimeBand 43200-64800 (12:00-18:00): PayCode="SAT_AFTERNOON"
128+
```
129+
130+
### Fixture 2: GlsA_Jordbrug_DyrePasning
131+
132+
Extends Standard with animal care time bands:
133+
134+
```
135+
PayRuleSet "GLS-A Jordbrug DyrePasning"
136+
WorkingTimeRuleSet:
137+
WeeklyNormalSeconds = 133200 (37h)
138+
DailyNormalSeconds = null (flexible for animal care)
139+
WeekStartsOn = Monday
140+
OvertimeBasis = Weekly (296h/8 weeks model)
141+
142+
PayDayRules: (same tier structure as Standard for overtime)
143+
WEEKDAY: same tiers as Standard
144+
SATURDAY: same tiers as Standard
145+
SUNDAY: same as Standard
146+
HOLIDAY: same as Standard
147+
GRUNDLOVSDAG: same as Standard
148+
149+
PayDayTypeRules (animal care time bands):
150+
WEEKDAY:
151+
TimeBand 0-18000 (00:00-05:00): PayCode="ANIMAL_NIGHT"
152+
TimeBand 18000-21600 (05:00-06:00): PayCode="SHIFTED_MORNING"
153+
TimeBand 21600-64800 (06:00-18:00): PayCode="NORMAL"
154+
TimeBand 64800-86400 (18:00-24:00): PayCode="SHIFTED_EVENING"
155+
156+
SATURDAY:
157+
TimeBand 0-43200 (00:00-12:00): PayCode="SAT_NORMAL"
158+
TimeBand 43200-86400 (12:00-24:00): PayCode="SAT_ANIMAL_AFTERNOON"
159+
160+
SUNDAY:
161+
TimeBand 0-86400 (00:00-24:00): PayCode="ANIMAL_SUN_HOLIDAY"
162+
163+
HOLIDAY:
164+
TimeBand 0-86400 (00:00-24:00): PayCode="ANIMAL_SUN_HOLIDAY"
165+
```
166+
167+
### Fixture 3: GlsA_Jordbrug_Laerling_Under18
168+
169+
```
170+
PayRuleSet "GLS-A Jordbrug Laerling U18"
171+
WorkingTimeRuleSet:
172+
WeeklyNormalSeconds = 144000 (40h)
173+
DailyNormalSeconds = 28800 (8h)
174+
WeekStartsOn = Monday
175+
OvertimeBasis = Daily
176+
177+
PayDayRules:
178+
WEEKDAY:
179+
Tier 1: Order=1, UpToSeconds=28800 (8h), PayCode="ELEV_NORMAL"
180+
Tier 2: Order=2, UpToSeconds=null, PayCode="ELEV_OVERTIME_50"
181+
182+
SATURDAY:
183+
Tier 1: Order=1, UpToSeconds=28800 (8h), PayCode="ELEV_SAT_WORK"
184+
Tier 2: Order=2, UpToSeconds=null, PayCode="ELEV_SAT_OVERTIME_50"
185+
(Saturday before/after noon split via PayDayTypeRules if needed)
186+
187+
SUNDAY:
188+
Tier 1: Order=1, UpToSeconds=7200 (2h), PayCode="ELEV_SUN_OT_50"
189+
Tier 2: Order=2, UpToSeconds=null, PayCode="ELEV_SUN_OT_80"
190+
191+
HOLIDAY:
192+
Tier 1: Order=1, UpToSeconds=7200 (2h), PayCode="ELEV_HOL_OT_50"
193+
Tier 2: Order=2, UpToSeconds=null, PayCode="ELEV_HOL_OT_80"
194+
```
195+
196+
### Fixture 4: GlsA_Jordbrug_Laerling_Over18
197+
198+
```
199+
PayRuleSet "GLS-A Jordbrug Laerling O18"
200+
WorkingTimeRuleSet:
201+
WeeklyNormalSeconds = 133200 (37h)
202+
DailyNormalSeconds = 26640 (7.4h)
203+
WeekStartsOn = Monday
204+
OvertimeBasis = DailyThenWeekly
205+
206+
PayDayRules:
207+
WEEKDAY:
208+
Tier 1: Order=1, UpToSeconds=26640 (7.4h), PayCode="ELEV_NORMAL"
209+
Tier 2: Order=2, UpToSeconds=33840 (7.4h+2h), PayCode="ELEV_OVERTIME_30"
210+
Tier 3: Order=3, UpToSeconds=null, PayCode="ELEV_OVERTIME_80"
211+
212+
SATURDAY:
213+
Tier 1: Order=1, UpToSeconds=null, PayCode="ELEV_SAT_WORK"
214+
(Saturday before/after noon split via PayDayTypeRules if needed)
215+
216+
SUNDAY:
217+
Tier 1: Order=1, UpToSeconds=7200 (2h), PayCode="ELEV_SUN_OT_50"
218+
Tier 2: Order=2, UpToSeconds=null, PayCode="ELEV_SUN_OT_80"
219+
220+
HOLIDAY:
221+
Tier 1: Order=1, UpToSeconds=7200 (2h), PayCode="ELEV_HOL_OT_50"
222+
Tier 2: Order=2, UpToSeconds=null, PayCode="ELEV_HOL_OT_80"
223+
```
224+
225+
### Fixture 5: GlsA_Jordbrug_Laerling_Under18_DyrePasning
226+
227+
Combines Under-18 tiers with animal care time bands.
228+
229+
```
230+
PayRuleSet "GLS-A Jordbrug Laerling U18 DyrePasning"
231+
WorkingTimeRuleSet:
232+
WeeklyNormalSeconds = 144000 (40h)
233+
DailyNormalSeconds = 28800 (8h)
234+
WeekStartsOn = Monday
235+
OvertimeBasis = Daily
236+
237+
PayDayRules: (same tier structure as Laerling_Under18)
238+
239+
PayDayTypeRules: (same time bands as DyrePasning but with ELEV_ pay codes)
240+
WEEKDAY:
241+
TimeBand 14400-18000 (04:00-05:00): PayCode="ELEV_ANIMAL_NIGHT"
242+
TimeBand 18000-21600 (05:00-06:00): PayCode="ELEV_SHIFTED_MORNING"
243+
TimeBand 21600-64800 (06:00-18:00): PayCode="ELEV_NORMAL"
244+
TimeBand 64800-72000 (18:00-20:00): PayCode="ELEV_SHIFTED_EVENING"
245+
Note: Under-18 cannot work 20:00-04:00 (agriculture exception allows 04:00 start)
246+
247+
SATURDAY:
248+
TimeBand 14400-43200 (04:00-12:00): PayCode="ELEV_SAT_NORMAL"
249+
TimeBand 43200-72000 (12:00-20:00): PayCode="ELEV_SAT_ANIMAL_AFTERNOON"
250+
```
251+
252+
## Backend Tests (C# NUnit)
253+
254+
### File: `eform-timeplanning-base/Microting.TimePlanningBase.Tests/GlsAJordbrugPayLineTests.cs`
255+
256+
Shared fixture helper class provides `CreateGlsAStandard()`, `CreateGlsADyrePasning()`, etc.
257+
258+
### Standard Agriculture Tests
259+
260+
| Test Name | DayCode | TotalSeconds | Expected PayLines |
261+
|-----------|---------|-------------|-------------------|
262+
| `Standard_Weekday_Normal_7h24m` | WEEKDAY | 26640 | NORMAL:26640 |
263+
| `Standard_Weekday_Overtime_2h` | WEEKDAY | 33840 | NORMAL:26640, OVERTIME_30:7200 |
264+
| `Standard_Weekday_Overtime_4h` | WEEKDAY | 41040 | NORMAL:26640, OVERTIME_30:7200, OVERTIME_80:7200 |
265+
| `Standard_Weekday_Short_4h` | WEEKDAY | 14400 | NORMAL:14400 |
266+
| `Standard_Saturday_BeforeNoon_6h` | SATURDAY | 06:00-12:00 (21600s) | SAT_NORMAL:21600 |
267+
| `Standard_Saturday_SpanNoon_10h` | SATURDAY | 06:00-16:00 (36000s) | SAT_NORMAL:21600, SAT_AFTERNOON:14400 |
268+
| `Standard_Sunday_8h` | SUNDAY | 28800 | SUN_HOLIDAY:28800 |
269+
| `Standard_Holiday_8h` | HOLIDAY | 28800 | SUN_HOLIDAY:28800 |
270+
| `Standard_Grundlovsdag_4h` | GRUNDLOVSDAG | 14400 | GRUNDLOVSDAG:14400 |
271+
272+
### Animal Care Tests
273+
274+
| Test Name | DayCode | Scenario | Expected |
275+
|-----------|---------|----------|----------|
276+
| `Animal_Weekday_Night_5h` | WEEKDAY | 00:00-05:00 | ANIMAL_NIGHT:18000 |
277+
| `Animal_Weekday_Night_And_Day` | WEEKDAY | 03:00-12:00 | ANIMAL_NIGHT:7200, SHIFTED_MORNING:3600, NORMAL:18000 |
278+
| `Animal_Saturday_Afternoon` | SATURDAY | 08:00-18:00 | SAT_NORMAL:14400, SAT_ANIMAL_AFTERNOON:21600 |
279+
| `Animal_Sunday_Full` | SUNDAY | 06:00-14:00 | ANIMAL_SUN_HOLIDAY:28800 |
280+
281+
### Apprentice Under-18 Tests
282+
283+
| Test Name | DayCode | TotalSeconds | Expected |
284+
|-----------|---------|-------------|----------|
285+
| `ElevU18_Weekday_Normal_8h` | WEEKDAY | 28800 | ELEV_NORMAL:28800 |
286+
| `ElevU18_Weekday_Over_10h` | WEEKDAY | 36000 | ELEV_NORMAL:28800, ELEV_OVERTIME_50:7200 |
287+
| `ElevU18_Sunday_2h` | SUNDAY | 7200 | ELEV_SUN_OT_50:7200 |
288+
| `ElevU18_Sunday_4h` | SUNDAY | 14400 | ELEV_SUN_OT_50:7200, ELEV_SUN_OT_80:7200 |
289+
| `ElevU18_Saturday_6h` | SATURDAY | 21600 | ELEV_SAT_NORMAL:21600 |
290+
291+
### Apprentice Over-18 Tests
292+
293+
| Test Name | DayCode | TotalSeconds | Expected |
294+
|-----------|---------|-------------|----------|
295+
| `ElevO18_Weekday_Normal` | WEEKDAY | 26640 | ELEV_NORMAL:26640 |
296+
| `ElevO18_Weekday_OT_2h` | WEEKDAY | 33840 | ELEV_NORMAL:26640, ELEV_OVERTIME_30:7200 |
297+
| `ElevO18_Sunday_2h` | SUNDAY | 7200 | ELEV_SUN_OT_50:7200 |
298+
| `ElevO18_Sunday_5h` | SUNDAY | 18000 | ELEV_SUN_OT_50:7200, ELEV_SUN_OT_80:10800 |
299+
300+
### Transition Test
301+
302+
| Test Name | Scenario | Expected |
303+
|-----------|----------|----------|
304+
| `Transition_Laerling_To_Standard` | Worker has Laerling U18 rule set Jan-Mar, Standard from Apr 1. Register 8h weekday in March, 8h weekday in April. | March: ELEV_NORMAL:28800. April: NORMAL:26640, OVERTIME_30:1360 |
305+
306+
## E2E Playwright Tests
307+
308+
### File: `eform-client/playwright/e2e/plugins/time-planning-pn/c/time-planning-glsa-3f-pay-rules.spec.ts`
309+
310+
### Scenario 1: Standard Agriculture Full Week Export
311+
312+
**Setup:**
313+
1. Navigate to Pay Rule Sets page
314+
2. Create "GLS-A Jordbrug Standard" with WEEKDAY (3 tiers), SATURDAY (2 tiers), SUNDAY (1 tier), HOLIDAY (1 tier)
315+
3. Assign rule set to test worker
316+
317+
**Test Data:**
318+
- Monday: 7.4h normal (06:00-13:24)
319+
- Tuesday: 9.4h with 2h overtime (06:00-15:24)
320+
- Wednesday: 7.4h normal
321+
- Thursday: 7.4h normal
322+
- Friday: 7.4h normal
323+
- Saturday: 4h before noon (08:00-12:00)
324+
325+
**Verification:**
326+
1. Export Excel for the test week
327+
2. Parse XLSX
328+
3. Assert columns: NORMAL, OVERTIME_30, SAT_NORMAL have correct hour totals
329+
330+
### Scenario 2: Animal Care Weekend
331+
332+
**Setup:**
333+
1. Create "GLS-A Jordbrug DyrePasning" with animal care time bands
334+
2. Assign to test worker
335+
336+
**Test Data:**
337+
- Wednesday night: 5h (00:00-05:00)
338+
- Saturday: 10h (06:00-16:00, spanning noon split)
339+
- Sunday: 8h (06:00-14:00)
340+
341+
**Verification:**
342+
1. Export Excel
343+
2. Assert: ANIMAL_NIGHT, SAT_NORMAL, SAT_ANIMAL_AFTERNOON, ANIMAL_SUN_HOLIDAY columns
344+
345+
### Scenario 3: Apprentice Transition
346+
347+
**Setup:**
348+
1. Create "GLS-A Laerling U18" rule set
349+
2. Create "GLS-A Jordbrug Standard" rule set
350+
3. Assign Laerling U18 from 2026-01-01 to 2026-03-31
351+
4. Assign Standard from 2026-04-01
352+
353+
**Test Data:**
354+
- March 15 (within apprentice period): 8h weekday
355+
- April 15 (within standard period): 8h weekday
356+
357+
**Verification:**
358+
1. Export for January-March: ELEV_NORMAL hours present
359+
2. Export for April onwards: NORMAL + OVERTIME_30 hours present (8h exceeds 7.4h norm)
360+
361+
## Verification Plan
362+
363+
### Backend Tests
364+
```bash
365+
cd eform-timeplanning-base
366+
dotnet test --filter "FullyQualifiedName~GlsAJordbrugPayLineTests" -v normal
367+
```
368+
369+
### E2E Tests
370+
```bash
371+
cd eform-angular-frontend/eform-client
372+
npx playwright test time-planning-glsa-3f-pay-rules.spec.ts
373+
```
374+
375+
### Manual Verification
376+
1. Start the application in dev mode
377+
2. Create a GLS-A/3F Standard rule set through the UI
378+
3. Register a week of work hours with various day types
379+
4. Export to Excel
380+
5. Open Excel and verify pay code columns match expected splits

0 commit comments

Comments
 (0)