@fleetbase/storefront-engine 0.3.31 → 0.4.1
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/customer-panel/orders.js +1 -1
- package/addon/components/modals/create-gateway.hbs +33 -12
- package/addon/components/modals/share-network.hbs +1 -1
- package/addon/components/network-category-picker.hbs +2 -1
- package/addon/components/network-category-picker.js +52 -22
- 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 +30 -9
- package/addon/components/widget/orders.js +7 -1
- package/addon/components/widget/storefront-key-metrics.js +3 -3
- package/addon/components/widget/storefront-metrics.hbs +11 -1
- package/addon/components/widget/storefront-metrics.js +103 -1
- package/addon/controllers/customers/index.js +2 -2
- package/addon/controllers/networks/index/network/index.js +2 -0
- package/addon/controllers/networks/index/network/orders.js +1 -2
- package/addon/controllers/networks/index/network/stores.js +68 -64
- package/addon/controllers/networks/index.js +1 -2
- package/addon/controllers/products/index/category.js +1 -2
- package/addon/controllers/products/index/index.js +1 -2
- package/addon/controllers/settings/gateways.js +6 -1
- package/addon/controllers/settings/index.js +1 -0
- package/addon/controllers/settings/notifications.js +3 -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/networks/index/network/stores.js +6 -5
- package/addon/routes/settings/index.js +5 -0
- package/addon/services/order-actions.js +31 -0
- package/addon/styles/storefront-engine.css +29 -0
- package/addon/templates/networks/index/network/index.hbs +13 -0
- package/addon/templates/networks/index/network/stores.hbs +15 -1
- package/addon/templates/networks/index.hbs +1 -1
- 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 +2 -3
- package/server/migrations/2025_09_01_041353_add_default_order_config_column.php +110 -0
- package/server/src/Console/Commands/MigrateStripeSandboxCustomers.php +150 -0
- package/server/src/Expansions/OrderExpansion.php +43 -0
- package/server/src/Http/Controllers/NetworkController.php +20 -3
- package/server/src/Http/Controllers/OrderController.php +62 -9
- package/server/src/Http/Controllers/v1/CheckoutController.php +45 -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/Store.php +36 -35
- package/server/src/Models/Customer.php +18 -2
- package/server/src/Models/FoodTruck.php +15 -0
- package/server/src/Models/Network.php +65 -2
- package/server/src/Models/Store.php +73 -10
- package/server/src/Notifications/StorefrontOrderAccepted.php +154 -0
- 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 +13 -4
- package/server/src/Support/Storefront.php +199 -37
- package/server/src/routes.php +2 -0
- package/translations/en-us.yaml +8 -0
|
@@ -20,41 +20,42 @@ class Store extends FleetbaseResource
|
|
|
20
20
|
$currency = $this->currency ?? 'USD';
|
|
21
21
|
|
|
22
22
|
return [
|
|
23
|
-
'id'
|
|
24
|
-
'uuid'
|
|
25
|
-
'public_id'
|
|
26
|
-
'key'
|
|
27
|
-
'company_uuid'
|
|
28
|
-
'created_by_uuid'
|
|
29
|
-
'logo_uuid'
|
|
30
|
-
'backdrop_uuid'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
23
|
+
'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
|
|
24
|
+
'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
|
|
25
|
+
'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
|
|
26
|
+
'key' => $this->when(Http::isInternalRequest(), $this->key),
|
|
27
|
+
'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
|
|
28
|
+
'created_by_uuid' => $this->when(Http::isInternalRequest(), $this->created_by_uuid),
|
|
29
|
+
'logo_uuid' => $this->when(Http::isInternalRequest(), $this->logo_uuid),
|
|
30
|
+
'backdrop_uuid' => $this->when(Http::isInternalRequest(), $this->backdrop_uuid),
|
|
31
|
+
'order_config_uuid' => $this->when(Http::isInternalRequest(), $this->order_config_uuid),
|
|
32
|
+
'name' => $this->name,
|
|
33
|
+
'description' => $this->description,
|
|
34
|
+
'translations' => $this->translations ?? [],
|
|
35
|
+
'website' => $this->website,
|
|
36
|
+
'facebook' => $this->facebook,
|
|
37
|
+
'instagram' => $this->instagram,
|
|
38
|
+
'twitter' => $this->twitter,
|
|
39
|
+
'email' => $this->email,
|
|
40
|
+
'phone' => $this->phone,
|
|
41
|
+
'tags' => $this->tags ?? [],
|
|
42
|
+
'currency' => $currency,
|
|
43
|
+
'country' => Utils::getCountryCodeByCurrency($currency, 'US'),
|
|
44
|
+
'options' => $this->formatOptions($this->options),
|
|
45
|
+
'logo_url' => $this->logo_url,
|
|
46
|
+
'backdrop_url' => $this->backdrop_url,
|
|
47
|
+
'rating' => $this->rating,
|
|
48
|
+
'online' => $this->online,
|
|
49
|
+
'alertable' => $this->alertable,
|
|
50
|
+
'is_network' => false,
|
|
51
|
+
'is_store' => true,
|
|
52
|
+
'category' => $this->when($request->filled('network') && ($request->has('with_category') || $request->inArray('with', 'category')), new Category($this->getNetworkCategoryUsingId($request->input('network')))),
|
|
53
|
+
'networks' => $this->when($request->boolean('with_networks') || $request->inArray('with', 'networks'), Network::collection($this->networks)),
|
|
54
|
+
'locations' => $this->when($request->boolean('with_locations') || $request->inArray('with', 'locations'), $this->locations->mapInto(StoreLocation::class)),
|
|
55
|
+
'media' => $this->when($request->boolean('with_media') || $request->inArray('with', 'media'), Media::collection($this->media)),
|
|
56
|
+
'slug' => $this->slug,
|
|
57
|
+
'created_at' => $this->created_at,
|
|
58
|
+
'updated_at' => $this->updated_at,
|
|
58
59
|
];
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\Storefront\Models;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Models\Contact;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
6
7
|
use Illuminate\Support\Str;
|
|
7
8
|
|
|
8
9
|
class Customer extends Contact
|
|
@@ -12,6 +13,22 @@ class Customer extends Contact
|
|
|
12
13
|
*/
|
|
13
14
|
protected string $payloadKey = 'customer';
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Boot the model.
|
|
18
|
+
*/
|
|
19
|
+
protected static function boot()
|
|
20
|
+
{
|
|
21
|
+
parent::boot();
|
|
22
|
+
|
|
23
|
+
static::creating(function ($model) {
|
|
24
|
+
$model->type = 'customer';
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
static::addGlobalScope('type', function ($builder) {
|
|
28
|
+
$builder->where('type', 'customer');
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
/**
|
|
16
33
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
17
34
|
*/
|
|
@@ -61,10 +78,9 @@ class Customer extends Contact
|
|
|
61
78
|
*/
|
|
62
79
|
public function countStorefrontOrdersFrom($id)
|
|
63
80
|
{
|
|
64
|
-
return
|
|
81
|
+
return Order::where(
|
|
65
82
|
[
|
|
66
83
|
'customer_uuid' => $this->uuid,
|
|
67
|
-
'type' => 'storefront',
|
|
68
84
|
'meta->storefront_id' => $id,
|
|
69
85
|
]
|
|
70
86
|
)->count();
|
|
@@ -5,6 +5,7 @@ namespace Fleetbase\Storefront\Models;
|
|
|
5
5
|
use Fleetbase\FleetOps\Models\ServiceArea;
|
|
6
6
|
use Fleetbase\FleetOps\Models\Vehicle;
|
|
7
7
|
use Fleetbase\FleetOps\Models\Zone;
|
|
8
|
+
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
8
9
|
use Fleetbase\Storefront\Http\Resources\FoodTruck as FoodTruckResource;
|
|
9
10
|
use Fleetbase\Traits\HasApiModelBehavior;
|
|
10
11
|
use Fleetbase\Traits\HasPublicid;
|
|
@@ -61,6 +62,13 @@ class FoodTruck extends StorefrontModel
|
|
|
61
62
|
'status',
|
|
62
63
|
];
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Dynamic attributes that are appended to object.
|
|
67
|
+
*
|
|
68
|
+
* @var array
|
|
69
|
+
*/
|
|
70
|
+
protected $appends = ['location'];
|
|
71
|
+
|
|
64
72
|
/**
|
|
65
73
|
* Get the store that owns this food truck.
|
|
66
74
|
*/
|
|
@@ -179,4 +187,11 @@ class FoodTruck extends StorefrontModel
|
|
|
179
187
|
|
|
180
188
|
return $this;
|
|
181
189
|
}
|
|
190
|
+
|
|
191
|
+
public function getLocationAttribute(): ?Point
|
|
192
|
+
{
|
|
193
|
+
$this->loadMissing('vehicle');
|
|
194
|
+
|
|
195
|
+
return $this->vehicle->location;
|
|
196
|
+
}
|
|
182
197
|
}
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
namespace Fleetbase\Storefront\Models;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\Casts\Json;
|
|
6
|
+
use Fleetbase\FleetOps\Models\OrderConfig;
|
|
6
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
7
8
|
use Fleetbase\Models\Category;
|
|
8
9
|
use Fleetbase\Models\Company;
|
|
9
10
|
use Fleetbase\Models\File;
|
|
10
11
|
use Fleetbase\Models\Invite;
|
|
11
12
|
use Fleetbase\Models\User;
|
|
13
|
+
use Fleetbase\Storefront\Support\Storefront;
|
|
12
14
|
use Fleetbase\Traits\HasApiModelBehavior;
|
|
13
15
|
use Fleetbase\Traits\HasOptionsAttributes;
|
|
14
16
|
use Fleetbase\Traits\HasPublicid;
|
|
@@ -53,7 +55,7 @@ class Network extends StorefrontModel
|
|
|
53
55
|
*
|
|
54
56
|
* @var array
|
|
55
57
|
*/
|
|
56
|
-
protected $fillable = ['created_by_uuid', 'company_uuid', 'logo_uuid', 'backdrop_uuid', 'key', 'online', 'name', 'description', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'translations', 'tags', 'currency', 'timezone', 'pod_method', 'options', 'alertable', 'slug'];
|
|
58
|
+
protected $fillable = ['created_by_uuid', 'company_uuid', 'logo_uuid', 'backdrop_uuid', 'order_config_uuid', 'key', 'online', 'name', 'description', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'translations', 'tags', 'currency', 'timezone', 'pod_method', 'options', 'alertable', 'slug'];
|
|
57
59
|
|
|
58
60
|
/**
|
|
59
61
|
* The attributes that should be cast to native types.
|
|
@@ -133,6 +135,14 @@ class Network extends StorefrontModel
|
|
|
133
135
|
return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(File::class);
|
|
134
136
|
}
|
|
135
137
|
|
|
138
|
+
/**
|
|
139
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
140
|
+
*/
|
|
141
|
+
public function orderConfig()
|
|
142
|
+
{
|
|
143
|
+
return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(OrderConfig::class);
|
|
144
|
+
}
|
|
145
|
+
|
|
136
146
|
/**
|
|
137
147
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
138
148
|
*/
|
|
@@ -156,7 +166,8 @@ class Network extends StorefrontModel
|
|
|
156
166
|
{
|
|
157
167
|
return $this->belongsToMany(Store::class, 'network_stores', 'network_uuid', 'store_uuid')
|
|
158
168
|
->using(NetworkStore::class)
|
|
159
|
-
->withPivot('category_uuid')
|
|
169
|
+
->withPivot(['category_uuid', 'deleted_at'])
|
|
170
|
+
->wherePivotNull('deleted_at');
|
|
160
171
|
}
|
|
161
172
|
|
|
162
173
|
/**
|
|
@@ -286,4 +297,56 @@ class Network extends StorefrontModel
|
|
|
286
297
|
|
|
287
298
|
return $this->createCategory($name, $description, $meta, $translations, $parent, $icon, $iconColor);
|
|
288
299
|
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Get the effective OrderConfig for this model.
|
|
303
|
+
*
|
|
304
|
+
* Loads the related `orderConfig` if missing and returns it.
|
|
305
|
+
* If no specific config is set, it falls back to the global default
|
|
306
|
+
* and memoizes that default into the relation (no DB write; just the relation cache)
|
|
307
|
+
* to avoid repeat lookups in the same request lifecycle.
|
|
308
|
+
*
|
|
309
|
+
* @throws \RuntimeException if no default OrderConfig is available (should not happen in a healthy install)
|
|
310
|
+
*/
|
|
311
|
+
public function getOrderConfig(): OrderConfig
|
|
312
|
+
{
|
|
313
|
+
// Ensure relation is loaded once
|
|
314
|
+
$this->loadMissing('orderConfig');
|
|
315
|
+
|
|
316
|
+
if ($this->orderConfig instanceof OrderConfig) {
|
|
317
|
+
return $this->orderConfig;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Fallback to default (must exist in your environment)
|
|
321
|
+
$default = Storefront::getDefaultOrderConfig();
|
|
322
|
+
|
|
323
|
+
if (!$default instanceof OrderConfig) {
|
|
324
|
+
// Keep your hard return type contract honest
|
|
325
|
+
throw new \RuntimeException('No default OrderConfig is configured.');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Memoize the fallback in the in-memory relation to avoid repeated lookups
|
|
329
|
+
$this->setRelation('orderConfig', $default);
|
|
330
|
+
|
|
331
|
+
return $default;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get the UUID of the effective OrderConfig for this model.
|
|
336
|
+
*
|
|
337
|
+
* Tries to use the local FK if present to avoid loading the relation;
|
|
338
|
+
* otherwise, resolves via getOrderConfig() (which memoizes).
|
|
339
|
+
*
|
|
340
|
+
* @return string non-empty UUID of the effective order config
|
|
341
|
+
*/
|
|
342
|
+
public function getOrderConfigId(): string
|
|
343
|
+
{
|
|
344
|
+
// Fast path: if the FK column is set, use it directly
|
|
345
|
+
if (!empty($this->order_config_uuid)) {
|
|
346
|
+
return (string) $this->order_config_uuid;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Otherwise rely on the resolved config (relation or default)
|
|
350
|
+
return (string) $this->getOrderConfig()->uuid;
|
|
351
|
+
}
|
|
289
352
|
}
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
namespace Fleetbase\Storefront\Models;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\Casts\Json;
|
|
6
|
+
use Fleetbase\FleetOps\Models\OrderConfig;
|
|
6
7
|
use Fleetbase\FleetOps\Models\Place;
|
|
7
8
|
use Fleetbase\Models\Category;
|
|
8
9
|
use Fleetbase\Models\Company;
|
|
9
10
|
use Fleetbase\Models\File;
|
|
10
11
|
use Fleetbase\Models\User;
|
|
12
|
+
use Fleetbase\Storefront\Support\Storefront;
|
|
11
13
|
use Fleetbase\Support\Utils as FleetbaseUtils;
|
|
12
14
|
use Fleetbase\Traits\HasApiModelBehavior;
|
|
13
15
|
use Fleetbase\Traits\HasMetaAttributes;
|
|
@@ -56,7 +58,7 @@ class Store extends StorefrontModel
|
|
|
56
58
|
*
|
|
57
59
|
* @var array
|
|
58
60
|
*/
|
|
59
|
-
protected $fillable = ['created_by_uuid', 'company_uuid', 'logo_uuid', 'backdrop_uuid', 'key', 'online', 'name', 'description', 'translations', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'tags', 'currency', 'meta', 'timezone', 'pod_method', 'options', 'alertable', 'slug'];
|
|
61
|
+
protected $fillable = ['created_by_uuid', 'company_uuid', 'logo_uuid', 'backdrop_uuid', 'order_config_uuid', 'key', 'online', 'name', 'description', 'translations', 'website', 'facebook', 'instagram', 'twitter', 'email', 'phone', 'tags', 'currency', 'meta', 'timezone', 'pod_method', 'options', 'alertable', 'slug'];
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
64
|
* The attributes that should be cast to native types.
|
|
@@ -137,6 +139,22 @@ class Store extends StorefrontModel
|
|
|
137
139
|
return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(File::class);
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
/**
|
|
143
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
144
|
+
*/
|
|
145
|
+
public function backdrop()
|
|
146
|
+
{
|
|
147
|
+
return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(File::class);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
152
|
+
*/
|
|
153
|
+
public function orderConfig()
|
|
154
|
+
{
|
|
155
|
+
return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(OrderConfig::class);
|
|
156
|
+
}
|
|
157
|
+
|
|
140
158
|
/**
|
|
141
159
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
142
160
|
*/
|
|
@@ -167,14 +185,6 @@ class Store extends StorefrontModel
|
|
|
167
185
|
)->where('type', 'storefront_store_media');
|
|
168
186
|
}
|
|
169
187
|
|
|
170
|
-
/**
|
|
171
|
-
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
172
|
-
*/
|
|
173
|
-
public function backdrop()
|
|
174
|
-
{
|
|
175
|
-
return $this->setConnection(config('fleetbase.connection.db'))->belongsTo(File::class);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
188
|
/**
|
|
179
189
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
180
190
|
*/
|
|
@@ -278,7 +288,8 @@ class Store extends StorefrontModel
|
|
|
278
288
|
{
|
|
279
289
|
return $this->belongsToMany(Network::class, 'network_stores', 'store_uuid', 'network_uuid')
|
|
280
290
|
->using(NetworkStore::class)
|
|
281
|
-
->withPivot('category_uuid')
|
|
291
|
+
->withPivot(['category_uuid', 'deleted_at'])
|
|
292
|
+
->wherePivotNull('deleted_at');
|
|
282
293
|
}
|
|
283
294
|
|
|
284
295
|
/**
|
|
@@ -459,4 +470,56 @@ class Store extends StorefrontModel
|
|
|
459
470
|
|
|
460
471
|
return null;
|
|
461
472
|
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Get the effective OrderConfig for this model.
|
|
476
|
+
*
|
|
477
|
+
* Loads the related `orderConfig` if missing and returns it.
|
|
478
|
+
* If no specific config is set, it falls back to the global default
|
|
479
|
+
* and memoizes that default into the relation (no DB write; just the relation cache)
|
|
480
|
+
* to avoid repeat lookups in the same request lifecycle.
|
|
481
|
+
*
|
|
482
|
+
* @throws \RuntimeException if no default OrderConfig is available (should not happen in a healthy install)
|
|
483
|
+
*/
|
|
484
|
+
public function getOrderConfig(): OrderConfig
|
|
485
|
+
{
|
|
486
|
+
// Ensure relation is loaded once
|
|
487
|
+
$this->loadMissing('orderConfig');
|
|
488
|
+
|
|
489
|
+
if ($this->orderConfig instanceof OrderConfig) {
|
|
490
|
+
return $this->orderConfig;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Fallback to default (must exist in your environment)
|
|
494
|
+
$default = Storefront::getDefaultOrderConfig();
|
|
495
|
+
|
|
496
|
+
if (!$default instanceof OrderConfig) {
|
|
497
|
+
// Keep your hard return type contract honest
|
|
498
|
+
throw new \RuntimeException('No default OrderConfig is configured.');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Memoize the fallback in the in-memory relation to avoid repeated lookups
|
|
502
|
+
$this->setRelation('orderConfig', $default);
|
|
503
|
+
|
|
504
|
+
return $default;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Get the UUID of the effective OrderConfig for this model.
|
|
509
|
+
*
|
|
510
|
+
* Tries to use the local FK if present to avoid loading the relation;
|
|
511
|
+
* otherwise, resolves via getOrderConfig() (which memoizes).
|
|
512
|
+
*
|
|
513
|
+
* @return string non-empty UUID of the effective order config
|
|
514
|
+
*/
|
|
515
|
+
public function getOrderConfigId(): string
|
|
516
|
+
{
|
|
517
|
+
// Fast path: if the FK column is set, use it directly
|
|
518
|
+
if (!empty($this->order_config_uuid)) {
|
|
519
|
+
return (string) $this->order_config_uuid;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Otherwise rely on the resolved config (relation or default)
|
|
523
|
+
return (string) $this->getOrderConfig()->uuid;
|
|
524
|
+
}
|
|
462
525
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\Storefront\Notifications;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
6
|
+
use Fleetbase\Storefront\Models\Network;
|
|
7
|
+
use Fleetbase\Storefront\Models\Store;
|
|
8
|
+
use Fleetbase\Storefront\Support\PushNotification;
|
|
9
|
+
use Fleetbase\Storefront\Support\Storefront;
|
|
10
|
+
use Illuminate\Bus\Queueable;
|
|
11
|
+
use Illuminate\Notifications\Messages\MailMessage;
|
|
12
|
+
use Illuminate\Notifications\Notification;
|
|
13
|
+
use NotificationChannels\Apn\ApnChannel;
|
|
14
|
+
use NotificationChannels\Fcm\FcmChannel;
|
|
15
|
+
|
|
16
|
+
class StorefrontOrderAccepted extends Notification
|
|
17
|
+
{
|
|
18
|
+
use Queueable;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The order instance this notification is for.
|
|
22
|
+
*/
|
|
23
|
+
public Order $order;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The order instance this notification is for.
|
|
27
|
+
*/
|
|
28
|
+
public Store|Network $storefront;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The time the notification was sent.
|
|
32
|
+
*/
|
|
33
|
+
public string $sentAt;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The ID of the notification.
|
|
37
|
+
*/
|
|
38
|
+
public string $notificationId;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The notification subject.
|
|
42
|
+
*/
|
|
43
|
+
public string $subject;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The notification body.
|
|
47
|
+
*/
|
|
48
|
+
public string $body;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The notification order status.
|
|
52
|
+
*/
|
|
53
|
+
public string $status;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a new notification instance.
|
|
57
|
+
*
|
|
58
|
+
* @return void
|
|
59
|
+
*/
|
|
60
|
+
public function __construct(Order $order)
|
|
61
|
+
{
|
|
62
|
+
$this->order = $order;
|
|
63
|
+
$this->storefront = Storefront::findAbout($order->getMeta('storefront_id'));
|
|
64
|
+
$this->sentAt = now()->toDateTimeString();
|
|
65
|
+
$this->notificationId = uniqid('notification_');
|
|
66
|
+
|
|
67
|
+
$this->subject = 'Your order from ' . $this->storefront->name . ' has been accepted.';
|
|
68
|
+
$this->body = 'Your order was accepted.';
|
|
69
|
+
$this->status = 'order_accepted';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the notification's delivery channels.
|
|
74
|
+
*
|
|
75
|
+
* @return array
|
|
76
|
+
*/
|
|
77
|
+
public function via($notifiable)
|
|
78
|
+
{
|
|
79
|
+
return ['mail', 'database', FcmChannel::class, ApnChannel::class];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the mail representation of the notification.
|
|
84
|
+
*
|
|
85
|
+
* @return MailMessage
|
|
86
|
+
*/
|
|
87
|
+
public function toMail($notifiable)
|
|
88
|
+
{
|
|
89
|
+
$message = (new MailMessage())
|
|
90
|
+
->subject($this->subject)
|
|
91
|
+
->line($this->body);
|
|
92
|
+
|
|
93
|
+
// $message->action('View Details', Utils::consoleUrl('', ['shift' => 'fleet-ops/orders/view/' . $this->order->public_id]));
|
|
94
|
+
|
|
95
|
+
return $message;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get the firebase cloud message representation of the notification.
|
|
100
|
+
*
|
|
101
|
+
* @return \NotificationChannels\Fcm\FcmMessage
|
|
102
|
+
*/
|
|
103
|
+
public function toFcm($notifiable)
|
|
104
|
+
{
|
|
105
|
+
return PushNotification::createFcmMessage(
|
|
106
|
+
$this->order,
|
|
107
|
+
$this->subject,
|
|
108
|
+
$this->body,
|
|
109
|
+
$this->status,
|
|
110
|
+
$notifiable
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the apns message representation of the notification.
|
|
116
|
+
*
|
|
117
|
+
* @return \NotificationChannels\Apn\ApnMessage
|
|
118
|
+
*/
|
|
119
|
+
public function toApn($notifiable)
|
|
120
|
+
{
|
|
121
|
+
return PushNotification::createApnMessage(
|
|
122
|
+
$this->order,
|
|
123
|
+
$this->subject,
|
|
124
|
+
$this->body,
|
|
125
|
+
$this->status,
|
|
126
|
+
$notifiable
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the array representation of the notification.
|
|
132
|
+
*/
|
|
133
|
+
public function toArray($notifiable): array
|
|
134
|
+
{
|
|
135
|
+
$this->order->loadMissing(['customer', 'company']);
|
|
136
|
+
$customer = $this->order->customer;
|
|
137
|
+
$company = $this->order->company;
|
|
138
|
+
|
|
139
|
+
return [
|
|
140
|
+
'notifiable' => $notifiable->public_id,
|
|
141
|
+
'notification_id' => $this->notificationId,
|
|
142
|
+
'sent_at' => $this->sentAt,
|
|
143
|
+
'subject' => $this->subject,
|
|
144
|
+
'message' => $this->status,
|
|
145
|
+
'storefront' => $this->storefront->name,
|
|
146
|
+
'storefront_id' => $this->storefront->public_id,
|
|
147
|
+
'id' => $customer->public_id,
|
|
148
|
+
'email' => $customer->email,
|
|
149
|
+
'phone' => $customer->phone,
|
|
150
|
+
'companyId' => $company->public_id,
|
|
151
|
+
'company' => $company->name,
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -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,13 @@ 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
|
+
return null;
|
|
26
|
+
}
|
|
24
27
|
|
|
25
28
|
return ApnMessage::create()
|
|
26
29
|
->badge(1)
|
|
@@ -33,10 +36,13 @@ class PushNotification
|
|
|
33
36
|
->via($client);
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
public static function createFcmMessage(Order $order, string $title, string $body, string $status, $notifiable = null): FcmMessage
|
|
39
|
+
public static function createFcmMessage(Order $order, string $title, string $body, string $status, $notifiable = null): ?FcmMessage
|
|
37
40
|
{
|
|
38
41
|
$storefront = static::getStorefrontFromOrder($order);
|
|
39
42
|
$notificationChannel = static::getNotificationChannel('apn', $storefront, $order);
|
|
43
|
+
if (!$notificationChannel) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
40
46
|
|
|
41
47
|
// Configure FCM
|
|
42
48
|
static::configureFcm($notificationChannel);
|
|
@@ -97,7 +103,7 @@ class PushNotification
|
|
|
97
103
|
return $firebaseConfig;
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
public static function getNotificationChannel(string $scheme, Network|Store $storefront, ?Order $order = null): NotificationChannel
|
|
106
|
+
public static function getNotificationChannel(string $scheme, Network|Store $storefront, ?Order $order = null): ?NotificationChannel
|
|
101
107
|
{
|
|
102
108
|
if ($order && $order->hasMeta('storefront_notification_channel')) {
|
|
103
109
|
return NotificationChannel::where([
|
|
@@ -113,9 +119,12 @@ class PushNotification
|
|
|
113
119
|
])->first();
|
|
114
120
|
}
|
|
115
121
|
|
|
116
|
-
public static function getApnClient(Network|Store $storefront, ?Order $order = null): PushOkClient
|
|
122
|
+
public static function getApnClient(Network|Store $storefront, ?Order $order = null): ?PushOkClient
|
|
117
123
|
{
|
|
118
124
|
$notificationChannel = static::getNotificationChannel('apn', $storefront, $order);
|
|
125
|
+
if (!$notificationChannel) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
119
128
|
$config = (array) $notificationChannel->config;
|
|
120
129
|
|
|
121
130
|
$isProductionEnv = Utils::castBoolean(data_get($config, 'production', app()->isProduction()));
|