@fleetbase/storefront-engine 0.1.7 → 0.1.8

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.
Files changed (307) hide show
  1. package/.php-cs-fixer.php +29 -0
  2. package/LICENSE.md +16 -4
  3. package/README.md +106 -13
  4. package/app/adapters/addon-category.js +1 -1
  5. package/app/adapters/gateway.js +1 -1
  6. package/app/adapters/network.js +1 -1
  7. package/app/adapters/notification-channel.js +1 -1
  8. package/app/adapters/product-addon-category.js +1 -1
  9. package/app/adapters/product-addon.js +1 -1
  10. package/app/adapters/product-hour.js +1 -1
  11. package/app/adapters/product-store-location.js +1 -1
  12. package/app/adapters/product-variant-option.js +1 -1
  13. package/app/adapters/product-variant.js +1 -1
  14. package/app/adapters/product.js +1 -1
  15. package/app/adapters/store-hour.js +1 -1
  16. package/app/adapters/store-location.js +1 -1
  17. package/app/adapters/store.js +1 -1
  18. package/app/adapters/storefront.js +1 -1
  19. package/app/components/file-record.js +1 -1
  20. package/app/components/modals/add-store-hours.js +1 -1
  21. package/app/components/modals/add-store-to-category.js +1 -1
  22. package/app/components/modals/add-stores-to-network.js +1 -1
  23. package/app/components/modals/assign-driver.js +1 -1
  24. package/app/components/modals/create-first-store.js +1 -1
  25. package/app/components/modals/create-gateway.js +1 -1
  26. package/app/components/modals/create-network-category.js +1 -1
  27. package/app/components/modals/create-network.js +1 -1
  28. package/app/components/modals/create-new-variant.js +1 -1
  29. package/app/components/modals/create-notification-channel.js +1 -1
  30. package/app/components/modals/create-product-category.js +1 -1
  31. package/app/components/modals/create-store.js +1 -1
  32. package/app/components/modals/edit-network.js +1 -1
  33. package/app/components/modals/import-products.js +1 -1
  34. package/app/components/modals/incoming-order.js +1 -1
  35. package/app/components/modals/manage-addons.js +1 -1
  36. package/app/components/modals/order-ready-assign-driver.js +1 -1
  37. package/app/components/modals/select-addon-category.js +1 -1
  38. package/app/components/modals/share-network.js +1 -1
  39. package/app/components/modals/store-details.js +1 -1
  40. package/app/components/modals/store-form.js +1 -1
  41. package/app/components/modals/store-location-form.js +1 -1
  42. package/app/components/network-category-picker.js +1 -1
  43. package/app/components/order-card.js +1 -1
  44. package/app/components/schedule-manager.js +1 -1
  45. package/app/components/settings-container.js +1 -1
  46. package/app/components/store-selector.js +1 -1
  47. package/app/components/widget/customers.js +1 -1
  48. package/app/components/widget/orders.js +1 -1
  49. package/app/components/widget/storefront-metrics.js +1 -1
  50. package/app/controllers/application.js +1 -1
  51. package/app/controllers/customers/index.js +1 -1
  52. package/app/controllers/home.js +1 -1
  53. package/app/controllers/networks/index/network/customers.js +1 -1
  54. package/app/controllers/networks/index/network/index.js +1 -1
  55. package/app/controllers/networks/index/network/orders.js +1 -1
  56. package/app/controllers/networks/index/network/stores.js +1 -1
  57. package/app/controllers/networks/index/network.js +1 -1
  58. package/app/controllers/networks/index.js +1 -1
  59. package/app/controllers/orders/index.js +1 -1
  60. package/app/controllers/products/index/category/edit.js +1 -1
  61. package/app/controllers/products/index/category/new.js +1 -1
  62. package/app/controllers/products/index/category.js +1 -1
  63. package/app/controllers/products/index/index/edit.js +1 -1
  64. package/app/controllers/products/index/index.js +1 -1
  65. package/app/controllers/products/index.js +1 -1
  66. package/app/controllers/settings/api.js +1 -1
  67. package/app/controllers/settings/gateways.js +1 -1
  68. package/app/controllers/settings/index.js +1 -1
  69. package/app/controllers/settings/locations.js +1 -1
  70. package/app/controllers/settings/notifications.js +1 -1
  71. package/app/helpers/get-tip-amount.js +1 -1
  72. package/app/models/addon-category.js +1 -1
  73. package/app/models/gateway.js +1 -1
  74. package/app/models/network.js +1 -1
  75. package/app/models/notification-channel.js +1 -1
  76. package/app/models/product-addon-category.js +1 -1
  77. package/app/models/product-addon.js +1 -1
  78. package/app/models/product-hour.js +1 -1
  79. package/app/models/product-store-location.js +1 -1
  80. package/app/models/product-variant-option.js +1 -1
  81. package/app/models/product-variant.js +1 -1
  82. package/app/models/product.js +1 -1
  83. package/app/models/store-hour.js +1 -1
  84. package/app/models/store-location.js +1 -1
  85. package/app/models/store.js +1 -1
  86. package/app/routes/application.js +1 -1
  87. package/app/routes/customers/index/edit.js +1 -1
  88. package/app/routes/customers/index.js +1 -1
  89. package/app/routes/home.js +1 -1
  90. package/app/routes/networks/index/network/customers.js +1 -1
  91. package/app/routes/networks/index/network/index.js +1 -1
  92. package/app/routes/networks/index/network/orders.js +1 -1
  93. package/app/routes/networks/index/network/stores.js +1 -1
  94. package/app/routes/networks/index/network.js +1 -1
  95. package/app/routes/networks/index.js +1 -1
  96. package/app/routes/orders/index/edit.js +1 -1
  97. package/app/routes/orders/index/new.js +1 -1
  98. package/app/routes/orders/index/view.js +1 -1
  99. package/app/routes/orders/index.js +1 -1
  100. package/app/routes/products/index/category/edit.js +1 -1
  101. package/app/routes/products/index/category/new.js +1 -1
  102. package/app/routes/products/index/category.js +1 -1
  103. package/app/routes/products/index/index/edit.js +1 -1
  104. package/app/routes/products/index/index.js +1 -1
  105. package/app/routes/products/index.js +1 -1
  106. package/app/routes/settings/api.js +1 -1
  107. package/app/routes/settings/gateways.js +1 -1
  108. package/app/routes/settings/index.js +1 -1
  109. package/app/routes/settings/locations.js +1 -1
  110. package/app/routes/settings/notifications.js +1 -1
  111. package/app/serializers/addon-category.js +1 -1
  112. package/app/serializers/network.js +1 -1
  113. package/app/serializers/notification-channel.js +1 -1
  114. package/app/serializers/product-addon-category.js +1 -1
  115. package/app/serializers/product-variant.js +1 -1
  116. package/app/serializers/product.js +1 -1
  117. package/app/serializers/store-location.js +1 -1
  118. package/app/serializers/store.js +1 -1
  119. package/app/services/storefront.js +1 -1
  120. package/app/templates/customers/index/edit.js +1 -1
  121. package/app/templates/customers/index.js +1 -1
  122. package/app/templates/home.js +1 -1
  123. package/app/templates/networks/index/network/customers.js +1 -1
  124. package/app/templates/networks/index/network/index.js +1 -1
  125. package/app/templates/networks/index/network/orders.js +1 -1
  126. package/app/templates/networks/index/network/stores.js +1 -1
  127. package/app/templates/networks/index/network.js +1 -1
  128. package/app/templates/networks/index.js +1 -1
  129. package/app/templates/orders/index/edit.js +1 -1
  130. package/app/templates/orders/index/new.js +1 -1
  131. package/app/templates/orders/index/view.js +1 -1
  132. package/app/templates/orders/index.js +1 -1
  133. package/app/templates/products/index/category/edit.js +1 -1
  134. package/app/templates/products/index/category/new.js +1 -1
  135. package/app/templates/products/index/category.js +1 -1
  136. package/app/templates/products/index/index/edit.js +1 -1
  137. package/app/templates/products/index/index.js +1 -1
  138. package/app/templates/products/index.js +1 -1
  139. package/app/templates/settings/api.js +1 -1
  140. package/app/templates/settings/gateways.js +1 -1
  141. package/app/templates/settings/index.js +1 -1
  142. package/app/templates/settings/locations.js +1 -1
  143. package/app/templates/settings/notifications.js +1 -1
  144. package/app/templates/settings.js +1 -1
  145. package/app/utils/create-shareable-link.js +1 -1
  146. package/app/utils/get-gateway-schemas.js +1 -1
  147. package/app/utils/get-notification-schemas.js +1 -1
  148. package/composer.json +88 -0
  149. package/extension.json +10 -0
  150. package/package.json +6 -6
  151. package/phpstan.neon.dist +8 -0
  152. package/phpunit.xml.dist +16 -0
  153. package/server/.gitattributes +14 -0
  154. package/server/README.md +40 -0
  155. package/server/config/api.php +101 -0
  156. package/server/config/database.connections.php +57 -0
  157. package/server/config/storefront.php +19 -0
  158. package/server/config/twilio-notification-channel.php +36 -0
  159. package/server/migrations/2023_05_03_025307_create_carts_table.php +44 -0
  160. package/server/migrations/2023_05_03_025307_create_checkouts_table.php +51 -0
  161. package/server/migrations/2023_05_03_025307_create_gateways_table.php +48 -0
  162. package/server/migrations/2023_05_03_025307_create_network_stores_table.php +36 -0
  163. package/server/migrations/2023_05_03_025307_create_networks_table.php +56 -0
  164. package/server/migrations/2023_05_03_025307_create_notification_channels_table.php +43 -0
  165. package/server/migrations/2023_05_03_025307_create_payment_methods_table.php +44 -0
  166. package/server/migrations/2023_05_03_025307_create_product_addon_categories_table.php +38 -0
  167. package/server/migrations/2023_05_03_025307_create_product_addons_table.php +43 -0
  168. package/server/migrations/2023_05_03_025307_create_product_hours_table.php +37 -0
  169. package/server/migrations/2023_05_03_025307_create_product_store_locations_table.php +34 -0
  170. package/server/migrations/2023_05_03_025307_create_product_variant_options_table.php +40 -0
  171. package/server/migrations/2023_05_03_025307_create_product_variants_table.php +44 -0
  172. package/server/migrations/2023_05_03_025307_create_products_table.php +59 -0
  173. package/server/migrations/2023_05_03_025307_create_reviews_table.php +41 -0
  174. package/server/migrations/2023_05_03_025307_create_store_hours_table.php +37 -0
  175. package/server/migrations/2023_05_03_025307_create_store_locations_table.php +38 -0
  176. package/server/migrations/2023_05_03_025307_create_stores_table.php +57 -0
  177. package/server/migrations/2023_05_03_025307_create_votes_table.php +39 -0
  178. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_carts_table.php +40 -0
  179. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_checkouts_table.php +48 -0
  180. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_gateways_table.php +40 -0
  181. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_network_stores_table.php +40 -0
  182. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_networks_table.php +42 -0
  183. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_notification_channels_table.php +40 -0
  184. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_payment_methods_table.php +40 -0
  185. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addon_categories_table.php +38 -0
  186. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_addons_table.php +38 -0
  187. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_hours_table.php +32 -0
  188. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_store_locations_table.php +34 -0
  189. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variant_options_table.php +32 -0
  190. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_product_variants_table.php +32 -0
  191. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_products_table.php +44 -0
  192. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_reviews_table.php +38 -0
  193. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_hours_table.php +32 -0
  194. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_store_locations_table.php +40 -0
  195. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_stores_table.php +42 -0
  196. package/server/migrations/2023_05_03_025310_add_foreign_keys_to_votes_table.php +38 -0
  197. package/server/src/Auth/Schemas/Storefront.php +95 -0
  198. package/server/src/Console/Commands/NotifyStorefrontOrderNearby.php +86 -0
  199. package/server/src/Expansions/EntityExpansion.php +44 -0
  200. package/server/src/Http/Controllers/ActionController.php +119 -0
  201. package/server/src/Http/Controllers/AddonCategoryController.php +13 -0
  202. package/server/src/Http/Controllers/CustomerController.php +13 -0
  203. package/server/src/Http/Controllers/GatewayController.php +13 -0
  204. package/server/src/Http/Controllers/MetricsController.php +71 -0
  205. package/server/src/Http/Controllers/NetworkController.php +170 -0
  206. package/server/src/Http/Controllers/NotificationChannelController.php +13 -0
  207. package/server/src/Http/Controllers/OrderController.php +154 -0
  208. package/server/src/Http/Controllers/ProductAddonCategoryController.php +14 -0
  209. package/server/src/Http/Controllers/ProductAddonController.php +14 -0
  210. package/server/src/Http/Controllers/ProductController.php +123 -0
  211. package/server/src/Http/Controllers/ProductHourController.php +13 -0
  212. package/server/src/Http/Controllers/ProductVariantController.php +13 -0
  213. package/server/src/Http/Controllers/ProductVariantOptionController.php +13 -0
  214. package/server/src/Http/Controllers/ReviewController.php +13 -0
  215. package/server/src/Http/Controllers/StoreController.php +26 -0
  216. package/server/src/Http/Controllers/StoreHourController.php +13 -0
  217. package/server/src/Http/Controllers/StoreLocationController.php +13 -0
  218. package/server/src/Http/Controllers/StorefrontController.php +15 -0
  219. package/server/src/Http/Controllers/VoteController.php +13 -0
  220. package/server/src/Http/Controllers/v1/CartController.php +161 -0
  221. package/server/src/Http/Controllers/v1/CategoryController.php +138 -0
  222. package/server/src/Http/Controllers/v1/CheckoutController.php +957 -0
  223. package/server/src/Http/Controllers/v1/CustomerController.php +482 -0
  224. package/server/src/Http/Controllers/v1/GatewayControllerController.php +11 -0
  225. package/server/src/Http/Controllers/v1/NetworkController.php +281 -0
  226. package/server/src/Http/Controllers/v1/PaymentMethodController.php +11 -0
  227. package/server/src/Http/Controllers/v1/ProductController.php +94 -0
  228. package/server/src/Http/Controllers/v1/ReviewController.php +270 -0
  229. package/server/src/Http/Controllers/v1/ServiceQuoteController.php +402 -0
  230. package/server/src/Http/Controllers/v1/StoreController.php +176 -0
  231. package/server/src/Http/Filter/AddonCategoryFilter.php +19 -0
  232. package/server/src/Http/Filter/CustomerFilter.php +18 -0
  233. package/server/src/Http/Filter/GatewayFilter.php +13 -0
  234. package/server/src/Http/Filter/NetworkFilter.php +18 -0
  235. package/server/src/Http/Filter/NotificationChannelFilter.php +13 -0
  236. package/server/src/Http/Filter/OrderFilter.php +46 -0
  237. package/server/src/Http/Filter/ProductFilter.php +28 -0
  238. package/server/src/Http/Filter/StoreFilter.php +42 -0
  239. package/server/src/Http/Filter/StoreLocationFilter.php +23 -0
  240. package/server/src/Http/Middleware/SetStorefrontSession.php +130 -0
  241. package/server/src/Http/Requests/AddStoreToNetworkCategory.php +45 -0
  242. package/server/src/Http/Requests/CaptureOrderRequest.php +30 -0
  243. package/server/src/Http/Requests/CreateCustomerRequest.php +44 -0
  244. package/server/src/Http/Requests/CreateReviewRequest.php +34 -0
  245. package/server/src/Http/Requests/GetServiceQuoteFromCart.php +40 -0
  246. package/server/src/Http/Requests/InitializeCheckoutRequest.php +38 -0
  247. package/server/src/Http/Requests/NetworkActionRequest.php +43 -0
  248. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +31 -0
  249. package/server/src/Http/Resources/Cart.php +31 -0
  250. package/server/src/Http/Resources/Category.php +48 -0
  251. package/server/src/Http/Resources/Customer.php +36 -0
  252. package/server/src/Http/Resources/Gateway.php +32 -0
  253. package/server/src/Http/Resources/Media.php +29 -0
  254. package/server/src/Http/Resources/Network.php +48 -0
  255. package/server/src/Http/Resources/Product.php +209 -0
  256. package/server/src/Http/Resources/Review.php +45 -0
  257. package/server/src/Http/Resources/ReviewCustomer.php +60 -0
  258. package/server/src/Http/Resources/Store.php +76 -0
  259. package/server/src/Http/Resources/StoreHour.php +29 -0
  260. package/server/src/Http/Resources/StoreLocation.php +33 -0
  261. package/server/src/Imports/ProductsImport.php +20 -0
  262. package/server/src/Jobs/DownloadProductImageUrl.php +60 -0
  263. package/server/src/Listeners/HandleOrderCompleted.php +31 -0
  264. package/server/src/Listeners/HandleOrderDispatched.php +34 -0
  265. package/server/src/Listeners/HandleOrderDriverAssigned.php +37 -0
  266. package/server/src/Listeners/HandleOrderStarted.php +27 -0
  267. package/server/src/Mail/StorefrontNetworkInvite.php +48 -0
  268. package/server/src/Models/AddonCategory.php +30 -0
  269. package/server/src/Models/Cart.php +691 -0
  270. package/server/src/Models/Checkout.php +166 -0
  271. package/server/src/Models/Customer.php +88 -0
  272. package/server/src/Models/Gateway.php +165 -0
  273. package/server/src/Models/Network.php +300 -0
  274. package/server/src/Models/NetworkStore.php +86 -0
  275. package/server/src/Models/NotificationChannel.php +147 -0
  276. package/server/src/Models/PaymentMethod.php +99 -0
  277. package/server/src/Models/Product.php +315 -0
  278. package/server/src/Models/ProductAddon.php +128 -0
  279. package/server/src/Models/ProductAddonCategory.php +90 -0
  280. package/server/src/Models/ProductHour.php +59 -0
  281. package/server/src/Models/ProductStoreLocation.php +77 -0
  282. package/server/src/Models/ProductVariant.php +125 -0
  283. package/server/src/Models/ProductVariantOption.php +86 -0
  284. package/server/src/Models/Review.php +127 -0
  285. package/server/src/Models/Store.php +478 -0
  286. package/server/src/Models/StoreHour.php +59 -0
  287. package/server/src/Models/StoreLocation.php +126 -0
  288. package/server/src/Models/StorefrontModel.php +22 -0
  289. package/server/src/Models/Vote.php +84 -0
  290. package/server/src/Notifications/StorefrontOrderCanceled.php +196 -0
  291. package/server/src/Notifications/StorefrontOrderCompleted.php +201 -0
  292. package/server/src/Notifications/StorefrontOrderCreated.php +157 -0
  293. package/server/src/Notifications/StorefrontOrderDriverAssigned.php +200 -0
  294. package/server/src/Notifications/StorefrontOrderEnroute.php +199 -0
  295. package/server/src/Notifications/StorefrontOrderNearby.php +201 -0
  296. package/server/src/Notifications/StorefrontOrderPreparing.php +202 -0
  297. package/server/src/Notifications/StorefrontOrderReadyForPickup.php +202 -0
  298. package/server/src/Observers/NetworkObserver.php +40 -0
  299. package/server/src/Observers/ProductObserver.php +118 -0
  300. package/server/src/Providers/EventServiceProvider.php +23 -0
  301. package/server/src/Providers/StorefrontServiceProvider.php +103 -0
  302. package/server/src/Support/Metrics.php +193 -0
  303. package/server/src/Support/OrderConfig.php +13 -0
  304. package/server/src/Support/QPay.php +208 -0
  305. package/server/src/Support/Storefront.php +201 -0
  306. package/server/src/routes.php +180 -0
  307. package/server/tests/Feature.php +5 -0
