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

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:

  1. No userId param → Uses ctx.getUserId() (current authenticated user)
  2. userId=all → No filtering, returns all events (for admin/manager views)
  3. 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