Shift Management
Overview
Section titled “Overview”Shift Management provides a workspace-scoped CRUD API for managing work shifts used in field force scheduling. Shifts define time windows (start and end times) that can be assigned to visits, enabling principals to organize merchandising activities across different time periods.
Use Cases
Section titled “Use Cases”- Scheduling: Assign specific shifts (morning, afternoon, night) to outlet visits
- Reporting: Filter and analyze visit performance by shift periods
- Time Windows: Define operational hours for different outlet categories
- Workforce Planning: Track which shifts are covered by team members
Workspace Scoping
Section titled “Workspace Scoping”All shift operations are scoped to the authenticated user’s workspace. Cross-workspace access is prevented at the repository layer by mandatory workspace_id filters on all queries.
Data Model
Section titled “Data Model”WorkShift Model
Section titled “WorkShift Model”| Field | Type | Description | Constraints |
|---|---|---|---|
| id | integer | Primary key | Auto-increment |
| workspace_id | integer | FK to workspaces | NOT NULL, indexed |
| name | string | Shift name (e.g., “Shift Pagi”) | VARCHAR(100), NOT NULL |
| start_time | string | Start time in HH:MM format | VARCHAR(5), NOT NULL |
| end_time | string | End time in HH:MM format | VARCHAR(5), NOT NULL |
| is_active | boolean | Whether shift is currently active | DEFAULT TRUE |
| created_at | timestamp | Creation timestamp | TIMESTAMPTZ |
| updated_at | timestamp | Last update timestamp | TIMESTAMPTZ |
| deleted_at | timestamp | Soft delete timestamp | TIMESTAMPTZ (nullable) |
API Endpoints
Section titled “API Endpoints”Endpoints
Section titled “Endpoints”| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /office/v1/shifts | shift.view or shift.manage | List shifts (paginated) |
| GET | /office/v1/shifts/:id | shift.view or shift.manage | Get shift by ID |
| POST | /office/v1/shifts | shift.manage (full access) | Create new shift |
| PUT | /office/v1/shifts/:id | shift.manage (full access) | Update shift |
| DELETE | /office/v1/shifts/:id | shift.manage (full access) | Delete shift (soft) |
App Routes (Mobile)
Section titled “App Routes (Mobile)”| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /app/v1/shifts | Valid auth token | List shifts (read-only for mobile app) |
Mobile routes provide read-only access for the field force app to retrieve available shifts for visit check-in and reporting.
Request/Response Examples
Section titled “Request/Response Examples”List Shifts
Section titled “List Shifts”Retrieve paginated list of shifts with optional keyword filtering.
GET /office/v1/shifts?page=1&limit=50&keyword=PagiAuthorization: Bearer <token>{ "data": { "data": [ { "id": 1, "name": "Shift Pagi", "start_time": "07:00", "end_time": "15:00", "is_active": true, "workspace_id": 1, "created_at": "2026-03-04T00:00:00Z", "updated_at": "2026-03-04T00:00:00Z" } ], "pagination": { "page": 1, "limit": 50, "total": 3, "total_pages": 1 } }, "message": "shifts retrieved", "code": "SHIFT_LIST"}Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| page | integer | No | 1 | Page number |
| limit | integer | No | 50 | Items per page (max 100) |
| keyword | string | No | - | Filter by shift name (case-insensitive) |
Get Shift by ID
Section titled “Get Shift by ID”Retrieve a single shift by its ID.
GET /office/v1/shifts/1Authorization: Bearer <token>{ "data": { "id": 1, "name": "Shift Pagi", "start_time": "07:00", "end_time": "15:00", "is_active": true, "workspace_id": 1, "created_at": "2026-03-04T00:00:00Z", "updated_at": "2026-03-04T00:00:00Z" }, "message": "shift retrieved", "code": "SHIFT_DETAIL"}Create Shift
Section titled “Create Shift”Create a new work shift.
POST /office/v1/shiftsAuthorization: Bearer <token>Content-Type: application/json
{ "name": "Weekend Shift", "start_time": "09:00", "end_time": "17:00", "is_active": true}{ "data": { "id": 4, "name": "Weekend Shift", "start_time": "09:00", "end_time": "17:00", "is_active": true, "workspace_id": 1, "created_at": "2026-03-05T00:00:00Z", "updated_at": "2026-03-05T00:00:00Z" }, "message": "shift created", "code": "SHIFT_CREATED"}Request Body Fields:
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Shift name (max 100 characters) |
| start_time | string | Yes | Start time in HH:MM format (24-hour) |
| end_time | string | Yes | End time in HH:MM format (24-hour) |
| is_active | boolean | No | Active status (defaults to true) |
Update Shift
Section titled “Update Shift”Update an existing shift.
PUT /office/v1/shifts/4Authorization: Bearer <token>Content-Type: application/json
{ "name": "Weekend Shift", "start_time": "10:00", "end_time": "18:00", "is_active": false}{ "message": "shift updated", "code": "SHIFT_UPDATED"}Delete Shift
Section titled “Delete Shift”Soft delete a shift (marks deleted_at timestamp).
DELETE /office/v1/shifts/4Authorization: Bearer <token>{ "message": "shift deleted", "code": "SHIFT_DELETED"}Default Seeder
Section titled “Default Seeder”The seeder 010_work_shifts.go creates three default shifts for the root workspace (workspace ID 1).
Default Shifts
Section titled “Default Shifts”| Name | Start Time | End Time | Description |
|---|---|---|---|
| Shift Pagi | 07:00 | 15:00 | Morning shift (8 hours) |
| Shift Siang | 15:00 | 23:00 | Afternoon shift (8 hours) |
| Shift Malam | 23:00 | 07:00 | Night shift (8 hours, crosses midnight) |
Running the Seeder
Section titled “Running the Seeder”To run the seeder on a fresh or existing installation:
go run ./cmd/seederv2/... --run 010_work_shiftsThe seeder is idempotent — it uses check-before-insert pattern and will not create duplicate shifts if they already exist.
Permission Seeding
Section titled “Permission Seeding”The seeder also creates and assigns the shift.manage permission:
- Creates
shift.managepermission with keyPermissionKeyShiftManage - Assigns full access to roles:
- Super Admin
- Admin
- Manager
Workspace Scoping
Section titled “Workspace Scoping”All shift queries are automatically scoped to the authenticated user’s workspace:
- Extraction:
workspace_idis extracted from the JWT auth context - Filtering: All repository queries include
WHERE workspace_id = ?clause - Isolation: Users cannot access shifts from other workspaces
- Assignment: New shifts automatically receive the workspace ID from context
This ensures multi-tenant data isolation and prevents unauthorized cross-workspace access.
RBAC Permissions
Section titled “RBAC Permissions”Shift Management uses two permission levels for access control.
Permission Keys
Section titled “Permission Keys”| Permission Key | Access Level | Operations |
|---|---|---|
shift.manage | Full | Create, Read, Update, Delete |
shift.view | Read-only | Read (List + Detail) |
Permission Matrix
Section titled “Permission Matrix”| Operation | Endpoint | Required Permission | Access Level |
|---|---|---|---|
| List | GET /office/v1/shifts | shift.view or shift.manage | View or Full |
| Detail | GET /office/v1/shifts/:id | shift.view or shift.manage | View or Full |
| Create | POST /office/v1/shifts | shift.manage | Full only |
| Update | PUT /office/v1/shifts/:id | shift.manage | Full only |
| Delete | DELETE /office/v1/shifts/:id | shift.manage | Full only |
Mobile App Access
Section titled “Mobile App Access”The mobile app (/app/v1/shifts) provides read-only access to all authenticated users. This allows field force members to:
- View available shifts during visit check-in
- See shift assignments on visit details
- Filter reports by shift periods
No RBAC checks are performed on app routes — authentication is sufficient.
Error Responses
Section titled “Error Responses”Common Errors
Section titled “Common Errors”400 Bad Request — Invalid ID:
{ "error": "invalid shift ID", "code": "BAD_REQUEST"}400 Bad Request — Validation Failed:
{ "error": "validation failed", "data": { "name": "Name is required", "start_time": "Start time is required" }, "code": "VALIDATION_ERROR"}404 Not Found:
{ "error": "shift not found", "code": "NOT_FOUND"}403 Forbidden — Insufficient Permission:
{ "error": "insufficient permission", "code": "FORBIDDEN"}Related Topics
Section titled “Related Topics”- Visits API — Shift assignment on visit creation
- Visit Management User Guide — How to assign shifts to visits
- Workspace Scoping — Multi-tenant data isolation
- RBAC System — Permission enforcement