@@ -0,0 +1,193 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Support;
4
+
5
+ use Fleetbase\Models\Company;
6
+ use Fleetbase\Storefront\Models\Network;
7
+ use Fleetbase\Storefront\Models\Store;
8
+ use Fleetbase\Storefront\Models\Product;
9
+ use Fleetbase\FleetOps\Models\Order;
10
+ use Illuminate\Support\Carbon;
11
+ use Illuminate\Support\Str;
12
+
13
+ /**
14
+ * Metrics service to pull analyitcs and metrics from Storefront.
15
+ *
16
+ * Ex:
17
+ * $metrics = Metrics::new($company)->withTotalStores()->withOrdersCanceled()->get();
18
+ */
19
+ class Metrics
20
+ {
21
+ protected \DateTime $start;
22
+ protected \DateTime $end;
23
+ protected Company $company;
24
+ protected array $metrics = [];
25
+
26
+ public static function new(Company $company, ?\DateTime $start = null, ?\DateTime $end = null): Metrics
27
+ {
28
+ $start = $start === null ? Carbon::create(1900)->toDateTime() : $start;
29
+ $end = $end === null ? Carbon::tomorrow()->toDateTime() : $end;
30
+
31
+ return (new static())->setCompany($company)->between($start, $end);
32
+ }
33
+
34
+ public static function forCompany(Company $company, ?\DateTime $start = null, ?\DateTime $end = null): Metrics
35
+ {
36
+ return static::new($company, $start, $end);
37
+ }
38
+
39
+ public function start(\DateTime $start): Metrics
40
+ {
41
+ $this->start = $start;
42
+
43
+ return $this;
44
+ }
45
+
46
+ public function end(\DateTime $end): Metrics
47
+ {
48
+ $this->end = $end;
49
+
50
+ return $this;
51
+ }
52
+
53
+ public function between(\DateTime $start, \DateTime $end): Metrics
54
+ {
55
+ return $this->start($start)->end($end);
56
+ }
57
+
58
+ private function setCompany(Company $company): Metrics
59
+ {
60
+ $this->company = $company;
61
+
62
+ return $this;
63
+ }
64
+
65
+ private function set($key, $value = null): Metrics
66
+ {
67
+ if (is_array($key)) {
68
+ foreach ($key as $k => $v) {
69
+ $this->set($k, $v);
70
+ }
71
+
72
+ return $this;
73
+ }
74
+
75
+ $this->metrics = data_set($this->metrics, $key, $value);
76
+
77
+ return $this;
78
+ }
79
+
80
+ public function get()
81
+ {
82
+ return $this->metrics;
83
+ }
84
+
85
+ public function with(?array $metrics = [])
86
+ {
87
+ if (empty($metrics)) {
88
+ $metrics = array_slice(get_class_methods($this), 9);
89
+ }
90
+
91
+ $metrics = array_map(
92
+ function ($metric) {
93
+ return Str::camel($metric);
94
+ },
95
+ $metrics
96
+ );
97
+
98
+ foreach ($metrics as $metric) {
99
+ if (method_exists($this, $metric)) {
100
+ $this->{$metric}();
101
+ }
102
+ }
103
+
104
+ return $this;
105
+ }
106
+
107
+ public function totalProducts(?callable $callback = null): Metrics
108
+ {
109
+ $query = Product::where('company_uuid', $this->company->uuid);
110
+
111
+ if (is_callable($callback)) {
112
+ $callback($query);
113
+ }
114
+
115
+ $data = $query->count();
116
+
117
+ return $this->set('total_products', $data);
118
+ }
119
+
120
+ public function totalStores(?callable $callback = null): Metrics
121
+ {
122
+ $query = Store::where('company_uuid', $this->company->uuid);
123
+
124
+ if (is_callable($callback)) {
125
+ $callback($query);
126
+ }
127
+
128
+ $data = $query->count();
129
+
130
+ return $this->set('total_stores', $data);
131
+ }
132
+
133
+ public function totalNetworks(?callable $callback = null): Metrics
134
+ {
135
+ $query = Network::where('company_uuid', $this->company->uuid);
136
+
137
+ if (is_callable($callback)) {
138
+ $callback($query);
139
+ }
140
+
141
+ $data = $query->count();
142
+
143
+ return $this->set('total_networks', $data);
144
+ }
145
+
146
+ public function ordersInProgress(?callable $callback = null): Metrics
147
+ {
148
+ $query = Order::where('company_uuid', $this->company->uuid)
149
+ ->whereBetween('created_at', [$this->start, $this->end])
150
+ ->where('type', 'storefront')
151
+ ->whereNotIn('status', ['completed', 'created', 'pending', 'canceled']);
152
+
153
+ if (is_callable($callback)) {
154
+ $callback($query);
155
+ }
156
+
157
+ $data = $query->count();
158
+
159
+ return $this->set('orders_in_progress', $data);
160
+ }
161
+
162
+ public function ordersCompleted(?callable $callback = null): Metrics
163
+ {
164
+ $query = Order::where('company_uuid', $this->company->uuid)
165
+ ->whereBetween('created_at', [$this->start, $this->end])
166
+ ->where('type', 'storefront')
167
+ ->where('status', 'completed');
168
+
169
+ if (is_callable($callback)) {
170
+ $callback($query);
171
+ }
172
+
173
+ $data = $query->count();
174
+
175
+ return $this->set('orders_completed', $data);
176
+ }
177
+
178
+ public function ordersCanceled(?callable $callback = null): Metrics
179
+ {
180
+ $query = Order::where('company_uuid', $this->company->uuid)
181
+ ->whereBetween('created_at', [$this->start, $this->end])
182
+ ->where('type', 'storefront')
183
+ ->where('status', 'canceled');
184
+
185
+ if (is_callable($callback)) {
186
+ $callback($query);
187
+ }
188
+
189
+ $data = $query->count();
190
+
191
+ return $this->set('orders_canceled', $data);
192
+ }
193
+ }
@@ -0,0 +1,13 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Support;
4
+
5
+ class OrderConfig {
6
+ /**
7
+ * Provides default order configs for Storefront.
8
+ */
9
+ public function get(): ?array
10
+ {
11
+ return config('storefront.api.types.order', []);
12
+ }
13
+ }
@@ -0,0 +1,208 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Support;
4
+
5
+ use GuzzleHttp\Client;
6
+
7
+ class QPay
8
+ {
9
+ private string $host = 'https://merchant.qpay.mn/';
10
+ private string $namespace = 'v2';
11
+ private ?string $callbackUrl;
12
+ private array $requestOptions = [];
13
+ private Client $client;
14
+
15
+ public function __construct(?string $username = null, ?string $password = null, ?string $callbackUrl = null)
16
+ {
17
+ $this->callbackUrl = $callbackUrl;
18
+ $this->requestOptions = [
19
+ 'base_uri' => $this->buildRequestUrl(),
20
+ 'auth' => [$username, $password],
21
+ 'headers' => [
22
+ 'Content-Type' => 'application/json',
23
+ 'Accept' => 'application/json'
24
+ ]
25
+ ];
26
+ $this->client = new Client($this->requestOptions);
27
+ }
28
+
29
+ public function updateRequestOption($key, $value)
30
+ {
31
+ $this->requestOptions[$key] = $value;
32
+ $this->client = new Client($this->requestOptions);
33
+ }
34
+
35
+ public function getClient()
36
+ {
37
+ return $this->client;
38
+ }
39
+
40
+ public function setNamespace(string $namespace): ?QPay
41
+ {
42
+ $this->namespace = $namespace;
43
+ $this->updateRequestOption('base_uri', $this->buildRequestUrl());
44
+
45
+ return $this;
46
+ }
47
+
48
+ private function buildRequestUrl(string $path = ''): string
49
+ {
50
+ $url = trim($this->host . $this->namespace . '/' . $path);
51
+ return $url;
52
+ }
53
+
54
+ public function useSandbox()
55
+ {
56
+ $this->host = 'https://merchant-sandbox.qpay.mn/';
57
+ $this->updateRequestOption('base_uri', $this->buildRequestUrl());
58
+
59
+ return $this;
60
+ }
61
+
62
+ public static function instance(?string $username = null, ?string $password = null, ?string $callbackUrl = null): QPay
63
+ {
64
+ return new static($username, $password, $callbackUrl);
65
+ }
66
+
67
+ private function hasCallbackUrl()
68
+ {
69
+ return isset($this->callbackUrl);
70
+ }
71
+
72
+ private function request(string $method, string $path, array $options = [])
73
+ {
74
+ $options['http_errors'] = false;
75
+
76
+ $response = $this->client->request($method, $path, $options);
77
+ $body = $response->getBody();
78
+ $contents = $body->getContents();
79
+ $json = json_decode($contents);
80
+
81
+ return $json;
82
+ }
83
+
84
+ public function post(string $path, array $params = [], array $options = [])
85
+ {
86
+ $options = ['json' => $params];
87
+
88
+ return $this->request('POST', $path, $options);
89
+ }
90
+
91
+ public function delete(string $path, array $params = [], array $options = [])
92
+ {
93
+ $options = ['json' => $params];
94
+
95
+ return $this->request('DELETE', $path, $options);
96
+ }
97
+
98
+ public function get(string $path, array $options = [])
99
+ {
100
+ return $this->request('GET', $path, $options);
101
+ }
102
+
103
+ public function getAuthToken()
104
+ {
105
+ return $this->post('auth/token');
106
+ }
107
+
108
+ public function refreshAuthToken()
109
+ {
110
+ return $this->post('auth/refresh');
111
+ }
112
+
113
+ private function useBearerToken(string $token): QPay
114
+ {
115
+ unset($this->requestOptions['auth']);
116
+ $headers = array_merge($this->requestOptions['headers'] ?? [], ['Authorization' => "Bearer $token"]);
117
+ $this->updateRequestOption('headers', $headers);
118
+
119
+ return $this;
120
+ }
121
+
122
+ public function setAuthToken(?string $accessToken = null): QPay
123
+ {
124
+ if ($accessToken) {
125
+ $this->useBearerToken($accessToken);
126
+ } else {
127
+ $response = $this->getAuthToken();
128
+ $token = $response->access_token;
129
+
130
+ if (isset($token)) {
131
+ $this->useBearerToken($token);
132
+ }
133
+ }
134
+
135
+ return $this;
136
+ }
137
+
138
+ public function createSimpleInvoice(int $amount, ?string $invoiceCode = '', ?string $invoiceDescription = '', ?string $invoiceReceiverCode = '', ?string $senderInvoiceNo = '', ?string $callbackUrl = null)
139
+ {
140
+ if (!$callbackUrl && $this->hasCallbackUrl()) {
141
+ $callbackUrl = $this->callbackUrl;
142
+ }
143
+
144
+ $params = array_filter([
145
+ 'invoice_code' => $invoiceCode,
146
+ 'amount' => $amount,
147
+ 'callback_url' => $callbackUrl,
148
+ 'invoice_description' => $invoiceDescription,
149
+ 'invoice_receiver_code' => $invoiceReceiverCode,
150
+ 'sender_invoice_no' => $senderInvoiceNo,
151
+ ]);
152
+
153
+ return $this->createQPayInvoice($params);
154
+ }
155
+
156
+ public function createQPayInvoice(array $params = [], $options = [])
157
+ {
158
+ if (!isset($params['callback_url']) && $this->hasCallbackUrl()) {
159
+ $params['callback_url'] = $this->callbackUrl;
160
+ }
161
+
162
+ return $this->post('invoice', $params, $options);
163
+ }
164
+
165
+ public function paymentCheck(string $invoiceId, $options = [])
166
+ {
167
+ $params = [
168
+ 'object_type' => 'INVOICE',
169
+ 'object_id' => $invoiceId
170
+ ];
171
+
172
+ return $this->post('payment/check', $params, $options);
173
+ }
174
+
175
+ public function paymentCancel(string $invoiceId, $options = [])
176
+ {
177
+ $params = [
178
+ 'callback_url' => '"https://qpay.mn/payment/result?payment_id=' . $invoiceId,
179
+ ];
180
+
181
+ return $this->delete('payment/cancel', $params, $options);
182
+ }
183
+
184
+ public function paymentRefund(string $invoiceId, $options = [])
185
+ {
186
+ $params = [
187
+ 'callback_url' => '"https://qpay.mn/payment/result?payment_id=' . $invoiceId,
188
+ ];
189
+
190
+ return $this->delete('payment/refund', $params, $options);
191
+ }
192
+
193
+ public static function createInvoice(string $username, string $password, array $invoiceParams = [])
194
+ {
195
+ return static::instance($username, $password)->setAuthToken()->createQPayInvoice($invoiceParams);
196
+ }
197
+
198
+ public static function generateCode(string $id)
199
+ {
200
+ return date('Ymd') . $id;
201
+ }
202
+
203
+ public static function cleanCode(string $code)
204
+ {
205
+ $code = str_replace(' ', '-', $code);
206
+ return preg_replace('/[^A-Za-z0-9\-]/', '', $code);
207
+ }
208
+ }
@@ -0,0 +1,201 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\Storefront\Support;
4
+
5
+ use Fleetbase\Storefront\Models\Gateway;
6
+ use Fleetbase\Storefront\Models\Network;
7
+ use Fleetbase\Storefront\Models\Product;
8
+ use Fleetbase\Storefront\Models\Store;
9
+ use Fleetbase\FleetOps\Models\Contact;
10
+ use Fleetbase\FleetOps\Models\Order;
11
+ use Fleetbase\Models\User;
12
+ use Fleetbase\Storefront\Notifications\StorefrontOrderCreated;
13
+ use Illuminate\Support\Facades\Notification;
14
+ use Illuminate\Support\Facades\Redis;
15
+ use Illuminate\Support\Str;
16
+ use Laravel\Sanctum\PersonalAccessToken;
17
+
18
+ class Storefront
19
+ {
20
+ /**
21
+ * Returns current store or network based on session `storefront_key`
22
+ * with bare minimum columns, but can optionally pass in more columns to receive.
23
+ *
24
+ * @param array $columns
25
+ * @return \Fleetbase\Models\Storefront\Network|\Fleetbase\Models\Storefront\Store
26
+ */
27
+ public static function about($columns = [], $with = [])
28
+ {
29
+ $key = session('storefront_key');
30
+
31
+ if (!$key) {
32
+ return null;
33
+ }
34
+
35
+ if (is_array($columns)) {
36
+ $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);
37
+ }
38
+
39
+ if (Str::startsWith($key, 'store')) {
40
+ $about = Store::select($columns)->where('key', $key)->with($with)->first();
41
+ } else {
42
+ $about = Network::select($columns)->where('key', $key)->with($with)->first();
43
+ }
44
+
45
+ $about->is_store = Str::startsWith($key, 'store');
46
+ $about->is_network = Str::startsWith($key, 'network');
47
+
48
+ return $about;
49
+ }
50
+
51
+ public static function findAbout($id, $columns = [], $with = [])
52
+ {
53
+ if (is_array($columns)) {
54
+ $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);
55
+ }
56
+
57
+ if (Str::startsWith($id, 'store')) {
58
+ $about = Store::select($columns)->where('public_id', $id)->with($with)->first();
59
+ } else {
60
+ $about = Network::select($columns)->where('public_id', $id)->with($with)->first();
61
+ }
62
+
63
+ if (!$about) {
64
+ return $about;
65
+ }
66
+
67
+ $about->is_store = Str::startsWith($id, 'store');
68
+ $about->is_network = Str::startsWith($id, 'network');
69
+
70
+ return $about;
71
+ }
72
+
73
+ public static function getStoreFromLocation(string $id, $columns = [], $with = [])
74
+ {
75
+ if (is_array($columns)) {
76
+ $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);
77
+ }
78
+
79
+ return Store::select($columns)->with($with)->whereHas('locations', function ($q) use ($id) {
80
+ $q->where('place_uuid', $id);
81
+ $q->orWhereHas('place', function ($q) use ($id) {
82
+ $q->where('public_id', $id);
83
+ });
84
+ })->first();
85
+ }
86
+
87
+ public static function getCustomerFromToken()
88
+ {
89
+ $token = request()->header('Customer-Token');
90
+
91
+ if ($token) {
92
+ $accessToken = PersonalAccessToken::findToken($token);
93
+
94
+ if ($accessToken && Utils::isUuid($accessToken->name)) {
95
+ $customer = Contact::where('uuid', $accessToken->name)->first();
96
+
97
+ if ($customer) {
98
+ return $customer;
99
+ }
100
+ }
101
+
102
+ if ($accessToken) {
103
+ return Contact::where('user_uuid', $accessToken->tokenable->uuid)->first();
104
+ }
105
+ }
106
+
107
+ return null;
108
+ }
109
+
110
+ public static function findGateway(string $code): ?Gateway
111
+ {
112
+ if ($code === 'cash') {
113
+ return Gateway::cash();
114
+ }
115
+
116
+ return Gateway::where([
117
+ 'code' => $code,
118
+ 'owner_uuid' => session('storefront_store') ?? session('storefront_network'),
119
+ ])->first();
120
+ }
121
+
122
+ public static function getFullDescriptionFromCartItem($cartItem)
123
+ {
124
+ $fullDescription = $cartItem->name;
125
+
126
+ if (is_array($cartItem->variants) && count($cartItem->variants)) {
127
+ $fullDescription .= ' with Variation: ';
128
+ $fullDescription .= collect($cartItem->variants)->pluck('name')->join(',');
129
+ }
130
+
131
+ if (is_array($cartItem->addons) && count($cartItem->addons)) {
132
+ $fullDescription .= ' with Addons: ';
133
+ $fullDescription .= collect($cartItem->addons)->pluck('name')->join(',');
134
+ }
135
+
136
+ return $fullDescription;
137
+ }
138
+
139
+ public static function destroyCart($cartId)
140
+ {
141
+ $prefix = 'cart:' . session('storefront_store') . ':';
142
+
143
+ return Redis::del($prefix . $cartId);
144
+ }
145
+
146
+ public static function getProduct($publicId)
147
+ {
148
+ return Product::select(['uuid', 'public_id', 'name', 'description', 'price', 'sale_price', 'is_on_sale'])->where(['public_id' => $publicId])->with([])->first();
149
+ }
150
+
151
+ public static function alertNewOrder(Order $order, $sendNow = false)
152
+ {
153
+ $about = static::about(['alertable']);
154
+ $alertables = [];
155
+
156
+ if ($about->is_network) {
157
+ $store = static::findAbout($order->getMeta('storefront_id'), ['alertable']);
158
+
159
+ if ($store && $store->public_id !== $about->public_id) {
160
+ $merge = Utils::get($store, 'alertable.for_new_order', []);
161
+ $alertables = array_merge($alertables, $merge);
162
+ }
163
+ }
164
+
165
+ $merge = Utils::get($about, 'alertable.for_new_order', []);
166
+ $alertables = array_merge($alertables, $merge);
167
+ $users = collect($alertables)->map(function ($id) {
168
+ return User::where('public_id', $id)->first();
169
+ });
170
+
171
+ if ($users->isEmpty()) {
172
+ return;
173
+ }
174
+
175
+ if ($sendNow) {
176
+ return Notification::sendNow($users, new StorefrontOrderCreated($order));
177
+ }
178
+
179
+ return Notification::send($users, new StorefrontOrderCreated($order));
180
+ }
181
+
182
+ public static function createStripeCustomerForContact(Contact &$customer)
183
+ {
184
+ $stripeCustomer = \Stripe\Customer::create([
185
+ 'description' => 'Customer created in Fleetbase Storefront',
186
+ 'email' => $customer->email,
187
+ 'name' => $customer->name,
188
+ 'phone' => $customer->phone,
189
+ 'metadata' => [
190
+ 'contact_id' => $customer->public_id,
191
+ 'storefront_id' => session('storefront_store') ?? session('storefront_network'),
192
+ 'company_id' => session('company')
193
+ ]
194
+ ]);
195
+
196
+ // set the stripe customer to customer meta
197
+ $customer->updateMeta('stripe_id', $stripeCustomer->id);
198
+
199
+ return $stripeCustomer;
200
+ }
201
+ }