Add personio-headcount skill (migrated from plugin)

This commit is contained in:
2026-03-05 13:57:01 +00:00
parent 30e3feae68
commit 4208187250
@@ -0,0 +1,134 @@
# Data Schemas
## Snapshot Structure
```json
{
"last_refreshed": "2026-03-04T19:00:00Z",
"total_records": 755,
"summary": {
"ACTIVE": 177,
"INACTIVE": 574,
"ONBOARDING": 1,
"LEAVE": 3
},
"employees": [
{
"personio_id": "35653993",
"status": "ONBOARDING",
"team_id": "417186",
"department_id": "1291054",
"office_id": "501083",
"start_date": "2026-05-01",
"end_date": null
}
]
}
```
## Team Mapping Structure
Maps Personio team IDs to human-readable names:
```json
{
"total_teams": 65,
"teams": [
{
"team_id": "4400959",
"team_name": "AI & Automation",
"active_headcount": 6,
"total_records": 7
}
]
}
```
## Department Mapping Structure
Groups teams under departments with real names from `department-mapping.csv`:
```json
{
"total_departments": 19,
"departments": [
{
"department_id": "1291054",
"department_name": "Finance & Business Navigation",
"active_headcount": 11,
"total_records": 45,
"team_ids": ["417186", "417184", "3442693", "3443053", "572110"],
"teams": ["Data", "Finance & Administration", "M&A", "Pricing", "Rev Ops"]
}
]
}
```
## Office Mapping Structure
```json
{
"total_offices": 8,
"offices": [
{
"office_id": "501083",
"active_headcount": 120,
"total_records": 500
}
]
}
```
## MCP Tool Reference
**Tool:** `Call_Personio_Get_Employees_` (full ID: `mcp__n8n-personio__Call_Personio_Get_Employees_`)
| Parameter | Type | Default | Description |
|-----------|--------|---------|--------------------------------------|
| limit | number | 50 | Records per page (use 50 for balance)|
| cursor | string | "" | Pagination cursor from previous page |
Pass parameters as a JSON string in the `input` field:
- First page: `{"limit": 50}`
- Subsequent pages: `{"limit": 50, "cursor": "<nextCursor>"}`
## Employee Record Fields (from API)
Each employee record from the MCP tool contains:
- `id` → store as `personio_id`
- `status` → ACTIVE, INACTIVE, ONBOARDING, or LEAVE
- `team_id` → Personio team ID (look up name in team_mapping.json)
- `department_id` → Personio department ID
- `office_id` → Personio office ID
- `start_date` → employment start
- `end_date` → contract end date (null if ongoing)
- `first_name`, `last_name`, `email`**NEVER STORE THESE** — strip before saving
## Incremental Refresh (Delta Update)
1. Read existing snapshot
2. Fetch all pages from Personio (no "modified since" filter available)
3. Compare each employee by `personio_id`:
- ID exists → update the record (status, team_id, etc. may have changed)
- ID is new → add it
- ID in snapshot but not in fresh data → keep (historical)
4. Recalculate summary counts
5. Update `last_refreshed` timestamp
6. Rebuild team_mapping.json counts
7. Save all files
## PDF Report Contents
The PDF report has 6 pages:
1. **Cover page** — KPI cards (active, onboarding, on leave, inactive)
2. **Headcount by Team** — all active teams ranked by headcount with percentages
3. **Headcount by Department** — departments with team listings
4. **New Joiners** — monthly breakdown since Jan 2025, with team attribution
5. **Departures** — monthly breakdown since Jan 2025, with team attribution
6. **Net Workforce Movement** — joiners vs leavers with cumulative net change
### XML Escaping
The report uses `xml_escape()` for special characters in names (e.g., "M&A" → "M&amp;A").
This is required because reportlab's Paragraph class uses an XML parser internally.