135 lines
3.6 KiB
Markdown
135 lines
3.6 KiB
Markdown
# 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&A").
|
|
This is required because reportlab's Paragraph class uses an XML parser internally.
|