Skip to content

Commit 36cf81e

Browse files
authored
Merge pull request #170 from fleetbase/dev-v1.6.25
release: v1.6.26 + fixed api multi-column sorting
2 parents 075d333 + 069c0b5 commit 36cf81e

36 files changed

+2660
-38
lines changed

SCHEDULING_MODULE.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Core Scheduling Module
2+
3+
This module provides a polymorphic, reusable scheduling system for the Fleetbase platform.
4+
5+
## Features
6+
7+
- **Polymorphic Architecture**: Schedule any entity type (drivers, vehicles, stores, warehouses, etc.)
8+
- **Flexible Schedule Items**: Assign items to any assignee with any resource
9+
- **Availability Management**: Track availability windows for any entity
10+
- **Constraint System**: Pluggable constraint validation framework
11+
- **Event-Driven**: Comprehensive event system for extensibility
12+
- **Activity Logging**: All scheduling activities logged via Spatie Activity Log
13+
14+
## Database Tables
15+
16+
1. **schedules** - Master schedule records
17+
2. **schedule_items** - Individual scheduled items/slots
18+
3. **schedule_templates** - Reusable schedule patterns
19+
4. **schedule_availability** - Availability tracking
20+
5. **schedule_constraints** - Configurable scheduling rules
21+
22+
## Models
23+
24+
- `Schedule` - Main schedule model with polymorphic subject
25+
- `ScheduleItem` - Schedule item with polymorphic assignee and resource
26+
- `ScheduleTemplate` - Reusable template patterns
27+
- `ScheduleAvailability` - Availability windows
28+
- `ScheduleConstraint` - Constraint definitions
29+
30+
## Services
31+
32+
- `ScheduleService` - Core scheduling operations
33+
- `AvailabilityService` - Availability management
34+
- `ConstraintService` - Pluggable constraint validation
35+
36+
## Events
37+
38+
- `ScheduleCreated`
39+
- `ScheduleUpdated`
40+
- `ScheduleDeleted`
41+
- `ScheduleItemCreated`
42+
- `ScheduleItemUpdated`
43+
- `ScheduleItemDeleted`
44+
- `ScheduleItemAssigned`
45+
- `ScheduleConstraintViolated`
46+
47+
## API Endpoints
48+
49+
All endpoints are available under `/int/v1/` prefix:
50+
51+
- `/schedules` - Schedule CRUD operations
52+
- `/schedule-items` - Schedule item CRUD operations
53+
- `/schedule-templates` - Template CRUD operations
54+
- `/schedule-availability` - Availability CRUD operations
55+
- `/schedule-constraints` - Constraint CRUD operations
56+
57+
## Extension Integration
58+
59+
Extensions can integrate with the scheduling module by:
60+
61+
1. **Registering Constraints**: Use `ConstraintService::register()` to add domain-specific constraints
62+
2. **Listening to Events**: Subscribe to scheduling events to trigger extension-specific workflows
63+
3. **Using the Meta Field**: Store extension-specific data in the `meta` JSON field
64+
65+
### Example: FleetOps HOS Constraint
66+
67+
```php
68+
// In FleetOps ServiceProvider
69+
public function boot()
70+
{
71+
$constraintService = app(\Fleetbase\Services\Scheduling\ConstraintService::class);
72+
$constraintService->register('driver', \Fleetbase\FleetOps\Constraints\HOSConstraint::class);
73+
}
74+
```
75+
76+
## Usage Examples
77+
78+
### Creating a Schedule
79+
80+
```php
81+
$schedule = Schedule::create([
82+
'company_uuid' => $company->uuid,
83+
'subject_type' => 'fleet',
84+
'subject_uuid' => $fleet->uuid,
85+
'name' => 'Weekly Driver Schedule',
86+
'start_date' => '2025-11-15',
87+
'end_date' => '2025-11-22',
88+
'timezone' => 'America/New_York',
89+
'status' => 'active',
90+
]);
91+
```
92+
93+
### Creating a Schedule Item
94+
95+
```php
96+
$item = ScheduleItem::create([
97+
'schedule_uuid' => $schedule->uuid,
98+
'assignee_type' => 'driver',
99+
'assignee_uuid' => $driver->uuid,
100+
'resource_type' => 'vehicle',
101+
'resource_uuid' => $vehicle->uuid,
102+
'start_at' => '2025-11-15 08:00:00',
103+
'end_at' => '2025-11-15 17:00:00',
104+
'status' => 'confirmed',
105+
]);
106+
```
107+
108+
### Setting Availability
109+
110+
```php
111+
$availability = ScheduleAvailability::create([
112+
'subject_type' => 'driver',
113+
'subject_uuid' => $driver->uuid,
114+
'start_at' => '2025-11-20 00:00:00',
115+
'end_at' => '2025-11-22 23:59:59',
116+
'is_available' => false,
117+
'reason' => 'vacation',
118+
]);
119+
```
120+
121+
## Future Enhancements
122+
123+
- Optimization algorithms for automatic schedule generation
124+
- RRULE processing for recurring patterns
125+
- Conflict detection and resolution
126+
- Capacity planning and load balancing
127+
- Multi-timezone support improvements

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fleetbase/core-api",
3-
"version": "1.6.24",
3+
"version": "1.6.25",
44
"description": "Core Framework and Resources for Fleetbase API",
55
"keywords": [
66
"fleetbase",
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('schedules', function (Blueprint $table) {
16+
$table->increments('id');
17+
$table->string('_key')->nullable();
18+
$table->string('uuid', 191)->nullable()->index();
19+
$table->string('public_id', 191)->nullable()->unique()->index();
20+
$table->string('company_uuid', 191)->nullable()->index();
21+
$table->string('subject_uuid', 191)->nullable()->index();
22+
$table->string('subject_type')->nullable()->index();
23+
$table->string('name')->nullable();
24+
$table->text('description')->nullable();
25+
$table->date('start_date')->nullable();
26+
$table->date('end_date')->nullable();
27+
$table->string('timezone', 50)->default('UTC');
28+
$table->enum('status', ['draft', 'published', 'active', 'paused', 'archived'])->default('draft')->index();
29+
$table->json('meta')->nullable();
30+
$table->softDeletes();
31+
$table->timestamp('created_at')->nullable()->index();
32+
$table->timestamp('updated_at')->nullable();
33+
34+
$table->unique(['uuid']);
35+
$table->index(['subject_uuid', 'subject_type', 'status']);
36+
$table->index(['company_uuid', 'status', 'start_date', 'end_date']);
37+
});
38+
}
39+
40+
/**
41+
* Reverse the migrations.
42+
*
43+
* @return void
44+
*/
45+
public function down()
46+
{
47+
Schema::dropIfExists('schedules');
48+
}
49+
};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('schedule_items', function (Blueprint $table) {
16+
$table->increments('id');
17+
$table->string('_key')->nullable();
18+
$table->string('uuid', 191)->nullable()->index();
19+
$table->string('public_id', 191)->nullable()->unique()->index();
20+
$table->string('schedule_uuid', 191)->nullable()->index();
21+
$table->string('assignee_uuid', 191)->nullable()->index();
22+
$table->string('assignee_type')->nullable()->index();
23+
$table->string('resource_uuid', 191)->nullable()->index();
24+
$table->string('resource_type')->nullable()->index();
25+
$table->timestamp('start_at')->nullable()->index();
26+
$table->timestamp('end_at')->nullable()->index();
27+
$table->integer('duration')->nullable()->comment('Duration in minutes');
28+
$table->timestamp('break_start_at')->nullable();
29+
$table->timestamp('break_end_at')->nullable();
30+
$table->enum('status', ['pending', 'confirmed', 'in_progress', 'completed', 'cancelled', 'no_show'])->default('pending')->index();
31+
$table->json('meta')->nullable();
32+
$table->softDeletes();
33+
$table->timestamp('created_at')->nullable()->index();
34+
$table->timestamp('updated_at')->nullable();
35+
36+
$table->unique(['uuid']);
37+
$table->index(['schedule_uuid', 'start_at', 'end_at']);
38+
$table->index(['assignee_uuid', 'assignee_type', 'status']);
39+
$table->index(['resource_uuid', 'resource_type', 'start_at', 'end_at']);
40+
$table->index(['status', 'start_at']);
41+
});
42+
}
43+
44+
/**
45+
* Reverse the migrations.
46+
*
47+
* @return void
48+
*/
49+
public function down()
50+
{
51+
Schema::dropIfExists('schedule_items');
52+
}
53+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('schedule_templates', function (Blueprint $table) {
16+
$table->increments('id');
17+
$table->string('_key')->nullable();
18+
$table->string('uuid', 191)->nullable()->index();
19+
$table->string('public_id', 191)->nullable()->unique()->index();
20+
$table->string('company_uuid', 191)->nullable()->index();
21+
$table->string('subject_uuid', 191)->nullable()->index();
22+
$table->string('subject_type')->nullable()->index();
23+
$table->string('name')->nullable();
24+
$table->text('description')->nullable();
25+
$table->time('start_time')->nullable();
26+
$table->time('end_time')->nullable();
27+
$table->integer('duration')->nullable()->comment('Duration in minutes');
28+
$table->integer('break_duration')->nullable()->comment('Break duration in minutes');
29+
$table->text('rrule')->nullable()->comment('RFC 5545 recurrence rule');
30+
$table->json('meta')->nullable();
31+
$table->softDeletes();
32+
$table->timestamp('created_at')->nullable()->index();
33+
$table->timestamp('updated_at')->nullable();
34+
35+
$table->unique(['uuid']);
36+
$table->index(['subject_uuid', 'subject_type']);
37+
});
38+
}
39+
40+
/**
41+
* Reverse the migrations.
42+
*
43+
* @return void
44+
*/
45+
public function down()
46+
{
47+
Schema::dropIfExists('schedule_templates');
48+
}
49+
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('schedule_availability', function (Blueprint $table) {
16+
$table->increments('id');
17+
$table->string('_key')->nullable();
18+
$table->string('uuid', 191)->nullable()->index();
19+
$table->string('subject_uuid', 191)->nullable()->index();
20+
$table->string('subject_type')->nullable()->index();
21+
$table->timestamp('start_at')->nullable()->index();
22+
$table->timestamp('end_at')->nullable()->index();
23+
$table->boolean('is_available')->default(true)->index();
24+
$table->tinyInteger('preference_level')->nullable()->comment('1-5 preference strength');
25+
$table->text('rrule')->nullable()->comment('RFC 5545 recurrence rule');
26+
$table->string('reason')->nullable();
27+
$table->text('notes')->nullable();
28+
$table->json('meta')->nullable();
29+
$table->softDeletes();
30+
$table->timestamp('created_at')->nullable()->index();
31+
$table->timestamp('updated_at')->nullable();
32+
33+
$table->unique(['uuid']);
34+
$table->index(['subject_uuid', 'subject_type', 'start_at', 'end_at', 'is_available'], 'schedule_subject_availability_composite_idx');
35+
$table->index(['subject_type', 'is_available', 'start_at', 'end_at'], 'schedule_availability_composite_idx');
36+
});
37+
}
38+
39+
/**
40+
* Reverse the migrations.
41+
*
42+
* @return void
43+
*/
44+
public function down()
45+
{
46+
Schema::dropIfExists('schedule_availability');
47+
}
48+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('schedule_constraints', function (Blueprint $table) {
16+
$table->increments('id');
17+
$table->string('_key')->nullable();
18+
$table->string('uuid', 191)->nullable()->index();
19+
$table->string('company_uuid', 191)->nullable()->index();
20+
$table->string('subject_uuid', 191)->nullable()->index();
21+
$table->string('subject_type')->nullable()->index();
22+
$table->string('name')->nullable();
23+
$table->text('description')->nullable();
24+
$table->string('type', 50)->nullable()->index()->comment('e.g., hos, labor, business, capacity');
25+
$table->string('category', 50)->nullable()->index()->comment('e.g., compliance, optimization');
26+
$table->string('constraint_key', 100)->nullable()->index();
27+
$table->text('constraint_value')->nullable();
28+
$table->string('jurisdiction', 50)->nullable()->comment('e.g., US-Federal, US-CA, EU');
29+
$table->integer('priority')->default(0)->comment('Higher = more important');
30+
$table->boolean('is_active')->default(true)->index();
31+
$table->json('meta')->nullable();
32+
$table->softDeletes();
33+
$table->timestamp('created_at')->nullable()->index();
34+
$table->timestamp('updated_at')->nullable();
35+
36+
$table->unique(['uuid']);
37+
$table->index(['type', 'category', 'is_active']);
38+
$table->index(['company_uuid', 'is_active']);
39+
$table->index(['subject_uuid', 'subject_type', 'is_active']);
40+
});
41+
}
42+
43+
/**
44+
* Reverse the migrations.
45+
*
46+
* @return void
47+
*/
48+
public function down()
49+
{
50+
Schema::dropIfExists('schedule_constraints');
51+
}
52+
};

0 commit comments

Comments
 (0)