@fleetbase/fleetops-engine 0.6.50 → 0.6.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -67
- package/addon/components/contact/panel-header.hbs +11 -48
- package/addon/components/customer/details.hbs +1 -2
- package/addon/components/customer/form.hbs +63 -51
- package/addon/components/customer/form.js +27 -1
- package/addon/components/customer/orders.hbs +48 -0
- package/addon/components/customer/orders.js +67 -5
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/home/getting-started-guidance.hbs +113 -0
- package/addon/components/home/getting-started-guidance.js +107 -0
- package/addon/components/issue/details/comments.hbs +3 -0
- package/addon/components/issue/details/comments.js +3 -0
- package/addon/components/issue/details/correspondence.hbs +9 -0
- package/addon/components/issue/details/correspondence.js +83 -0
- package/addon/components/issue/details/documents.hbs +69 -0
- package/addon/components/issue/details/documents.js +85 -0
- package/addon/components/issue/details.hbs +220 -59
- package/addon/components/issue/details.js +245 -0
- package/addon/components/issue/form.hbs +19 -1
- package/addon/components/issue/panel-header.hbs +43 -0
- package/addon/components/issue/panel-header.js +22 -0
- package/addon/components/issue/timeline.hbs +40 -0
- package/addon/components/issue/timeline.js +45 -0
- package/addon/components/leaflet-tracking-marker.js +3 -0
- package/addon/components/map/drawer/position-listing.js +3 -0
- package/addon/components/map/leaflet-live-map.hbs +2 -2
- package/addon/components/map/leaflet-live-map.js +17 -0
- package/addon/components/map/order-list-overlay.hbs +4 -4
- package/addon/components/modals/convert-customer-to-vendor.hbs +30 -0
- package/addon/components/modals/convert-customer-to-vendor.js +55 -0
- package/addon/components/modals/issue-assign.hbs +20 -0
- package/addon/components/modals/issue-assign.js +49 -0
- package/addon/components/modals/issue-close.hbs +30 -0
- package/addon/components/modals/issue-close.js +73 -0
- package/addon/components/modals/issue-status.hbs +20 -0
- package/addon/components/modals/issue-status.js +43 -0
- package/addon/components/modals/reset-customer-credentials.js +12 -6
- package/addon/components/modals/update-order-activity.hbs +51 -17
- package/addon/components/modals/update-order-activity.js +59 -5
- package/addon/components/order/details/activity.js +2 -2
- package/addon/components/order/details/proof.hbs +40 -38
- package/addon/components/order/details/proof.js +31 -4
- package/addon/components/order/details.hbs +5 -5
- package/addon/components/order/details.js +19 -1
- package/addon/components/order/header.hbs +1 -1
- package/addon/components/route-list.hbs +0 -12
- package/addon/components/service-rate/details.hbs +1 -1
- package/addon/components/service-rate/form.hbs +1 -1
- package/addon/components/service-rate/form.js +4 -0
- package/addon/components/vendor/personnel-manager.hbs +166 -0
- package/addon/components/vendor/personnel-manager.js +143 -0
- package/addon/components/widget/live-fleet.hbs +1 -0
- package/addon/components/widget/live-fleet.js +2 -7
- package/addon/components/widget/revenue-trend.hbs +1 -1
- package/addon/components/widget/revenue-trend.js +75 -2
- package/addon/controllers/management/contacts/customers/details.js +31 -13
- package/addon/controllers/management/contacts/customers.js +16 -5
- package/addon/controllers/management/contacts/index/details.js +31 -13
- package/addon/controllers/management/contacts/index.js +7 -0
- package/addon/controllers/management/issues/index/details.js +52 -6
- package/addon/controllers/management/vendors/index/details.js +4 -0
- package/addon/controllers/operations/orders/index/details.js +11 -3
- package/addon/controllers/operations/orders/index.js +1 -1
- package/addon/extension.js +19 -12
- package/addon/helpers/format-duration.js +1 -38
- package/addon/initializers/load-leaflet-assets.js +5 -6
- package/addon/initializers/patch-ember-leaflet-tooltip-layer.js +51 -0
- package/addon/routes/management/issues/index/details.js +1 -1
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +231 -1
- package/addon/services/getting-started.js +44 -0
- package/addon/services/issue-actions.js +71 -0
- package/addon/services/leaflet-map-manager.js +2 -0
- package/addon/services/map-adapter/leaflet.js +19 -0
- package/addon/services/service-areas.js +3 -0
- package/addon/styles/fleetops-engine.css +1036 -0
- package/addon/templates/management/contacts/customers/details.hbs +1 -1
- package/addon/templates/management/contacts/index/details.hbs +1 -1
- package/addon/templates/management/contacts.hbs +2 -2
- package/addon/templates/management/issues/index/details/index.hbs +3 -1
- package/addon/templates/management/issues/index/details.hbs +3 -2
- package/addon/templates/management/vendors/index/details/personnel.hbs +1 -2
- package/addon/templates/operations/orders/index/details/index.hbs +2 -2
- package/addon/templates/operations/orders/index.hbs +1 -1
- package/addon/templates/operations/service-rates/index/edit.hbs +1 -0
- package/addon/templates/operations/service-rates/index/new.hbs +1 -0
- package/addon/templates/settings/payments/onboard.hbs +8 -8
- package/addon/utils/leaflet-draw-namespace-guard.js +22 -0
- package/addon/utils/leaflet-plugin-loader.js +219 -0
- package/addon/utils/setup-customer-portal.js +4 -64
- package/app/components/home/getting-started-guidance.js +1 -0
- package/app/components/issue/details/comments.js +1 -0
- package/app/components/issue/details/correspondence.js +1 -0
- package/app/components/issue/details/documents.js +1 -0
- package/app/components/issue/panel-header.js +1 -0
- package/app/components/modals/convert-customer-to-vendor.js +1 -0
- package/app/components/modals/issue-assign.js +1 -0
- package/app/components/modals/issue-close.js +1 -0
- package/app/components/modals/issue-status.js +1 -0
- package/app/components/vendor/personnel-manager.js +1 -0
- package/app/helpers/format-duration.js +1 -1
- package/app/services/getting-started.js +1 -0
- package/app/utils/leaflet-draw-namespace-guard.js +1 -0
- package/app/utils/leaflet-plugin-loader.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +5 -5
- package/server/migrations/2026_05_31_000001_add_portal_fields_to_vendor_personnels_table.php +30 -0
- package/server/migrations/2026_06_03_000000_ensure_tags_column_on_issues_table.php +31 -0
- package/server/migrations/2026_06_03_000001_add_order_uuid_to_issues_table.php +25 -0
- package/server/migrations/2026_06_04_000001_backfill_order_transaction_subjects.php +39 -0
- package/server/migrations/2026_06_04_000002_backfill_order_transaction_directions.php +24 -0
- package/server/resources/views/mail/customer-credentials.blade.php +16 -13
- package/server/src/Expansions/UserFilterExpansion.php +28 -0
- package/server/src/Http/Controllers/Api/v1/IssueController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -25
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +10 -58
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +135 -0
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +116 -6
- package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/IssueController.php +251 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +172 -97
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +10 -68
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +89 -0
- package/server/src/Http/Requests/CreateIssueRequest.php +4 -0
- package/server/src/Http/Requests/UpdateIssueRequest.php +4 -0
- package/server/src/Http/Resources/v1/Index/Order.php +12 -9
- package/server/src/Http/Resources/v1/Issue.php +25 -0
- package/server/src/Http/Resources/v1/Order.php +20 -18
- package/server/src/Mail/CustomerCredentialsMail.php +5 -6
- package/server/src/Models/Contact.php +30 -51
- package/server/src/Models/Issue.php +47 -6
- package/server/src/Models/Order.php +33 -6
- package/server/src/Models/ServiceArea.php +1 -1
- package/server/src/Models/VendorPersonnel.php +10 -1
- package/server/src/Models/Zone.php +1 -1
- package/server/src/Observers/PurchaseRateObserver.php +20 -0
- package/server/src/Observers/ZoneObserver.php +56 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +1 -0
- package/server/src/Support/Analytics/TopDrivers.php +9 -1
- package/server/src/Support/GettingStarted.php +144 -0
- package/server/src/Support/PlaceSearch.php +282 -0
- package/server/src/Support/ResolvesOrderServiceStops.php +22 -12
- package/server/src/routes.php +33 -1
- package/server/tests/AnalyticsRoutesTest.php +8 -0
- package/server/tests/CustomerContactIdentitySafetyTest.php +41 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +2 -2
- package/server/tests/GettingStartedStatusTest.php +21 -0
- package/server/tests/OrderActivityFlowRegressionTest.php +116 -0
- package/server/tests/ServiceAreaZoneApiShapeTest.php +44 -0
- package/translations/ar-ae.yml +9 -0
- package/translations/bg-bg.yaml +9 -0
- package/translations/en-us.yaml +11 -0
- package/translations/es-pa.yaml +9 -0
- package/translations/fr-fr.yaml +9 -0
- package/translations/mn-mn.yaml +9 -0
- package/translations/pt-br.yaml +9 -0
- package/translations/ru-ru.yaml +9 -0
- package/translations/vi-vn.yaml +9 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Resources\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Support\Utils;
|
|
6
|
+
use Fleetbase\Http\Resources\File;
|
|
6
7
|
use Fleetbase\Http\Resources\FleetbaseResource;
|
|
7
8
|
use Fleetbase\Http\Resources\User;
|
|
8
9
|
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
@@ -26,12 +27,15 @@ class Issue extends FleetbaseResource
|
|
|
26
27
|
'driver_uuid' => $this->when(Http::isInternalRequest(), $this->driver_uuid),
|
|
27
28
|
'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
|
|
28
29
|
'vehicle_uuid' => $this->when(Http::isInternalRequest(), $this->vehicle_uuid),
|
|
30
|
+
'order_uuid' => $this->when(Http::isInternalRequest(), $this->order_uuid ?? data_get($this, 'meta.order_uuid')),
|
|
29
31
|
'assigned_to_uuid' => $this->when(Http::isInternalRequest(), $this->assigned_to_uuid),
|
|
30
32
|
'reported_by_uuid' => $this->when(Http::isInternalRequest(), $this->reported_by_uuid),
|
|
31
33
|
'assignee' => $this->whenLoaded('assignee', fn () => new User($this->assignee)),
|
|
32
34
|
'reporter' => $this->whenLoaded('reporter', fn () => new User($this->reporter)),
|
|
33
35
|
'vehicle' => $this->whenLoaded('vehicle', fn () => new Vehicle($this->vehicle)),
|
|
34
36
|
'driver' => $this->whenLoaded('driver', fn () => new Driver($this->driver)),
|
|
37
|
+
'order' => $this->when(Http::isInternalRequest(), fn () => $this->resolveLinkedOrder()),
|
|
38
|
+
'files' => $this->whenLoaded('files', fn () => File::collection($this->files)),
|
|
35
39
|
'driver_name' => $this->driver_name,
|
|
36
40
|
'vehicle_name' => $this->vehicle_name,
|
|
37
41
|
'vehicle_id' => $this->vehicle_id,
|
|
@@ -55,6 +59,27 @@ class Issue extends FleetbaseResource
|
|
|
55
59
|
]);
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
protected function resolveLinkedOrder()
|
|
63
|
+
{
|
|
64
|
+
if ($this->relationLoaded('order') && $this->order) {
|
|
65
|
+
$this->order->loadMissing(['payload.pickup', 'payload.dropoff', 'trackingNumber', 'customer']);
|
|
66
|
+
|
|
67
|
+
return new Order($this->order);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
$orderUuid = $this->order_uuid ?? data_get($this, 'meta.order_uuid');
|
|
71
|
+
if (!$orderUuid) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$order = \Fleetbase\FleetOps\Models\Order::with(['payload.pickup', 'payload.dropoff', 'trackingNumber', 'customer'])
|
|
76
|
+
->where('uuid', $orderUuid)
|
|
77
|
+
->where('company_uuid', $this->company_uuid)
|
|
78
|
+
->first();
|
|
79
|
+
|
|
80
|
+
return $order ? new Order($order) : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
58
83
|
/**
|
|
59
84
|
* Transform the resource into an webhook payload.
|
|
60
85
|
*
|
|
@@ -73,24 +73,26 @@ class Order extends FleetbaseResource
|
|
|
73
73
|
'tracking_statuses' => $this->whenLoaded('trackingStatuses', function () {
|
|
74
74
|
return TrackingStatus::collection($this->trackingStatuses);
|
|
75
75
|
}),
|
|
76
|
-
'tracking'
|
|
77
|
-
'barcode'
|
|
78
|
-
'qr_code'
|
|
79
|
-
'comments'
|
|
80
|
-
'files'
|
|
81
|
-
'purchase_rate'
|
|
82
|
-
'notes'
|
|
83
|
-
'type'
|
|
84
|
-
'status'
|
|
85
|
-
'pod_method'
|
|
86
|
-
'pod_required'
|
|
87
|
-
'dispatched'
|
|
88
|
-
'started'
|
|
89
|
-
'adhoc'
|
|
90
|
-
'adhoc_distance'
|
|
91
|
-
'distance'
|
|
92
|
-
'time'
|
|
93
|
-
'
|
|
76
|
+
'tracking' => $this->when($isInternal, $this->trackingNumber ? $this->trackingNumber->tracking_number : null),
|
|
77
|
+
'barcode' => $this->when($isInternal, $this->trackingNumber ? $this->trackingNumber->barcode : null),
|
|
78
|
+
'qr_code' => $this->when($isInternal, $this->trackingNumber ? $this->trackingNumber->qr_code : null),
|
|
79
|
+
'comments' => $this->when($isInternal, Comment::collection($this->comments)),
|
|
80
|
+
'files' => $this->when($isInternal, $this->files, File::collection($this->files)),
|
|
81
|
+
'purchase_rate' => new PurchaseRate($this->purchaseRate),
|
|
82
|
+
'notes' => $this->notes,
|
|
83
|
+
'type' => $this->type,
|
|
84
|
+
'status' => $this->status,
|
|
85
|
+
'pod_method' => $this->pod_method,
|
|
86
|
+
'pod_required' => (bool) data_get($this, 'pod_required', false),
|
|
87
|
+
'dispatched' => (bool) data_get($this, 'dispatched', false),
|
|
88
|
+
'started' => (bool) data_get($this, 'started', false),
|
|
89
|
+
'adhoc' => (bool) data_get($this, 'adhoc', false),
|
|
90
|
+
'adhoc_distance' => (int) $this->getAdhocDistance(),
|
|
91
|
+
'distance' => (int) $this->distance,
|
|
92
|
+
'time' => (int) $this->time,
|
|
93
|
+
'transaction_amount' => $this->transaction_amount,
|
|
94
|
+
'currency' => $this->transaction_currency,
|
|
95
|
+
'tracker_data' => $this->when($withTrackerData, function () use ($tracker) {
|
|
94
96
|
return $this->resource->tracker_data ?? ($tracker ? $tracker->toArray() : null);
|
|
95
97
|
}),
|
|
96
98
|
'eta' => $this->when($withEta, function () use ($tracker) {
|
|
@@ -45,7 +45,7 @@ class CustomerCredentialsMail extends Mailable
|
|
|
45
45
|
$this->customer->loadMissing('company');
|
|
46
46
|
|
|
47
47
|
return new Envelope(
|
|
48
|
-
subject: 'Your
|
|
48
|
+
subject: 'Your ' . $this->customer->company->name . ' customer portal access is ready',
|
|
49
49
|
);
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -63,19 +63,18 @@ class CustomerCredentialsMail extends Mailable
|
|
|
63
63
|
'customer' => $this->customer,
|
|
64
64
|
'plaintextPassword' => $this->plaintextPassword,
|
|
65
65
|
'customerPortalUrl' => $this->getCustomerPortalAccessUrl(),
|
|
66
|
-
'currentHour' => now()->hour,
|
|
67
66
|
]
|
|
68
67
|
);
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
/**
|
|
72
|
-
* Get the customer portal URL
|
|
71
|
+
* Get the customer portal URL.
|
|
73
72
|
*/
|
|
74
|
-
private function getCustomerPortalAccessUrl():
|
|
73
|
+
private function getCustomerPortalAccessUrl(): string
|
|
75
74
|
{
|
|
76
75
|
$customerPortalConfig = Setting::lookupFromCompany('customer-portal-config');
|
|
77
|
-
$accessUrlSlug = data_get($customerPortalConfig, 'accessUrlSlug');
|
|
76
|
+
$accessUrlSlug = data_get($customerPortalConfig, 'accessUrlSlug', 'customer-portal');
|
|
78
77
|
|
|
79
|
-
return
|
|
78
|
+
return Utils::consoleUrl($accessUrlSlug ?: 'customer-portal');
|
|
80
79
|
}
|
|
81
80
|
}
|
|
@@ -7,10 +7,8 @@ use Fleetbase\FleetOps\Exceptions\CustomerUserConflictException;
|
|
|
7
7
|
use Fleetbase\FleetOps\Exceptions\UserAlreadyExistsException;
|
|
8
8
|
use Fleetbase\FleetOps\Support\Utils;
|
|
9
9
|
use Fleetbase\Models\CompanyUser;
|
|
10
|
-
use Fleetbase\Models\Invite;
|
|
11
10
|
use Fleetbase\Models\Model;
|
|
12
11
|
use Fleetbase\Models\User;
|
|
13
|
-
use Fleetbase\Notifications\UserInvited;
|
|
14
12
|
use Fleetbase\Traits\HasApiModelBehavior;
|
|
15
13
|
use Fleetbase\Traits\HasApiModelCache;
|
|
16
14
|
use Fleetbase\Traits\HasCustomFields;
|
|
@@ -301,15 +299,15 @@ class Contact extends Model
|
|
|
301
299
|
}
|
|
302
300
|
|
|
303
301
|
/**
|
|
304
|
-
* Creates a new user from the given contact
|
|
302
|
+
* Creates a new user from the given contact without sending an organization invitation.
|
|
305
303
|
*
|
|
306
304
|
* This method first checks if a user with the same email or phone number as the contact already exists.
|
|
307
305
|
* If such a user exists, it throws a UserAlreadyExistsException. Otherwise, it proceeds to create a new
|
|
308
306
|
* user record using the contact's details, assigns the user to the contact's company, and links the
|
|
309
|
-
* user to the contact.
|
|
307
|
+
* user to the contact.
|
|
310
308
|
*
|
|
311
309
|
* @param Contact $contact the contact instance from which the user should be created
|
|
312
|
-
* @param bool $sendInvite
|
|
310
|
+
* @param bool $sendInvite Deprecated and ignored. Contact and customer users do not receive organization invitations.
|
|
313
311
|
*
|
|
314
312
|
* @return User the newly created user instance
|
|
315
313
|
*
|
|
@@ -379,8 +377,8 @@ class Contact extends Model
|
|
|
379
377
|
// Set user type
|
|
380
378
|
$user->setType($contact->type);
|
|
381
379
|
|
|
382
|
-
//
|
|
383
|
-
|
|
380
|
+
// Assign to company without triggering core organization invitations.
|
|
381
|
+
static::assignUserToContactCompany($contact, $user);
|
|
384
382
|
|
|
385
383
|
// Assign customer role
|
|
386
384
|
if ($contact->isCustomer()) {
|
|
@@ -393,23 +391,6 @@ class Contact extends Model
|
|
|
393
391
|
$contact->update(['user_uuid' => $user->uuid]);
|
|
394
392
|
}
|
|
395
393
|
|
|
396
|
-
// Optionally, send invite
|
|
397
|
-
if ($sendInvite) {
|
|
398
|
-
// send invitation to user
|
|
399
|
-
$invitation = Invite::create([
|
|
400
|
-
'company_uuid' => $user->company_uuid,
|
|
401
|
-
'created_by_uuid' => session('user'),
|
|
402
|
-
'subject_uuid' => $user->company_uuid,
|
|
403
|
-
'subject_type' => Utils::getMutationType('company'),
|
|
404
|
-
'protocol' => 'email',
|
|
405
|
-
'recipients' => [$user->email],
|
|
406
|
-
'reason' => 'join_company',
|
|
407
|
-
]);
|
|
408
|
-
|
|
409
|
-
// notify user
|
|
410
|
-
$user->notify(new UserInvited($invitation));
|
|
411
|
-
}
|
|
412
|
-
|
|
413
394
|
$contact->setRelation('user', $user);
|
|
414
395
|
|
|
415
396
|
return $user;
|
|
@@ -491,21 +472,20 @@ class Contact extends Model
|
|
|
491
472
|
}
|
|
492
473
|
|
|
493
474
|
/**
|
|
494
|
-
* Assigns a user to the company
|
|
475
|
+
* Assigns a user to the contact's company without sending an organization invitation.
|
|
495
476
|
*
|
|
496
477
|
* This method performs the following actions:
|
|
497
478
|
* 1. Associates the provided user with the current company, assigning them a role based on the
|
|
498
479
|
* company's type (e.g., 'Fleet-Ops Customer' or 'Fleet-Ops Contact').
|
|
499
480
|
* 2. If the user is of type 'customer', assigns the 'Fleet-Ops Customer' role specifically.
|
|
500
481
|
* 3. Updates the current entity's `user_uuid` to reference the assigned user.
|
|
501
|
-
* 4. Optionally sends an invitation email to the user, inviting them to join the company.
|
|
502
482
|
*
|
|
503
483
|
* @param User $user the user to be assigned to the company
|
|
504
|
-
* @param bool $sendInvite
|
|
484
|
+
* @param bool $sendInvite Deprecated and ignored. Contact and customer users do not receive organization invitations.
|
|
505
485
|
*
|
|
506
486
|
* @return self returns the current instance to allow method chaining
|
|
507
487
|
*
|
|
508
|
-
* @throws \Exception if an error occurs during user assignment
|
|
488
|
+
* @throws \Exception if an error occurs during user assignment
|
|
509
489
|
*/
|
|
510
490
|
public function assignUser(User $user, bool $sendInvite = false): self
|
|
511
491
|
{
|
|
@@ -525,8 +505,8 @@ class Contact extends Model
|
|
|
525
505
|
}
|
|
526
506
|
}
|
|
527
507
|
|
|
528
|
-
//
|
|
529
|
-
|
|
508
|
+
// Assign to company without triggering core organization invitations.
|
|
509
|
+
static::assignUserToContactCompany($this, $user);
|
|
530
510
|
|
|
531
511
|
// Get the company user instance
|
|
532
512
|
$companyUser = $user->getCompanyUser($this->company);
|
|
@@ -538,23 +518,6 @@ class Contact extends Model
|
|
|
538
518
|
// Set user to contact
|
|
539
519
|
$this->update(['user_uuid' => $user->uuid]);
|
|
540
520
|
|
|
541
|
-
// Optionally, send invite
|
|
542
|
-
if ($sendInvite) {
|
|
543
|
-
// send invitation to user
|
|
544
|
-
$invitation = Invite::create([
|
|
545
|
-
'company_uuid' => $user->company_uuid,
|
|
546
|
-
'created_by_uuid' => session('user'),
|
|
547
|
-
'subject_uuid' => $user->company_uuid,
|
|
548
|
-
'subject_type' => Utils::getMutationType('company'),
|
|
549
|
-
'protocol' => 'email',
|
|
550
|
-
'recipients' => [$user->email],
|
|
551
|
-
'reason' => 'join_company',
|
|
552
|
-
]);
|
|
553
|
-
|
|
554
|
-
// notify user
|
|
555
|
-
$user->notify(new UserInvited($invitation));
|
|
556
|
-
}
|
|
557
|
-
|
|
558
521
|
$this->setRelation('user', $user);
|
|
559
522
|
|
|
560
523
|
return $this;
|
|
@@ -657,12 +620,11 @@ class Contact extends Model
|
|
|
657
620
|
}
|
|
658
621
|
|
|
659
622
|
/**
|
|
660
|
-
* Creates a new user from the current contact instance
|
|
623
|
+
* Creates a new user from the current contact instance without sending an organization invitation.
|
|
661
624
|
*
|
|
662
|
-
* This method is a wrapper around the createUserFromContact method.
|
|
663
|
-
* from a contact instance and, optionally, sending an invitation to the newly created user.
|
|
625
|
+
* This method is a wrapper around the createUserFromContact method.
|
|
664
626
|
*
|
|
665
|
-
* @param bool $sendInvite
|
|
627
|
+
* @param bool $sendInvite Deprecated and ignored. Contact and customer users do not receive organization invitations.
|
|
666
628
|
*
|
|
667
629
|
* @return User the newly created user instance
|
|
668
630
|
*/
|
|
@@ -709,4 +671,21 @@ class Contact extends Model
|
|
|
709
671
|
{
|
|
710
672
|
return !$this->hasUser();
|
|
711
673
|
}
|
|
674
|
+
|
|
675
|
+
private static function assignUserToContactCompany(Contact $contact, User $user): ?CompanyUser
|
|
676
|
+
{
|
|
677
|
+
$contact->loadMissing('company');
|
|
678
|
+
|
|
679
|
+
if (!$contact->company) {
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
$role = $contact->isCustomer() ? 'Fleet-Ops Customer' : 'Fleet-Ops Contact';
|
|
684
|
+
$companyUser = $contact->company->addUser($user, $role);
|
|
685
|
+
|
|
686
|
+
$user->forceFill(['company_uuid' => $contact->company->uuid])->saveQuietly();
|
|
687
|
+
$user->setRelation('companyUser', $companyUser);
|
|
688
|
+
|
|
689
|
+
return $companyUser;
|
|
690
|
+
}
|
|
712
691
|
}
|
|
@@ -14,7 +14,11 @@ use Fleetbase\Traits\HasCustomFields;
|
|
|
14
14
|
use Fleetbase\Traits\HasPublicId;
|
|
15
15
|
use Fleetbase\Traits\HasUuid;
|
|
16
16
|
use Fleetbase\Traits\TracksApiCredential;
|
|
17
|
+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
18
|
+
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
17
19
|
use Illuminate\Support\Str;
|
|
20
|
+
use Spatie\Activitylog\LogOptions;
|
|
21
|
+
use Spatie\Activitylog\Traits\LogsActivity;
|
|
18
22
|
|
|
19
23
|
class Issue extends Model
|
|
20
24
|
{
|
|
@@ -24,6 +28,7 @@ class Issue extends Model
|
|
|
24
28
|
use SpatialTrait;
|
|
25
29
|
use HasApiModelBehavior;
|
|
26
30
|
use HasApiModelCache;
|
|
31
|
+
use LogsActivity;
|
|
27
32
|
use HasCustomFields;
|
|
28
33
|
|
|
29
34
|
/**
|
|
@@ -60,6 +65,7 @@ class Issue extends Model
|
|
|
60
65
|
'assigned_to_uuid',
|
|
61
66
|
'vehicle_uuid',
|
|
62
67
|
'driver_uuid',
|
|
68
|
+
'order_uuid',
|
|
63
69
|
'issue_id',
|
|
64
70
|
'location',
|
|
65
71
|
'category',
|
|
@@ -106,6 +112,31 @@ class Issue extends Model
|
|
|
106
112
|
*/
|
|
107
113
|
protected $appends = ['driver_name', 'vehicle_name', 'assignee_name', 'reporter_name'];
|
|
108
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Get the activity log options for the model.
|
|
117
|
+
*/
|
|
118
|
+
public function getActivitylogOptions(): LogOptions
|
|
119
|
+
{
|
|
120
|
+
return LogOptions::defaults()
|
|
121
|
+
->logOnly([
|
|
122
|
+
'title',
|
|
123
|
+
'report',
|
|
124
|
+
'status',
|
|
125
|
+
'priority',
|
|
126
|
+
'assigned_to_uuid',
|
|
127
|
+
'vehicle_uuid',
|
|
128
|
+
'driver_uuid',
|
|
129
|
+
'order_uuid',
|
|
130
|
+
'type',
|
|
131
|
+
'category',
|
|
132
|
+
'tags',
|
|
133
|
+
'resolved_at',
|
|
134
|
+
'meta',
|
|
135
|
+
])
|
|
136
|
+
->logOnlyDirty()
|
|
137
|
+
->dontSubmitEmptyLogs();
|
|
138
|
+
}
|
|
139
|
+
|
|
109
140
|
/**
|
|
110
141
|
* The attributes excluded from the model's JSON form.
|
|
111
142
|
*
|
|
@@ -114,7 +145,7 @@ class Issue extends Model
|
|
|
114
145
|
protected $hidden = [];
|
|
115
146
|
|
|
116
147
|
/**
|
|
117
|
-
* @return
|
|
148
|
+
* @return BelongsTo
|
|
118
149
|
*/
|
|
119
150
|
public function reportedBy()
|
|
120
151
|
{
|
|
@@ -122,7 +153,7 @@ class Issue extends Model
|
|
|
122
153
|
}
|
|
123
154
|
|
|
124
155
|
/**
|
|
125
|
-
* @return
|
|
156
|
+
* @return BelongsTo
|
|
126
157
|
*/
|
|
127
158
|
public function reporter()
|
|
128
159
|
{
|
|
@@ -130,7 +161,7 @@ class Issue extends Model
|
|
|
130
161
|
}
|
|
131
162
|
|
|
132
163
|
/**
|
|
133
|
-
* @return
|
|
164
|
+
* @return BelongsTo
|
|
134
165
|
*/
|
|
135
166
|
public function assignedTo()
|
|
136
167
|
{
|
|
@@ -138,7 +169,7 @@ class Issue extends Model
|
|
|
138
169
|
}
|
|
139
170
|
|
|
140
171
|
/**
|
|
141
|
-
* @return
|
|
172
|
+
* @return BelongsTo
|
|
142
173
|
*/
|
|
143
174
|
public function assignee()
|
|
144
175
|
{
|
|
@@ -146,7 +177,7 @@ class Issue extends Model
|
|
|
146
177
|
}
|
|
147
178
|
|
|
148
179
|
/**
|
|
149
|
-
* @return
|
|
180
|
+
* @return BelongsTo
|
|
150
181
|
*/
|
|
151
182
|
public function vehicle()
|
|
152
183
|
{
|
|
@@ -154,13 +185,23 @@ class Issue extends Model
|
|
|
154
185
|
}
|
|
155
186
|
|
|
156
187
|
/**
|
|
157
|
-
* @return
|
|
188
|
+
* @return BelongsTo
|
|
158
189
|
*/
|
|
159
190
|
public function driver()
|
|
160
191
|
{
|
|
161
192
|
return $this->belongsTo(Driver::class);
|
|
162
193
|
}
|
|
163
194
|
|
|
195
|
+
public function order(): BelongsTo
|
|
196
|
+
{
|
|
197
|
+
return $this->belongsTo(Order::class, 'order_uuid', 'uuid')->without(['comments', 'files']);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public function files(): HasMany
|
|
201
|
+
{
|
|
202
|
+
return $this->hasMany(\Fleetbase\Models\File::class, 'subject_uuid', 'uuid')->latest();
|
|
203
|
+
}
|
|
204
|
+
|
|
164
205
|
/**
|
|
165
206
|
* Set the title attribute for the Issue model.
|
|
166
207
|
*
|
|
@@ -169,6 +169,7 @@ class Order extends Model
|
|
|
169
169
|
'tracking',
|
|
170
170
|
'total_entities',
|
|
171
171
|
'transaction_amount',
|
|
172
|
+
'transaction_currency',
|
|
172
173
|
'customer_name',
|
|
173
174
|
'customer_phone',
|
|
174
175
|
'facilitator_name',
|
|
@@ -508,6 +509,16 @@ class Order extends Model
|
|
|
508
509
|
return data_get($this, 'transaction.amount');
|
|
509
510
|
}
|
|
510
511
|
|
|
512
|
+
/**
|
|
513
|
+
* The transaction amount for the order.
|
|
514
|
+
*
|
|
515
|
+
* @return string
|
|
516
|
+
*/
|
|
517
|
+
public function getTransactionCurrencyAttribute()
|
|
518
|
+
{
|
|
519
|
+
return data_get($this, 'transaction.currency');
|
|
520
|
+
}
|
|
521
|
+
|
|
511
522
|
/**
|
|
512
523
|
* The customer name for the order.
|
|
513
524
|
*
|
|
@@ -1143,7 +1154,17 @@ class Order extends Model
|
|
|
1143
1154
|
$this->setRelation('purchaseRate', $purchaseRate);
|
|
1144
1155
|
|
|
1145
1156
|
if ($purchaseRate->transaction_uuid) {
|
|
1146
|
-
$
|
|
1157
|
+
$transaction = Transaction::where('uuid', $purchaseRate->transaction_uuid)->first();
|
|
1158
|
+
if ($transaction instanceof Transaction) {
|
|
1159
|
+
$transaction->update([
|
|
1160
|
+
'subject_uuid' => $this->uuid,
|
|
1161
|
+
'subject_type' => static::class,
|
|
1162
|
+
'context_uuid' => $purchaseRate->uuid,
|
|
1163
|
+
'context_type' => PurchaseRate::class,
|
|
1164
|
+
'direction' => Transaction::DIRECTION_CREDIT,
|
|
1165
|
+
]);
|
|
1166
|
+
$this->setRelation('transaction', $transaction);
|
|
1167
|
+
}
|
|
1147
1168
|
}
|
|
1148
1169
|
|
|
1149
1170
|
if (
|
|
@@ -1176,12 +1197,17 @@ class Order extends Model
|
|
|
1176
1197
|
'company_uuid' => session('company', $this->company_uuid),
|
|
1177
1198
|
'customer_uuid' => $this->customer_uuid,
|
|
1178
1199
|
'customer_type' => $this->customer_type,
|
|
1200
|
+
'subject_uuid' => $this->uuid,
|
|
1201
|
+
'subject_type' => static::class,
|
|
1202
|
+
'context_uuid' => $this->uuid,
|
|
1203
|
+
'context_type' => static::class,
|
|
1179
1204
|
'gateway_transaction_id' => Transaction::generateNumber(),
|
|
1180
1205
|
'gateway' => 'internal',
|
|
1181
1206
|
'amount' => 0,
|
|
1182
1207
|
'currency' => Utils::getCompanyTransactionCurrency($this->company ?? $this->company_uuid),
|
|
1183
1208
|
'description' => 'Dispatch order',
|
|
1184
1209
|
'type' => 'dispatch',
|
|
1210
|
+
'direction' => Transaction::DIRECTION_CREDIT,
|
|
1185
1211
|
'status' => 'success',
|
|
1186
1212
|
]);
|
|
1187
1213
|
|
|
@@ -1379,8 +1405,9 @@ class Order extends Model
|
|
|
1379
1405
|
*
|
|
1380
1406
|
* The process involves the following steps:
|
|
1381
1407
|
* 1. Creating a new Activity instance with the 'completed' code and relevant details.
|
|
1382
|
-
* 2.
|
|
1383
|
-
*
|
|
1408
|
+
* 2. Updating the order's activity with the new 'completed' activity through `updateActivity`,
|
|
1409
|
+
* which applies and persists the 'completed' status.
|
|
1410
|
+
* 3. Notifying that the order has been completed via `notifyCompleted`.
|
|
1384
1411
|
*
|
|
1385
1412
|
* @param Proof|null $proof Optional. Additional proof or details for the activity update.
|
|
1386
1413
|
*
|
|
@@ -1388,15 +1415,15 @@ class Order extends Model
|
|
|
1388
1415
|
*/
|
|
1389
1416
|
public function complete(?Proof $proof = null): self
|
|
1390
1417
|
{
|
|
1391
|
-
$this->notifyCompleted();
|
|
1392
|
-
|
|
1393
1418
|
$doesntHaveCompletedActivity = TrackingStatus::where(['tracking_number_uuid' => $this->tracking_number_uuid, 'code' => 'COMPLETED'])->doesntExist();
|
|
1394
1419
|
if ($doesntHaveCompletedActivity) {
|
|
1395
1420
|
$activity = $this->config()->getCompletedActivity();
|
|
1396
1421
|
|
|
1397
|
-
|
|
1422
|
+
$this->updateActivity($activity, $proof);
|
|
1398
1423
|
}
|
|
1399
1424
|
|
|
1425
|
+
$this->notifyCompleted();
|
|
1426
|
+
|
|
1400
1427
|
return $this;
|
|
1401
1428
|
}
|
|
1402
1429
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Models;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\Models\Model;
|
|
6
|
+
use Fleetbase\Models\User;
|
|
6
7
|
use Fleetbase\Traits\HasUuid;
|
|
7
8
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
8
9
|
|
|
@@ -22,7 +23,7 @@ class VendorPersonnel extends Model
|
|
|
22
23
|
*
|
|
23
24
|
* @var array
|
|
24
25
|
*/
|
|
25
|
-
protected $fillable = ['vendor_uuid', 'contact_uuid'];
|
|
26
|
+
protected $fillable = ['vendor_uuid', 'contact_uuid', 'role', 'status', 'invited_by_uuid'];
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* These attributes that can be queried.
|
|
@@ -60,4 +61,12 @@ class VendorPersonnel extends Model
|
|
|
60
61
|
{
|
|
61
62
|
return $this->belongsTo(Contact::class);
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The Fleetbase user who invited this personnel.
|
|
67
|
+
*/
|
|
68
|
+
public function invitedBy(): BelongsTo
|
|
69
|
+
{
|
|
70
|
+
return $this->belongsTo(User::class, 'invited_by_uuid');
|
|
71
|
+
}
|
|
63
72
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Observers;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
5
6
|
use Fleetbase\FleetOps\Models\PurchaseRate;
|
|
6
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
7
8
|
use Fleetbase\Models\Company;
|
|
@@ -18,7 +19,12 @@ class PurchaseRateObserver
|
|
|
18
19
|
*/
|
|
19
20
|
public function creating(PurchaseRate $purchaseRate)
|
|
20
21
|
{
|
|
22
|
+
if (!$purchaseRate->uuid) {
|
|
23
|
+
$purchaseRate->uuid = PurchaseRate::generateUuid();
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
$purchaseRate->load(['serviceQuote.items', 'serviceQuote.serviceRate']);
|
|
27
|
+
$order = $this->resolveOrder($purchaseRate);
|
|
22
28
|
|
|
23
29
|
// get company
|
|
24
30
|
$company = Company::where('uuid', session('company', $purchaseRate->company_uuid))->first();
|
|
@@ -32,12 +38,17 @@ class PurchaseRateObserver
|
|
|
32
38
|
'company_uuid' => session('company', $purchaseRate->company_uuid),
|
|
33
39
|
'customer_uuid' => $purchaseRate->customer_uuid,
|
|
34
40
|
'customer_type' => $purchaseRate->customer_type,
|
|
41
|
+
'subject_uuid' => $order?->uuid,
|
|
42
|
+
'subject_type' => $order ? Order::class : null,
|
|
43
|
+
'context_uuid' => $purchaseRate->uuid,
|
|
44
|
+
'context_type' => PurchaseRate::class,
|
|
35
45
|
'gateway_transaction_id' => $purchaseRate->getMeta('transaction_id', Transaction::generateNumber()),
|
|
36
46
|
'gateway' => 'internal',
|
|
37
47
|
'amount' => data_get($purchaseRate, 'serviceQuote.amount', 0),
|
|
38
48
|
'currency' => $currency,
|
|
39
49
|
'description' => 'Dispatch order',
|
|
40
50
|
'type' => 'dispatch',
|
|
51
|
+
'direction' => Transaction::DIRECTION_CREDIT,
|
|
41
52
|
'status' => 'success',
|
|
42
53
|
]);
|
|
43
54
|
|
|
@@ -56,4 +67,13 @@ class PurchaseRateObserver
|
|
|
56
67
|
$purchaseRate->transaction_uuid = $transaction->uuid;
|
|
57
68
|
$purchaseRate->status = $purchaseRate->status ?: Transaction::STATUS_SUCCESS;
|
|
58
69
|
}
|
|
70
|
+
|
|
71
|
+
protected function resolveOrder(PurchaseRate $purchaseRate): ?Order
|
|
72
|
+
{
|
|
73
|
+
if (!$purchaseRate->payload_uuid) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return Order::where('payload_uuid', $purchaseRate->payload_uuid)->first();
|
|
78
|
+
}
|
|
59
79
|
}
|