Calendar scheduling
The Calendar Scheduling extension provides a unified calendar view of all maintenance activities — inspections, interventions, and work orders — with per-user filtering and real-time assignment notifications.
Table of Contents¶
- Overview
- Extension Endpoints
- Per-User Filtering
- Assignment Notifications
- WebSocket Events
- Data Formats
Overview¶
The calendar system is implemented as a built-in extension (CalendarSchedulingExtension) that aggregates data from three entity types:
| Entity | Date Field | User Field |
|---|---|---|
| Inspections | plannedDate |
users (ManyToMany) |
| Interventions | scheduleDate |
users (ManyToMany) |
| Work Orders | scheduledStartDate |
assignedTo (ManyToOne) + technicians (ManyToMany) |
Architecture¶
Mobile/Web Client
│
▼
Extension Endpoints (/ext/calendar-scheduling/*)
│
├── resolveUserId(ctx, params) ← Determines filtering scope
│
├── InspectionRepository ← Per-user or all inspections
├── InterventionRepository ← Per-user or all interventions
└── WorkOrderRepository ← Per-user or all work orders
Extension Endpoints¶
All endpoints are available at /ext/calendar-scheduling/ and require authentication.
GET /ext/calendar-scheduling/events¶
Returns all calendar events (inspections, interventions, work orders) within a date range.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
start |
ISO datetime | Yes | Range start (e.g., 2026-01-01T00:00:00) |
end |
ISO datetime | Yes | Range end (e.g., 2026-01-31T23:59:59) |
userId |
Long or "all" |
No | Filter by user. Defaults to current user. Use all for admin view. |
Response:
{
"events": [
{
"id": 1,
"type": "inspection",
"title": "Monthly turbine inspection",
"start": "2026-01-15T09:00:00",
"end": "2026-01-15T12:00:00",
"status": "PLANNED",
"priority": "HIGH",
"assignees": ["John Doe", "Jane Smith"],
"assigneeIds": [42, 55],
"unitName": "Turbine A1",
"color": "#4CAF50"
}
]
}
GET /ext/calendar-scheduling/inspections¶
Returns only inspections within a date range.
GET /ext/calendar-scheduling/interventions¶
Returns only interventions within a date range.
GET /ext/calendar-scheduling/work-orders¶
Returns only work orders within a date range.
GET /ext/calendar-scheduling/upcoming¶
Returns upcoming events within the next N days (default: 7).
| Parameter | Type | Required | Description |
|---|---|---|---|
days |
Integer | No | Number of days ahead (default: 7) |
userId |
Long or "all" |
No | Filter by user |
GET /ext/calendar-scheduling/overdue¶
Returns overdue work orders (past scheduledEndDate, not completed/closed/cancelled).
GET /ext/calendar-scheduling/summary¶
Returns event counts grouped by type and status for a date range.
GET /ext/calendar-scheduling/settings¶
Returns calendar display settings (colors, default views).
Per-User Filtering¶
By default, all data endpoints filter events to the authenticated user only. This ensures field technicians see only their own schedule.
How It Works¶
The resolveUserId() helper determines the filtering scope:
- No
userIdparam → Usesctx.getUserId()(current authenticated user) userId=all→ No filtering, returns all events (for admin/manager views)userId=42→ Filters to specific user ID (for managers viewing a technician's schedule)
User Matching Rules¶
| Entity | User is matched if... |
|---|---|
| Inspection | User is in inspection.users set |
| Intervention | User is in intervention.users set |
| Work Order | User is workOrder.assignedTo OR user is in workOrder.technicians set |
Repository Queries¶
Efficient JPQL queries with JOINs are used to filter at the database level (not in-memory):
// Inspections
SELECT i FROM Inspection i JOIN i.users u
WHERE u.id = :userId AND i.plannedDate BETWEEN :start AND :end
// Interventions
SELECT i FROM Intervention i JOIN i.users u
WHERE u.id = :userId AND i.scheduleDate BETWEEN :start AND :end
// Work Orders (checks both assignedTo and technicians)
SELECT DISTINCT wo FROM WorkOrder wo LEFT JOIN wo.technicians t
WHERE (wo.assignedTo.id = :userId OR t.id = :userId)
AND wo.status IN :statuses AND wo.scheduledStartDate BETWEEN :start AND :end
Assignment Notifications¶
When technicians are assigned to work orders, interventions, or inspections, the system automatically sends in-app notifications and WebSocket calendar refresh events.
Trigger Points¶
| Action | What Happens |
|---|---|
| Create inspection/intervention/work order | All assigned users are notified |
| Update inspection/intervention/work order | Only newly added users are notified |
Notification Flow¶
Service.create() / Service.update()
│
├── Compute new assignees (diff old vs new)
│
└── AssignmentNotificationService (async)
│
├── NotificationService.createNotification()
│ → Persists in-app notification
│ → Pushes via WebSocket /user/queue/notifications
│
└── WebSocketService.sendCalendarUpdate()
→ Pushes via WebSocket /user/queue/calendar
New Assignee Detection¶
For create operations, all users in the entity are considered "new."
For update operations, the service:
1. Loads the old entity within the current transaction
2. Force-initializes lazy collections with Hibernate.initialize()
3. Compares old user set vs new user set
4. Only users in newSet - oldSet receive notifications
Notification Content¶
In-App Notification (persisted to DB):
{
"title": "New Work Order Assignment",
"message": "You have been assigned to work order WO-1706745600",
"type": "ASSIGNMENT",
"referenceType": "WORK_ORDER",
"referenceId": 42
}
WebSocket Calendar Update (ephemeral):
{
"action": "ASSIGNMENT",
"entityType": "WORK_ORDER",
"entityId": 42,
"timestamp": "2026-02-11T14:30:00"
}
WebSocket Events¶
Calendar Updates Channel¶
Destination: /user/queue/calendar
Sent when a user is newly assigned to a maintenance entity. The client should refresh its calendar view upon receiving this event.
| Field | Type | Description |
|---|---|---|
action |
String | Always "ASSIGNMENT" |
entityType |
String | "WORK_ORDER", "INTERVENTION", or "INSPECTION" |
entityId |
Long | ID of the entity |
timestamp |
String | ISO datetime of the event |
Notifications Channel¶
Destination: /user/queue/notifications
Standard notification channel. Assignment notifications are pushed here alongside other notification types.
Data Formats¶
Event Colors¶
| Entity Type | Default Color |
|---|---|
| Inspection | #4CAF50 (green) |
| Intervention | #2196F3 (blue) |
| Work Order | Varies by priority |
Work Order Priority Colors¶
| Priority | Color |
|---|---|
| CRITICAL | #F44336 (red) |
| HIGH | #FF9800 (orange) |
| MEDIUM | #FFC107 (yellow) |
| LOW | #8BC34A (light green) |
Event Map Fields¶
All calendar events share these common fields:
| Field | Type | Description |
|---|---|---|
id |
Long | Entity ID |
type |
String | "inspection", "intervention", "work_order" |
title |
String | Display title |
start |
String | ISO datetime start |
end |
String | ISO datetime end (may be null) |
status |
String | Entity status |
priority |
String | Priority level (work orders) |
assignees |
List\<String> | Display names of assigned users |
assigneeIds |
List\<Long> | IDs of assigned users |
unitName |
String | Associated unit/asset name |
color |
String | Hex color for display |
Implementation Details¶
Key Classes¶
| Class | Purpose |
|---|---|
CalendarSchedulingExtension |
Extension with all /ext/calendar-scheduling/* endpoints |
AssignmentNotificationService |
Detects assignment changes and triggers notifications |
WorkOrderService |
Overrides create/update for WO notification triggers |
InterventionService |
Overrides create/update for intervention notification triggers |
InspectionService |
Overrides create/update for inspection notification triggers |
WebSocketService |
sendCalendarUpdate() for calendar refresh events |
NotificationService |
createNotification() for persisted in-app notifications |
Async Processing¶
Assignment notifications run asynchronously on the notificationExecutor thread pool to avoid blocking the main request thread. This means:
- The HTTP response returns immediately after save
- Notifications are delivered in the background
- If notification delivery fails, the entity save is not rolled back