@fleetbase/storefront-engine 0.4.0 → 0.4.2
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/addon/components/customer-panel/orders.hbs +2 -2
- package/addon/components/modals/create-gateway.hbs +33 -12
- package/addon/components/network-category-picker.js +1 -1
- package/addon/components/order-panel.hbs +1 -1
- package/addon/components/widget/customers.hbs +5 -2
- package/addon/components/widget/customers.js +14 -6
- package/addon/components/widget/orders.hbs +31 -10
- package/addon/components/widget/orders.js +7 -1
- package/addon/components/widget/storefront-key-metrics.js +2 -2
- package/addon/components/widget/storefront-metrics.hbs +11 -1
- package/addon/components/widget/storefront-metrics.js +103 -1
- package/addon/controllers/networks/index/network/index.js +2 -0
- package/addon/controllers/networks/index/network/stores.js +0 -1
- package/addon/controllers/products/index/category/new.js +13 -2
- package/addon/controllers/settings/gateways.js +6 -1
- package/addon/controllers/settings/index.js +1 -0
- package/addon/controllers/settings/notifications.js +4 -5
- package/addon/models/network.js +1 -0
- package/addon/models/store.js +1 -0
- package/addon/routes/networks/index/network/index.js +5 -0
- package/addon/routes/settings/index.js +5 -0
- package/addon/services/order-actions.js +31 -0
- package/addon/styles/storefront-engine.css +22 -0
- package/addon/templates/home.hbs +2 -2
- package/addon/templates/networks/index/network/index.hbs +21 -8
- package/addon/templates/networks/index/network/stores.hbs +8 -1
- package/addon/templates/products/index/category/new.hbs +149 -134
- package/addon/templates/settings/gateways.hbs +15 -4
- package/addon/templates/settings/index.hbs +13 -0
- package/addon/templates/settings/notifications.hbs +2 -2
- package/addon/utils/commerce-date-ranges.js +263 -0
- package/app/utils/commerce-date-ranges.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/server/migrations/2025_09_01_041353_add_default_order_config_column.php +110 -0
- package/server/src/Console/Commands/MigrateStripeSandboxCustomers.php +165 -0
- package/server/src/Expansions/OrderExpansion.php +43 -0
- package/server/src/Http/Controllers/NetworkController.php +1 -1
- package/server/src/Http/Controllers/OrderController.php +62 -9
- package/server/src/Http/Controllers/v1/CheckoutController.php +57 -48
- package/server/src/Http/Controllers/v1/ServiceQuoteController.php +19 -3
- package/server/src/Http/Middleware/SetStorefrontSession.php +8 -6
- package/server/src/Http/Resources/Customer.php +41 -17
- package/server/src/Http/Resources/Gateway.php +16 -11
- package/server/src/Http/Resources/Network.php +35 -34
- package/server/src/Http/Resources/NotificationChannel.php +17 -10
- package/server/src/Http/Resources/Product.php +1 -0
- package/server/src/Http/Resources/Store.php +36 -35
- package/server/src/Models/Customer.php +18 -2
- package/server/src/Models/FoodTruck.php +29 -2
- package/server/src/Models/Network.php +63 -1
- package/server/src/Models/Store.php +71 -9
- package/server/src/Notifications/StorefrontOrderAccepted.php +164 -0
- package/server/src/Notifications/StorefrontOrderCanceled.php +11 -1
- package/server/src/Notifications/StorefrontOrderCompleted.php +11 -1
- package/server/src/Notifications/StorefrontOrderDriverAssigned.php +12 -4
- package/server/src/Notifications/StorefrontOrderEnroute.php +12 -4
- package/server/src/Notifications/StorefrontOrderNearby.php +12 -4
- package/server/src/Notifications/StorefrontOrderPreparing.php +12 -4
- package/server/src/Notifications/StorefrontOrderReadyForPickup.php +12 -4
- package/server/src/Observers/OrderObserver.php +6 -4
- package/server/src/Providers/StorefrontServiceProvider.php +1 -0
- package/server/src/Rules/IsValidLocation.php +12 -0
- package/server/src/Support/PushNotification.php +20 -4
- package/server/src/Support/Storefront.php +283 -37
- package/server/src/routes.php +1 -0
- package/translations/{ar-ae.yml → ar-ae.yaml} +313 -253
- package/translations/bg-bg.yaml +734 -0
- package/translations/en-us.yaml +5 -0
- package/translations/es-es.yaml +732 -0
- package/translations/fr-fr.yaml +748 -0
- package/translations/mn-mn.yaml +725 -0
- package/translations/pt-br.yaml +732 -0
- package/translations/ru-ru.yaml +726 -0
- package/translations/vi-vn.yaml +412 -338
- package/translations/zh-cn.yaml +659 -0
|
@@ -71,12 +71,20 @@ class StorefrontOrderPreparing extends Notification
|
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Get the notification's delivery channels.
|
|
74
|
-
*
|
|
75
|
-
* @return array
|
|
76
74
|
*/
|
|
77
|
-
public function via($notifiable)
|
|
75
|
+
public function via($notifiable): array
|
|
78
76
|
{
|
|
79
|
-
|
|
77
|
+
$channels = ['mail', 'database'];
|
|
78
|
+
|
|
79
|
+
if (Storefront::hasNotificationChannelConfigured($this->storefront, 'apn')) {
|
|
80
|
+
$channels[] = ApnChannel::class;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (Storefront::hasNotificationChannelConfigured($this->storefront, 'fcm')) {
|
|
84
|
+
$channels[] = FcmChannel::class;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return $channels;
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
/**
|
|
@@ -71,12 +71,20 @@ class StorefrontOrderReadyForPickup extends Notification
|
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Get the notification's delivery channels.
|
|
74
|
-
*
|
|
75
|
-
* @return array
|
|
76
74
|
*/
|
|
77
|
-
public function via($notifiable)
|
|
75
|
+
public function via($notifiable): array
|
|
78
76
|
{
|
|
79
|
-
|
|
77
|
+
$channels = ['mail', 'database'];
|
|
78
|
+
|
|
79
|
+
if (Storefront::hasNotificationChannelConfigured($this->storefront, 'apn')) {
|
|
80
|
+
$channels[] = ApnChannel::class;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (Storefront::hasNotificationChannelConfigured($this->storefront, 'fcm')) {
|
|
84
|
+
$channels[] = FcmChannel::class;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return $channels;
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
/**
|
|
@@ -14,10 +14,12 @@ class OrderObserver
|
|
|
14
14
|
*/
|
|
15
15
|
public function creating(Order $order)
|
|
16
16
|
{
|
|
17
|
-
// Set the storefront order config
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
// Set the storefront order config if none is set already
|
|
18
|
+
if (!$order->order_config_uuid) {
|
|
19
|
+
$orderConfig = Storefront::getDefaultOrderConfig();
|
|
20
|
+
if ($orderConfig) {
|
|
21
|
+
$order->order_config_uuid = $orderConfig->uuid;
|
|
22
|
+
}
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -58,6 +58,7 @@ class StorefrontServiceProvider extends CoreServiceProvider
|
|
|
58
58
|
\Fleetbase\Storefront\Console\Commands\NotifyStorefrontOrderNearby::class,
|
|
59
59
|
\Fleetbase\Storefront\Console\Commands\SendOrderNotification::class,
|
|
60
60
|
\Fleetbase\Storefront\Console\Commands\PurgeExpiredCarts::class,
|
|
61
|
+
\Fleetbase\Storefront\Console\Commands\MigrateStripeSandboxCustomers::class,
|
|
61
62
|
];
|
|
62
63
|
|
|
63
64
|
/**
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
namespace Fleetbase\Storefront\Rules;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Models\Place;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
6
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
8
|
+
use Fleetbase\Storefront\Models\FoodTruck;
|
|
7
9
|
use Fleetbase\Storefront\Models\StoreLocation;
|
|
8
10
|
use Illuminate\Contracts\Validation\Rule;
|
|
9
11
|
use Illuminate\Support\Str;
|
|
@@ -29,6 +31,16 @@ class IsValidLocation implements Rule
|
|
|
29
31
|
return StoreLocation::where('public_id', $value)->exists();
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
// Validate Vehicle id
|
|
35
|
+
if (is_string($value) && Str::startsWith($value, 'vehicle_')) {
|
|
36
|
+
return Vehicle::where('public_id', $value)->exists();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Validate FoodTruck id
|
|
40
|
+
if (is_string($value) && Str::startsWith($value, 'food_truck_')) {
|
|
41
|
+
return FoodTruck::where('public_id', $value)->exists();
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
// Validate object with coordinates
|
|
33
45
|
if (isset($value->coordinates)) {
|
|
34
46
|
return Utils::isCoordinates($value->coordinates);
|
|
@@ -17,10 +17,14 @@ use Pushok\Client as PushOkClient;
|
|
|
17
17
|
|
|
18
18
|
class PushNotification
|
|
19
19
|
{
|
|
20
|
-
public static function createApnMessage(Order $order, string $title, string $body, string $status, $notifiable = null): ApnMessage
|
|
20
|
+
public static function createApnMessage(Order $order, string $title, string $body, string $status, $notifiable = null): ?ApnMessage
|
|
21
21
|
{
|
|
22
22
|
$storefront = static::getStorefrontFromOrder($order);
|
|
23
23
|
$client = static::getApnClient($storefront, $order);
|
|
24
|
+
if (!$client) {
|
|
25
|
+
// create apn message anyway
|
|
26
|
+
return new ApnMessage($title, $body);
|
|
27
|
+
}
|
|
24
28
|
|
|
25
29
|
return ApnMessage::create()
|
|
26
30
|
->badge(1)
|
|
@@ -33,10 +37,19 @@ class PushNotification
|
|
|
33
37
|
->via($client);
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
public static function createFcmMessage(Order $order, string $title, string $body, string $status, $notifiable = null): FcmMessage
|
|
40
|
+
public static function createFcmMessage(Order $order, string $title, string $body, string $status, $notifiable = null): ?FcmMessage
|
|
37
41
|
{
|
|
38
42
|
$storefront = static::getStorefrontFromOrder($order);
|
|
39
43
|
$notificationChannel = static::getNotificationChannel('apn', $storefront, $order);
|
|
44
|
+
if (!$notificationChannel) {
|
|
45
|
+
// create fcm message anyway
|
|
46
|
+
return new FcmMessage(
|
|
47
|
+
notification: new FcmNotification(
|
|
48
|
+
title: $title,
|
|
49
|
+
body: $body
|
|
50
|
+
)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
40
53
|
|
|
41
54
|
// Configure FCM
|
|
42
55
|
static::configureFcm($notificationChannel);
|
|
@@ -97,7 +110,7 @@ class PushNotification
|
|
|
97
110
|
return $firebaseConfig;
|
|
98
111
|
}
|
|
99
112
|
|
|
100
|
-
public static function getNotificationChannel(string $scheme, Network|Store $storefront, ?Order $order = null): NotificationChannel
|
|
113
|
+
public static function getNotificationChannel(string $scheme, Network|Store $storefront, ?Order $order = null): ?NotificationChannel
|
|
101
114
|
{
|
|
102
115
|
if ($order && $order->hasMeta('storefront_notification_channel')) {
|
|
103
116
|
return NotificationChannel::where([
|
|
@@ -113,9 +126,12 @@ class PushNotification
|
|
|
113
126
|
])->first();
|
|
114
127
|
}
|
|
115
128
|
|
|
116
|
-
public static function getApnClient(Network|Store $storefront, ?Order $order = null): PushOkClient
|
|
129
|
+
public static function getApnClient(Network|Store $storefront, ?Order $order = null): ?PushOkClient
|
|
117
130
|
{
|
|
118
131
|
$notificationChannel = static::getNotificationChannel('apn', $storefront, $order);
|
|
132
|
+
if (!$notificationChannel) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
119
135
|
$config = (array) $notificationChannel->config;
|
|
120
136
|
|
|
121
137
|
$isProductionEnv = Utils::castBoolean(data_get($config, 'production', app()->isProduction()));
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\Storefront\Support;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Flow\Activity;
|
|
5
6
|
use Fleetbase\FleetOps\Models\Contact;
|
|
6
7
|
use Fleetbase\FleetOps\Models\Order;
|
|
7
8
|
use Fleetbase\FleetOps\Models\OrderConfig;
|
|
@@ -9,11 +10,15 @@ use Fleetbase\Models\Company;
|
|
|
9
10
|
use Fleetbase\Models\User;
|
|
10
11
|
use Fleetbase\Storefront\Models\Gateway;
|
|
11
12
|
use Fleetbase\Storefront\Models\Network;
|
|
13
|
+
use Fleetbase\Storefront\Models\NotificationChannel;
|
|
12
14
|
use Fleetbase\Storefront\Models\Product;
|
|
13
15
|
use Fleetbase\Storefront\Models\Store;
|
|
16
|
+
use Fleetbase\Storefront\Notifications\StorefrontOrderAccepted;
|
|
14
17
|
use Fleetbase\Storefront\Notifications\StorefrontOrderCreated;
|
|
15
18
|
use Fleetbase\Support\Auth;
|
|
16
19
|
use Fleetbase\Support\Utils;
|
|
20
|
+
use Illuminate\Support\Facades\DB;
|
|
21
|
+
use Illuminate\Support\Facades\Log;
|
|
17
22
|
use Illuminate\Support\Facades\Notification;
|
|
18
23
|
use Illuminate\Support\Facades\Redis;
|
|
19
24
|
use Illuminate\Support\Str;
|
|
@@ -21,15 +26,20 @@ use Laravel\Sanctum\PersonalAccessToken;
|
|
|
21
26
|
|
|
22
27
|
class Storefront
|
|
23
28
|
{
|
|
29
|
+
/** @var string */
|
|
30
|
+
private const CONFIG_KEY = 'storefront';
|
|
31
|
+
/** @var string */
|
|
32
|
+
private const CONFIG_NS = 'system:order-config:storefront';
|
|
33
|
+
/** @var array<string,OrderConfig> In-memory cache keyed by company UUID */
|
|
34
|
+
private static array $configCache = [];
|
|
35
|
+
|
|
24
36
|
/**
|
|
25
37
|
* Returns current store or network based on session `storefront_key`
|
|
26
38
|
* with bare minimum columns, but can optionally pass in more columns to receive.
|
|
27
39
|
*
|
|
28
40
|
* @param array $columns
|
|
29
|
-
*
|
|
30
|
-
* @return \Fleetbase\Models\Storefront\Network|\Fleetbase\Models\Storefront\Store
|
|
31
41
|
*/
|
|
32
|
-
public static function about($columns = [], $with = [])
|
|
42
|
+
public static function about($columns = [], $with = []): Store|Network|null
|
|
33
43
|
{
|
|
34
44
|
$key = session('storefront_key');
|
|
35
45
|
|
|
@@ -38,7 +48,7 @@ class Storefront
|
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
if (is_array($columns)) {
|
|
41
|
-
$columns = array_merge(['uuid', 'public_id', 'company_uuid', 'backdrop_uuid', 'logo_uuid', 'name', 'description', 'translations', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'tags', 'currency', 'timezone', 'pod_method', 'options'], $columns);
|
|
51
|
+
$columns = array_merge(['uuid', 'public_id', 'company_uuid', 'backdrop_uuid', 'logo_uuid', 'order_config_uuid', 'name', 'description', 'translations', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'tags', 'currency', 'timezone', 'pod_method', 'options'], $columns);
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
if (Str::startsWith($key, 'store')) {
|
|
@@ -53,10 +63,16 @@ class Storefront
|
|
|
53
63
|
return $about;
|
|
54
64
|
}
|
|
55
65
|
|
|
56
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Returns current store or network based ID param passed
|
|
68
|
+
* with bare minimum columns, but can optionally pass in more columns to receive.
|
|
69
|
+
*
|
|
70
|
+
* @param array $columns
|
|
71
|
+
*/
|
|
72
|
+
public static function findAbout($id, $columns = [], $with = []): Store|Network|null
|
|
57
73
|
{
|
|
58
74
|
if (is_array($columns)) {
|
|
59
|
-
$columns = array_merge(['uuid', 'public_id', 'company_uuid', 'backdrop_uuid', 'logo_uuid', 'name', 'description', 'translations', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'tags', 'currency', 'timezone', 'pod_method', 'options'], $columns);
|
|
75
|
+
$columns = array_merge(['uuid', 'public_id', 'company_uuid', 'backdrop_uuid', 'logo_uuid', 'order_config_uuid', 'name', 'description', 'translations', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'tags', 'currency', 'timezone', 'pod_method', 'options'], $columns);
|
|
60
76
|
}
|
|
61
77
|
|
|
62
78
|
if (Str::startsWith($id, 'store')) {
|
|
@@ -204,57 +220,106 @@ class Storefront
|
|
|
204
220
|
return $stripeCustomer;
|
|
205
221
|
}
|
|
206
222
|
|
|
207
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Ensure an Order has an effective OrderConfig.
|
|
225
|
+
* - If already set (FK or relation), return it.
|
|
226
|
+
* - Else resolve default for the order's company and patch the FK quietly.
|
|
227
|
+
*
|
|
228
|
+
* @param \Fleetbase\Storefront\Models\Order $order
|
|
229
|
+
*
|
|
230
|
+
* @return \Fleetbase\Storefront\Models\OrderConfig|null
|
|
231
|
+
*/
|
|
232
|
+
public static function patchOrderConfig($order): ?OrderConfig
|
|
208
233
|
{
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
234
|
+
// FK fast-path
|
|
235
|
+
if (!empty($order->order_config_uuid)) {
|
|
236
|
+
return OrderConfig::where('uuid', $order->order_config_uuid)->first();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Relation fast-path (if your Order has relation 'orderConfig')
|
|
240
|
+
$order->loadMissing('orderConfig');
|
|
241
|
+
if ($order->orderConfig instanceof OrderConfig) {
|
|
242
|
+
return $order->orderConfig;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Resolve by company
|
|
246
|
+
$companyUuid = $order->company_uuid ?? ($order->company->uuid ?? null);
|
|
247
|
+
$orderConfig = static::getOrderConfig($companyUuid);
|
|
248
|
+
|
|
249
|
+
if ($orderConfig) {
|
|
250
|
+
// Quiet write to avoid events, if preferred
|
|
251
|
+
$order->forceFill(['order_config_uuid' => $orderConfig->uuid]);
|
|
252
|
+
method_exists($order, 'saveQuietly') ? $order->saveQuietly() : $order->save();
|
|
215
253
|
}
|
|
216
254
|
|
|
217
255
|
return $orderConfig;
|
|
218
256
|
}
|
|
219
257
|
|
|
220
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Get the default Storefront OrderConfig for the "current" company context.
|
|
260
|
+
*
|
|
261
|
+
* @return \Fleetbase\Storefront\Models\OrderConfig|null
|
|
262
|
+
*/
|
|
263
|
+
public static function getDefaultOrderConfig(): ?OrderConfig
|
|
221
264
|
{
|
|
222
|
-
$company = Auth::getCompany();
|
|
223
|
-
if ($company) {
|
|
224
|
-
return static::getOrderConfig($company);
|
|
225
|
-
}
|
|
265
|
+
$company = session('company') ?? (method_exists(Auth::class, 'getCompany') ? Auth::getCompany() : null);
|
|
226
266
|
|
|
227
|
-
return
|
|
267
|
+
return static::getOrderConfig($company);
|
|
228
268
|
}
|
|
229
269
|
|
|
230
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Get (or lazily create) the Storefront OrderConfig for a company.
|
|
272
|
+
* Accepts Company model, UUID string, or null (falls back to session company).
|
|
273
|
+
*
|
|
274
|
+
* @return \Fleetbase\Storefront\Models\OrderConfig|null
|
|
275
|
+
*/
|
|
276
|
+
public static function getOrderConfig(Company|string|null $company): ?OrderConfig
|
|
231
277
|
{
|
|
232
|
-
$
|
|
233
|
-
if (!$
|
|
234
|
-
|
|
278
|
+
$companyUuid = static::resolveCompanyUuid($company);
|
|
279
|
+
if (!$companyUuid) {
|
|
280
|
+
return null;
|
|
235
281
|
}
|
|
236
282
|
|
|
237
|
-
|
|
283
|
+
// Request-lifetime cache
|
|
284
|
+
if (isset(static::$configCache[$companyUuid])) {
|
|
285
|
+
return static::$configCache[$companyUuid];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
$attrs = [
|
|
289
|
+
'company_uuid' => $companyUuid,
|
|
290
|
+
'key' => self::CONFIG_KEY,
|
|
291
|
+
'namespace' => self::CONFIG_NS,
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
$config = OrderConfig::where($attrs)->first();
|
|
295
|
+
if (!$config) {
|
|
296
|
+
// Short transaction to avoid dupes under race
|
|
297
|
+
$config = DB::transaction(function () use ($attrs, $companyUuid) {
|
|
298
|
+
$existing = OrderConfig::where($attrs)->first();
|
|
299
|
+
if ($existing) {
|
|
300
|
+
return $existing;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return static::createStorefrontConfig($companyUuid);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return static::$configCache[$companyUuid] = $config;
|
|
238
308
|
}
|
|
239
309
|
|
|
240
310
|
/**
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
* This method checks if a storefront configuration (OrderConfig) already exists for the given company.
|
|
244
|
-
* If it exists, the method returns the existing configuration. Otherwise, it creates a new configuration with
|
|
245
|
-
* predefined settings for a storefront order process. The configuration includes various stages like 'created',
|
|
246
|
-
* 'started', 'canceled', 'completed', etc., each defined with specific attributes like key, code, color, logic,
|
|
247
|
-
* events, status, actions, details, and more. These stages help manage the order lifecycle in a storefront context.
|
|
311
|
+
* Create (or retrieve) the Storefront config for a company.
|
|
312
|
+
* Accepts Company model or UUID string.
|
|
248
313
|
*
|
|
249
|
-
* @
|
|
250
|
-
*
|
|
251
|
-
* @return OrderConfig the storefront order configuration associated with the specified company
|
|
314
|
+
* @return \Fleetbase\Storefront\Models\OrderConfig
|
|
252
315
|
*/
|
|
253
|
-
public static function createStorefrontConfig(Company $company): OrderConfig
|
|
316
|
+
public static function createStorefrontConfig(Company|string $company): OrderConfig
|
|
254
317
|
{
|
|
318
|
+
$companyUuid = $company instanceof Company ? $company->uuid : $company;
|
|
319
|
+
|
|
255
320
|
return OrderConfig::firstOrCreate(
|
|
256
321
|
[
|
|
257
|
-
'company_uuid' => $
|
|
322
|
+
'company_uuid' => $companyUuid,
|
|
258
323
|
'key' => 'storefront',
|
|
259
324
|
'namespace' => 'system:order-config:storefront',
|
|
260
325
|
],
|
|
@@ -343,7 +408,7 @@ class Storefront
|
|
|
343
408
|
'require_pod' => false,
|
|
344
409
|
],
|
|
345
410
|
'picked_up' => [
|
|
346
|
-
'key' => '
|
|
411
|
+
'key' => 'picked_up',
|
|
347
412
|
'code' => 'picked_up',
|
|
348
413
|
'color' => '#1f2937',
|
|
349
414
|
'logic' => [],
|
|
@@ -483,4 +548,185 @@ class Storefront
|
|
|
483
548
|
]
|
|
484
549
|
);
|
|
485
550
|
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Normalize a Company|UUID|null to a UUID string (or null if unresolved).
|
|
554
|
+
*/
|
|
555
|
+
protected static function resolveCompanyUuid(Company|string|null $company): ?string
|
|
556
|
+
{
|
|
557
|
+
if ($company instanceof Company && !empty($company->uuid)) {
|
|
558
|
+
return (string) $company->uuid;
|
|
559
|
+
}
|
|
560
|
+
if (is_string($company) && $company !== '' && (method_exists(Str::class, 'isUuid') ? Str::isUuid($company) : true)) {
|
|
561
|
+
return $company;
|
|
562
|
+
}
|
|
563
|
+
$sessionCompany = session('company');
|
|
564
|
+
|
|
565
|
+
return !empty($sessionCompany) ? (string) $sessionCompany : null;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
public static function createAcceptedActivity(?OrderConfig $orderConfig = null): Activity
|
|
569
|
+
{
|
|
570
|
+
return new Activity([
|
|
571
|
+
'key' => 'accepted',
|
|
572
|
+
'code' => 'accepted',
|
|
573
|
+
'color' => '#1f2937',
|
|
574
|
+
'logic' => [],
|
|
575
|
+
'events' => [],
|
|
576
|
+
'status' => 'Order has been accepted',
|
|
577
|
+
'actions' => [],
|
|
578
|
+
'details' => 'Order has been accepted by {storefront.name}',
|
|
579
|
+
'options' => [],
|
|
580
|
+
'complete' => false,
|
|
581
|
+
'entities' => [],
|
|
582
|
+
'sequence' => 0,
|
|
583
|
+
'activities' => ['dispatched'],
|
|
584
|
+
'internalId' => Str::uuid(),
|
|
585
|
+
'pod_method' => 'scan',
|
|
586
|
+
'require_pod' => false,
|
|
587
|
+
], $orderConfig ? $orderConfig->activities()->toArray() : []);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
public static function autoAcceptOrder(Order $order)
|
|
591
|
+
{
|
|
592
|
+
// Patch order config
|
|
593
|
+
$orderConfig = static::patchOrderConfig($order);
|
|
594
|
+
$activity = static::createAcceptedActivity($orderConfig);
|
|
595
|
+
|
|
596
|
+
// Dispatch already if order is a pickup
|
|
597
|
+
if ($order->isMeta('is_pickup')) {
|
|
598
|
+
$order->firstDispatchWithActivity();
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Set order as accepted
|
|
602
|
+
try {
|
|
603
|
+
$order->setStatus($activity->code);
|
|
604
|
+
$order->insertActivity($activity, $order->getLastLocation());
|
|
605
|
+
} catch (\Exception $e) {
|
|
606
|
+
Log::debug('[Storefront] was unable to accept an order.', ['order' => $order, 'activity' => $activity]);
|
|
607
|
+
|
|
608
|
+
return response()->error('Unable to accept order.');
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Notify customer order was accepted
|
|
612
|
+
try {
|
|
613
|
+
$order->customer->notify(new StorefrontOrderAccepted($order));
|
|
614
|
+
} catch (\Exception $e) {
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return $order;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
public static function autoDispatchOrder(Order $order, bool $adhoc = true)
|
|
621
|
+
{
|
|
622
|
+
// Patch order config
|
|
623
|
+
Storefront::patchOrderConfig($order);
|
|
624
|
+
|
|
625
|
+
if ($order->isMeta('is_pickup')) {
|
|
626
|
+
$order->updateStatus('pickup_ready');
|
|
627
|
+
|
|
628
|
+
return $order;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// toggle order to adhoc
|
|
632
|
+
if ($adhoc === true) {
|
|
633
|
+
$order->update(['adhoc' => true]);
|
|
634
|
+
} else {
|
|
635
|
+
// Find nearest driver and assign
|
|
636
|
+
$driver = $order->findClosestDrivers()->first();
|
|
637
|
+
if ($driver) {
|
|
638
|
+
$order->assignDriver($driver);
|
|
639
|
+
} else {
|
|
640
|
+
// no driver available to make adhoc
|
|
641
|
+
$order->update(['adhoc' => true]);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
$order->dispatchWithActivity();
|
|
646
|
+
|
|
647
|
+
return $order;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Determine whether the given Store or Network has a configured notification channel.
|
|
652
|
+
*
|
|
653
|
+
* Accepts either a model instance (Store|Network) or an identifier string.
|
|
654
|
+
* When a string is provided, this method attempts to resolve it in the following order:
|
|
655
|
+
* 1) UUID → `where('uuid', $id)`
|
|
656
|
+
* 2) public_id → `where('public_id', $id)` (if your models use a public_id column)
|
|
657
|
+
*
|
|
658
|
+
* If resolution fails, the method returns false.
|
|
659
|
+
*
|
|
660
|
+
* @param Store|\Fleetbase\FleetOps\Models\Network|string|null $subject
|
|
661
|
+
* Store/Network model instance or identifier (uuid/public_id)
|
|
662
|
+
* @param string $channel Channel scheme/key (e.g., "email", "sms", "fcm").
|
|
663
|
+
*
|
|
664
|
+
* @return bool true if a NotificationChannel exists for the subject and scheme; otherwise false
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* YourClass::hasNotificationChannelConfigures($store, 'email');
|
|
668
|
+
* YourClass::hasNotificationChannelConfigures($networkUuid, 'fcm');
|
|
669
|
+
* YourClass::hasNotificationChannelConfigures('STO-12345', 'sms'); // public_id example
|
|
670
|
+
*
|
|
671
|
+
* @note The method name appears to have a typo; consider renaming to:
|
|
672
|
+
* hasNotificationChannelConfigured() and keeping this as a BC alias.
|
|
673
|
+
*/
|
|
674
|
+
public static function hasNotificationChannelConfigured(Store|Network|string|null $subject, string $channel): bool
|
|
675
|
+
{
|
|
676
|
+
$model = self::resolveSubjectToModel($subject);
|
|
677
|
+
|
|
678
|
+
if (!$model) {
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return NotificationChannel::query()
|
|
683
|
+
->where('owner_uuid', $model->uuid)
|
|
684
|
+
->where('scheme', $channel)
|
|
685
|
+
->exists();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Resolve the provided subject into a Store or Network model.
|
|
690
|
+
*
|
|
691
|
+
* Attempts resolution by uuid first, then by public_id (if present).
|
|
692
|
+
* Returns null if no matching model can be found.
|
|
693
|
+
*
|
|
694
|
+
* @param Store|\Fleetbase\FleetOps\Models\Network|string|null $subject
|
|
695
|
+
*
|
|
696
|
+
* @return Store|\Fleetbase\FleetOps\Models\Network|null
|
|
697
|
+
*/
|
|
698
|
+
protected static function resolveSubjectToModel(Store|Network|string|null $subject): Store|Network|null
|
|
699
|
+
{
|
|
700
|
+
if ($subject instanceof Store || $subject instanceof Network) {
|
|
701
|
+
return $subject;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (!is_string($subject) || $subject === '') {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Try UUID
|
|
709
|
+
if (Str::isUuid($subject)) {
|
|
710
|
+
if ($found = Store::query()->where('uuid', $subject)->first()) {
|
|
711
|
+
return $found;
|
|
712
|
+
}
|
|
713
|
+
if ($found = Network::query()->where('uuid', $subject)->first()) {
|
|
714
|
+
return $found;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Try public_id (optional; remove if you don't use it)
|
|
719
|
+
if (property_exists(Store::class, 'public_id') || Schema::hasColumn((new Store())->getTable(), 'public_id')) {
|
|
720
|
+
if ($found = Store::query()->where('public_id', $subject)->first()) {
|
|
721
|
+
return $found;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (property_exists(Network::class, 'public_id') || Schema::hasColumn((new Network())->getTable(), 'public_id')) {
|
|
725
|
+
if ($found = Network::query()->where('public_id', $subject)->first()) {
|
|
726
|
+
return $found;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
486
732
|
}
|
package/server/src/routes.php
CHANGED
|
@@ -161,6 +161,7 @@ Route::prefix(config('storefront.api.routing.prefix', 'storefront'))->namespace(
|
|
|
161
161
|
function ($router, $controller) {
|
|
162
162
|
$router->post('accept', $controller('acceptOrder'));
|
|
163
163
|
$router->post('ready', $controller('markOrderAsReady'));
|
|
164
|
+
$router->post('preparing', $controller('markOrderAsPreparing'));
|
|
164
165
|
$router->post('completed', $controller('markOrderAsCompleted'));
|
|
165
166
|
}
|
|
166
167
|
);
|